From f68768e284f3a6663fefd37978fbe0dc57b69cab Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Fri, 27 Feb 2026 03:57:25 +0200 Subject: [PATCH 01/58] Check --- orchestrator/src/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 62d5d0662..9d8e24d4d 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -73,3 +73,6 @@ def checkout(): # This is useful for development. # The default port is 5000. app.run(host='0.0.0.0') + + +# Hello \ No newline at end of file From 6c41ef952ccab6ba727ec5eddeeb1df0012b8eb7 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 09:20:42 +0200 Subject: [PATCH 02/58] https://chatgpt.com/s/t_69a2965bcc808191906e28d9aee7299c --- orchestrator/src/app.py | 26 ++++++++++++++++++++------ test_checkout.json | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 test_checkout.json diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 62d5d0662..421232c11 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -50,12 +50,26 @@ def checkout(): """ Responds with a JSON object containing the order ID, status, and suggested books. """ - # Get request object data to json - request_data = json.loads(request.data) - # Print request object data - print("Request Data:", request_data.get('items')) + # Read the JSON body from the POST request + request_data = request.get_json(silent=True) - # Dummy response following the provided YAML specification for the bookstore + # If the request body is missing or invalid JSON, return a 400 error + if request_data is None: + return { + "error": { + "code": "BAD_REQUEST", + "message": "Request body must be valid JSON." + } + }, 400 + + # Print the full request and a few important parts + print("FULL REQUEST DATA:", request_data) + print("USER:", request_data.get("user")) + print("ITEMS:", request_data.get("items")) + print("SHIPPING METHOD:", request_data.get("shippingMethod")) + print("TERMS ACCEPTED:", request_data.get("termsAndConditionsAccepted")) + + # Still a dummy response for now order_status_response = { 'orderId': '12345', 'status': 'Order Approved', @@ -65,7 +79,7 @@ def checkout(): ] } - return order_status_response + return order_status_response, 200 if __name__ == '__main__': diff --git a/test_checkout.json b/test_checkout.json new file mode 100644 index 000000000..a78ba6f7a --- /dev/null +++ b/test_checkout.json @@ -0,0 +1,20 @@ +{ + "user": { + "name": "Test User", + "contact": "test@example.com", + "creditCard": { + "number": "4111111111111111", + "expirationDate": "12/30", + "cvv": "123" + }, + "userComment": "Please handle with care." + }, + "items": [ + { + "name": "Book A", + "quantity": 1 + } + ], + "shippingMethod": "Standard", + "termsAndConditionsAccepted": true +} From 419b9f203112b7cf1ed169a89024d282a12547af Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 09:37:21 +0200 Subject: [PATCH 03/58] Improve checkout parsing, logging, and basic validation --- orchestrator/src/app.py | 88 ++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 421232c11..8d23bc7d5 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -50,10 +50,8 @@ def checkout(): """ Responds with a JSON object containing the order ID, status, and suggested books. """ - # Read the JSON body from the POST request request_data = request.get_json(silent=True) - # If the request body is missing or invalid JSON, return a 400 error if request_data is None: return { "error": { @@ -62,24 +60,78 @@ def checkout(): } }, 400 - # Print the full request and a few important parts + user = request_data.get("user", {}) + items = request_data.get("items", []) + shipping_method = request_data.get("shippingMethod") + terms_accepted = request_data.get("termsAndConditionsAccepted", False) + + user_name = user.get("name") + user_contact = user.get("contact") + user_comment = user.get("userComment", "") + + credit_card = user.get("creditCard", {}) + card_number = credit_card.get("number") + expiration_date = credit_card.get("expirationDate") + cvv = credit_card.get("cvv") + print("FULL REQUEST DATA:", request_data) - print("USER:", request_data.get("user")) - print("ITEMS:", request_data.get("items")) - print("SHIPPING METHOD:", request_data.get("shippingMethod")) - print("TERMS ACCEPTED:", request_data.get("termsAndConditionsAccepted")) - - # Still a dummy response for now - order_status_response = { - 'orderId': '12345', - 'status': 'Order Approved', - 'suggestedBooks': [ - {'bookId': '123', 'title': 'The Best Book', 'author': 'Author 1'}, - {'bookId': '456', 'title': 'The Second Best Book', 'author': 'Author 2'} - ] - } + print("USER NAME:", user_name) + print("USER CONTACT:", user_contact) + print("USER COMMENT:", user_comment) + print("ITEMS:", items) + print("SHIPPING METHOD:", shipping_method) + print("TERMS ACCEPTED:", terms_accepted) + print("CARD NUMBER:", card_number) + print("EXPIRATION DATE:", expiration_date) + print("CVV:", cvv) + + # Basic validation + if not user_name: + return { + "error": { + "code": "BAD_REQUEST", + "message": "User name is required." + } + }, 400 - return order_status_response, 200 + if not user_contact: + return { + "error": { + "code": "BAD_REQUEST", + "message": "User contact is required." + } + }, 400 + + if not items: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + + if not terms_accepted: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + + if not card_number or not expiration_date or not cvv: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + + # Dummy success response for now + return { + "orderId": "12345", + "status": "Order Approved", + "suggestedBooks": [ + {"bookId": "123", "title": "The Best Book", "author": "Author 1"}, + {"bookId": "456", "title": "The Second Best Book", "author": "Author 2"} + ] + }, 200 if __name__ == '__main__': From e38851effe80c2e2eecf900e31ec86e14a1a6777 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 09:47:17 +0200 Subject: [PATCH 04/58] Add checkout validation tests and scaffold transaction_verification service --- test_checkout_empty_items.json | 15 +++++++++++++++ test_checkout_terms_false.json | 20 ++++++++++++++++++++ transaction_verification/Dockerfile | 0 transaction_verification/requirements.txt | 0 transaction_verification/src/app.py | 0 5 files changed, 35 insertions(+) create mode 100644 test_checkout_empty_items.json create mode 100644 test_checkout_terms_false.json create mode 100644 transaction_verification/Dockerfile create mode 100644 transaction_verification/requirements.txt create mode 100644 transaction_verification/src/app.py diff --git a/test_checkout_empty_items.json b/test_checkout_empty_items.json new file mode 100644 index 000000000..df39e004d --- /dev/null +++ b/test_checkout_empty_items.json @@ -0,0 +1,15 @@ +{ + "user": { + "name": "Test User", + "contact": "test@example.com", + "creditCard": { + "number": "4111111111111111", + "expirationDate": "12/30", + "cvv": "123" + }, + "userComment": "Please handle with care." + }, + "items": [], + "shippingMethod": "Standard", + "termsAndConditionsAccepted": true +} diff --git a/test_checkout_terms_false.json b/test_checkout_terms_false.json new file mode 100644 index 000000000..82087d4b1 --- /dev/null +++ b/test_checkout_terms_false.json @@ -0,0 +1,20 @@ +{ + "user": { + "name": "Test User", + "contact": "test@example.com", + "creditCard": { + "number": "4111111111111111", + "expirationDate": "12/30", + "cvv": "123" + }, + "userComment": "Please handle with care." + }, + "items": [ + { + "name": "Book A", + "quantity": 1 + } + ], + "shippingMethod": "Standard", + "termsAndConditionsAccepted": false +} diff --git a/transaction_verification/Dockerfile b/transaction_verification/Dockerfile new file mode 100644 index 000000000..e69de29bb diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py new file mode 100644 index 000000000..e69de29bb From 8491cc9cb8b1622aff59727f17167d32ec79814e Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 10:12:05 +0200 Subject: [PATCH 05/58] Add transaction_verification gRPC service and Docker Compose setup --- docker-compose.yaml | 26 ++++++- transaction_verification/Dockerfile | 8 +++ transaction_verification/requirements.txt | 4 ++ transaction_verification/src/app.py | 67 +++++++++++++++++++ utils/pb/transaction_verification/__init__.py | 0 .../transaction_verification.proto | 22 ++++++ .../transaction_verification_pb2.py | 30 +++++++++ .../transaction_verification_pb2.pyi | 31 +++++++++ .../transaction_verification_pb2_grpc.py | 66 ++++++++++++++++++ 9 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 utils/pb/transaction_verification/__init__.py create mode 100644 utils/pb/transaction_verification/transaction_verification.proto create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2.py create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2.pyi create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2_grpc.py diff --git a/docker-compose.yaml b/docker-compose.yaml index b4a60a537..a4ea5c994 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,6 +13,7 @@ services: volumes: # Mount the frontend directory - ./frontend/src:/usr/share/nginx/html + orchestrator: build: # Use the current directory as the build context @@ -35,12 +36,13 @@ services: - ./utils:/app/utils # Mount the orchestrator/src directory in the current directory to the /app/orchestrator/src directory in the container - ./orchestrator/src:/app/orchestrator/src + fraud_detection: build: # Use the current directory as the build context # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the fraud_detection directorys + # Use the Dockerfile in the fraud_detection directory dockerfile: ./fraud_detection/Dockerfile ports: # Expose port 50051 on the host, and map port 50051 of the container to port 50051 on the host @@ -56,4 +58,24 @@ services: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils # Mount the fraud_detection/src directory in the current directory to the /app/fraud_detection/src directory in the container - - ./fraud_detection/src:/app/fraud_detection/src \ No newline at end of file + - ./fraud_detection/src:/app/fraud_detection/src + + transaction_verification: + build: + # Use the current directory as the build context + context: ./ + # Use the Dockerfile in the transaction_verification directory + dockerfile: ./transaction_verification/Dockerfile + ports: + # Expose port 50052 on the host, and map port 50052 of the container to port 50052 on the host + - 50052:50052 + environment: + # Pass the environment variables to the container + - PYTHONUNBUFFERED=TRUE + # The PYTHONFILE environment variable specifies the absolute entry point of the application + - PYTHONFILE=/app/transaction_verification/src/app.py + volumes: + # Mount the utils directory in the current directory to the /app/utils directory in the container + - ./utils:/app/utils + # Mount the transaction_verification/src directory in the current directory to the container + - ./transaction_verification/src:/app/transaction_verification/src \ No newline at end of file diff --git a/transaction_verification/Dockerfile b/transaction_verification/Dockerfile index e69de29bb..ad4936c4a 100644 --- a/transaction_verification/Dockerfile +++ b/transaction_verification/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.11 + +WORKDIR /app + +COPY ./transaction_verification/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +CMD python utils/other/hotreload.py "transaction_verification/src/app.py" \ No newline at end of file diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt index e69de29bb..43fdaf26f 100644 --- a/transaction_verification/requirements.txt +++ b/transaction_verification/requirements.txt @@ -0,0 +1,4 @@ +grpcio==1.60.0 +grpcio-tools==1.60.0 +protobuf==4.25.2 +watchdog==6.0.0 \ No newline at end of file diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index e69de29bb..36f592052 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -0,0 +1,67 @@ +import sys +import os +from concurrent import futures + +FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +transaction_verification_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/transaction_verification') +) +sys.path.insert(0, transaction_verification_grpc_path) + +import grpc +import transaction_verification_pb2 as transaction_verification +import transaction_verification_pb2_grpc as transaction_verification_grpc + + +class TransactionVerificationService( + transaction_verification_grpc.TransactionVerificationServiceServicer +): + def VerifyTransaction(self, request, context): + print("Received transaction verification request") + print("user_name:", request.user_name) + print("user_contact:", request.user_contact) + print("item_count:", request.item_count) + print("terms_accepted:", request.terms_accepted) + + is_valid = True + message = "Transaction is valid." + + if not request.user_name: + is_valid = False + message = "Missing user name." + elif not request.user_contact: + is_valid = False + message = "Missing user contact." + elif request.item_count <= 0: + is_valid = False + message = "No items in order." + elif not request.terms_accepted: + is_valid = False + message = "Terms and conditions not accepted." + elif not request.card_number or not request.expiration_date or not request.cvv: + is_valid = False + message = "Missing credit card information." + + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = is_valid + response.message = message + + print("Returning verification result:", response.is_valid, response.message) + return response + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor()) + transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( + TransactionVerificationService(), server + ) + + port = "50052" + server.add_insecure_port("[::]:" + port) + server.start() + print("Transaction verification server started. Listening on port 50052.") + server.wait_for_termination() + + +if __name__ == '__main__': + serve() \ No newline at end of file diff --git a/utils/pb/transaction_verification/__init__.py b/utils/pb/transaction_verification/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/pb/transaction_verification/transaction_verification.proto b/utils/pb/transaction_verification/transaction_verification.proto new file mode 100644 index 000000000..44ba9cec7 --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package transaction_verification; + +service TransactionVerificationService { + rpc VerifyTransaction (TransactionVerificationRequest) returns (TransactionVerificationResponse); +} + +message TransactionVerificationRequest { + string user_name = 1; + string user_contact = 2; + string card_number = 3; + string expiration_date = 4; + string cvv = 5; + int32 item_count = 6; + bool terms_accepted = 7; +} + +message TransactionVerificationResponse { + bool is_valid = 1; + string message = 2; +} \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py new file mode 100644 index 000000000..447731158 --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: transaction_verification.proto +# Protobuf Python Version: 4.25.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xb0\x01\n\x1eTransactionVerificationRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x14\n\x0cuser_contact\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x03 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x04 \x01(\t\x12\x0b\n\x03\x63vv\x18\x05 \x01(\t\x12\x12\n\nitem_count\x18\x06 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x07 \x01(\x08\"D\n\x1fTransactionVerificationResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xab\x01\n\x1eTransactionVerificationService\x12\x88\x01\n\x11VerifyTransaction\x12\x38.transaction_verification.TransactionVerificationRequest\x1a\x39.transaction_verification.TransactionVerificationResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_start=61 + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_end=237 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_start=239 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_end=307 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=310 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=481 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.pyi b/utils/pb/transaction_verification/transaction_verification_pb2.pyi new file mode 100644 index 000000000..5bb457074 --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2.pyi @@ -0,0 +1,31 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class TransactionVerificationRequest(_message.Message): + __slots__ = ("user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") + USER_NAME_FIELD_NUMBER: _ClassVar[int] + USER_CONTACT_FIELD_NUMBER: _ClassVar[int] + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_DATE_FIELD_NUMBER: _ClassVar[int] + CVV_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] + user_name: str + user_contact: str + card_number: str + expiration_date: str + cvv: str + item_count: int + terms_accepted: bool + def __init__(self, user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + +class TransactionVerificationResponse(_message.Message): + __slots__ = ("is_valid", "message") + IS_VALID_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + is_valid: bool + message: str + def __init__(self, is_valid: bool = ..., message: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py new file mode 100644 index 000000000..32d7e091c --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import transaction_verification_pb2 as transaction__verification__pb2 + + +class TransactionVerificationServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.VerifyTransaction = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/VerifyTransaction', + request_serializer=transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.TransactionVerificationResponse.FromString, + ) + + +class TransactionVerificationServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def VerifyTransaction(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_TransactionVerificationServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'VerifyTransaction': grpc.unary_unary_rpc_method_handler( + servicer.VerifyTransaction, + request_deserializer=transaction__verification__pb2.TransactionVerificationRequest.FromString, + response_serializer=transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'transaction_verification.TransactionVerificationService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class TransactionVerificationService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def VerifyTransaction(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/transaction_verification.TransactionVerificationService/VerifyTransaction', + transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + transaction__verification__pb2.TransactionVerificationResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From 606456c4304a8f119f0ff9113dcc7da396d9e0d1 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 10:30:05 +0200 Subject: [PATCH 06/58] Connect orchestrator to transaction_verification via gRPC --- orchestrator/src/app.py | 84 +++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 8d23bc7d5..67e48007f 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -5,13 +5,28 @@ # The path of the stubs is relative to the current file, or absolute inside the container. # Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) + +fraud_detection_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/fraud_detection') +) sys.path.insert(0, fraud_detection_grpc_path) import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc +transaction_verification_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/transaction_verification') +) +sys.path.insert(0, transaction_verification_grpc_path) +import transaction_verification_pb2 as transaction_verification +import transaction_verification_pb2_grpc as transaction_verification_grpc + import grpc +# Import Flask. +from flask import Flask, request +from flask_cors import CORS + + def greet(name='you'): # Establish a connection with the fraud-detection gRPC service. with grpc.insecure_channel('fraud_detection:50051') as channel: @@ -21,19 +36,33 @@ def greet(name='you'): response = stub.SayHello(fraud_detection.HelloRequest(name=name)) return response.greeting -# Import Flask. -# Flask is a web framework for Python. -# It allows you to build a web application quickly. -# For more information, see https://flask.palletsprojects.com/en/latest/ -from flask import Flask, request -from flask_cors import CORS -import json + +def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): + # Establish a connection with the transaction_verification gRPC service. + with grpc.insecure_channel('transaction_verification:50052') as channel: + # Create a stub object. + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + # Call the service through the stub object. + response = stub.VerifyTransaction( + transaction_verification.TransactionVerificationRequest( + user_name=user_name or "", + user_contact=user_contact or "", + card_number=card_number or "", + expiration_date=expiration_date or "", + cvv=cvv or "", + item_count=item_count, + terms_accepted=terms_accepted + ) + ) + return response + # Create a simple Flask app. app = Flask(__name__) # Enable CORS for the app. CORS(app, resources={r'/*': {'origins': '*'}}) + # Define a GET endpoint. @app.route('/', methods=['GET']) def index(): @@ -45,6 +74,7 @@ def index(): # Return the response. return response + @app.route('/checkout', methods=['POST']) def checkout(): """ @@ -85,7 +115,7 @@ def checkout(): print("EXPIRATION DATE:", expiration_date) print("CVV:", cvv) - # Basic validation + # Keep these simple bad-request checks locally if not user_name: return { "error": { @@ -102,21 +132,25 @@ def checkout(): } }, 400 - if not items: - return { - "orderId": "12345", - "status": "Order Rejected", - "suggestedBooks": [] - }, 200 - - if not terms_accepted: - return { - "orderId": "12345", - "status": "Order Rejected", - "suggestedBooks": [] - }, 200 - - if not card_number or not expiration_date or not cvv: + item_count = len(items) + + print("Calling transaction_verification service...") + verification_response = verify_transaction( + user_name=user_name, + user_contact=user_contact, + card_number=card_number, + expiration_date=expiration_date, + cvv=cvv, + item_count=item_count, + terms_accepted=terms_accepted + ) + print( + "transaction_verification result:", + verification_response.is_valid, + verification_response.message + ) + + if not verification_response.is_valid: return { "orderId": "12345", "status": "Order Rejected", @@ -138,4 +172,4 @@ def checkout(): # Run the app in debug mode to enable hot reloading. # This is useful for development. # The default port is 5000. - app.run(host='0.0.0.0') + app.run(host='0.0.0.0') \ No newline at end of file From f8c3e61eb49e4de85d2b93be701d76faa83aee00 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 10:51:50 +0200 Subject: [PATCH 07/58] Add suggestions gRPC service and connect it to orchestrator --- docker-compose.yaml | 15 ++++- orchestrator/src/app.py | 43 +++++++++++-- suggestions/Dockerfile | 8 +++ suggestions/requirements.txt | 4 ++ suggestions/src/app.py | 60 ++++++++++++++++++ utils/pb/suggestions/__init__.py | 0 utils/pb/suggestions/suggestions.proto | 22 +++++++ utils/pb/suggestions/suggestions_pb2.py | 32 ++++++++++ utils/pb/suggestions/suggestions_pb2.pyi | 30 +++++++++ utils/pb/suggestions/suggestions_pb2_grpc.py | 66 ++++++++++++++++++++ 10 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 suggestions/Dockerfile create mode 100644 suggestions/requirements.txt create mode 100644 suggestions/src/app.py create mode 100644 utils/pb/suggestions/__init__.py create mode 100644 utils/pb/suggestions/suggestions.proto create mode 100644 utils/pb/suggestions/suggestions_pb2.py create mode 100644 utils/pb/suggestions/suggestions_pb2.pyi create mode 100644 utils/pb/suggestions/suggestions_pb2_grpc.py diff --git a/docker-compose.yaml b/docker-compose.yaml index a4ea5c994..e4818a9c8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -78,4 +78,17 @@ services: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils # Mount the transaction_verification/src directory in the current directory to the container - - ./transaction_verification/src:/app/transaction_verification/src \ No newline at end of file + - ./transaction_verification/src:/app/transaction_verification/src + + suggestions: + build: + context: ./ + dockerfile: ./suggestions/Dockerfile + ports: + - 50053:50053 + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/suggestions/src/app.py + volumes: + - ./utils:/app/utils + - ./suggestions/src:/app/suggestions/src \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 67e48007f..94d5e8949 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -20,6 +20,13 @@ import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/suggestions') +) +sys.path.insert(0, suggestions_grpc_path) +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc + import grpc # Import Flask. @@ -57,6 +64,21 @@ def verify_transaction(user_name, user_contact, card_number, expiration_date, cv return response +def get_suggestions(user_name, item_count): + # Establish a connection with the suggestions gRPC service. + with grpc.insecure_channel('suggestions:50053') as channel: + # Create a stub object. + stub = suggestions_grpc.SuggestionsServiceStub(channel) + # Call the service through the stub object. + response = stub.GetSuggestions( + suggestions.SuggestionsRequest( + user_name=user_name or "", + item_count=item_count + ) + ) + return response + + # Create a simple Flask app. app = Flask(__name__) # Enable CORS for the app. @@ -157,14 +179,25 @@ def checkout(): "suggestedBooks": [] }, 200 - # Dummy success response for now + print("Calling suggestions service...") + suggestions_response = get_suggestions( + user_name=user_name, + item_count=item_count + ) + print("suggestions returned:", len(suggestions_response.books), "books") + + suggested_books = [] + for book in suggestions_response.books: + suggested_books.append({ + "bookId": book.bookId, + "title": book.title, + "author": book.author + }) + return { "orderId": "12345", "status": "Order Approved", - "suggestedBooks": [ - {"bookId": "123", "title": "The Best Book", "author": "Author 1"}, - {"bookId": "456", "title": "The Second Best Book", "author": "Author 2"} - ] + "suggestedBooks": suggested_books }, 200 diff --git a/suggestions/Dockerfile b/suggestions/Dockerfile new file mode 100644 index 000000000..1c35664df --- /dev/null +++ b/suggestions/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.11 + +WORKDIR /app + +COPY ./suggestions/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +CMD python utils/other/hotreload.py "suggestions/src/app.py" \ No newline at end of file diff --git a/suggestions/requirements.txt b/suggestions/requirements.txt new file mode 100644 index 000000000..43fdaf26f --- /dev/null +++ b/suggestions/requirements.txt @@ -0,0 +1,4 @@ +grpcio==1.60.0 +grpcio-tools==1.60.0 +protobuf==4.25.2 +watchdog==6.0.0 \ No newline at end of file diff --git a/suggestions/src/app.py b/suggestions/src/app.py new file mode 100644 index 000000000..258c2fb0c --- /dev/null +++ b/suggestions/src/app.py @@ -0,0 +1,60 @@ +import sys +import os +from concurrent import futures + +FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/suggestions') +) +sys.path.insert(0, suggestions_grpc_path) + +import grpc +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc + + +class SuggestionsService(suggestions_grpc.SuggestionsServiceServicer): + def GetSuggestions(self, request, context): + print("Received suggestions request") + print("user_name:", request.user_name) + print("item_count:", request.item_count) + + static_books = [ + {"bookId": "101", "title": "Distributed Systems Basics", "author": "A. Author"}, + {"bookId": "102", "title": "Designing Data-Intensive Applications", "author": "Martin Kleppmann"}, + {"bookId": "103", "title": "Clean Code", "author": "Robert C. Martin"}, + {"bookId": "104", "title": "The Pragmatic Programmer", "author": "Andrew Hunt"}, + ] + + response = suggestions.SuggestionsResponse() + + if request.item_count > 0: + chosen = static_books[:2] + else: + chosen = [] + + for book in chosen: + b = response.books.add() + b.bookId = book["bookId"] + b.title = book["title"] + b.author = book["author"] + + print("Returning", len(response.books), "suggested books") + return response + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor()) + suggestions_grpc.add_SuggestionsServiceServicer_to_server( + SuggestionsService(), server + ) + + port = "50053" + server.add_insecure_port("[::]:" + port) + server.start() + print("Suggestions server started. Listening on port 50053.") + server.wait_for_termination() + + +if __name__ == '__main__': + serve() \ No newline at end of file diff --git a/utils/pb/suggestions/__init__.py b/utils/pb/suggestions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto new file mode 100644 index 000000000..12f15f4cb --- /dev/null +++ b/utils/pb/suggestions/suggestions.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package suggestions; + +service SuggestionsService { + rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); +} + +message SuggestionsRequest { + string user_name = 1; + int32 item_count = 2; +} + +message SuggestedBook { + string bookId = 1; + string title = 2; + string author = 3; +} + +message SuggestionsResponse { + repeated SuggestedBook books = 1; +} \ No newline at end of file diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py new file mode 100644 index 000000000..550a4d48e --- /dev/null +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: suggestions.proto +# Protobuf Python Version: 4.25.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\";\n\x12SuggestionsRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x12\n\nitem_count\x18\x02 \x01(\x05\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"@\n\x13SuggestionsResponse\x12)\n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x1a.suggestions.SuggestedBook2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'suggestions_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_SUGGESTIONSREQUEST']._serialized_start=34 + _globals['_SUGGESTIONSREQUEST']._serialized_end=93 + _globals['_SUGGESTEDBOOK']._serialized_start=95 + _globals['_SUGGESTEDBOOK']._serialized_end=157 + _globals['_SUGGESTIONSRESPONSE']._serialized_start=159 + _globals['_SUGGESTIONSRESPONSE']._serialized_end=223 + _globals['_SUGGESTIONSSERVICE']._serialized_start=225 + _globals['_SUGGESTIONSSERVICE']._serialized_end=330 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/suggestions/suggestions_pb2.pyi b/utils/pb/suggestions/suggestions_pb2.pyi new file mode 100644 index 000000000..d3d31c343 --- /dev/null +++ b/utils/pb/suggestions/suggestions_pb2.pyi @@ -0,0 +1,30 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SuggestionsRequest(_message.Message): + __slots__ = ("user_name", "item_count") + USER_NAME_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + user_name: str + item_count: int + def __init__(self, user_name: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... + +class SuggestedBook(_message.Message): + __slots__ = ("bookId", "title", "author") + BOOKID_FIELD_NUMBER: _ClassVar[int] + TITLE_FIELD_NUMBER: _ClassVar[int] + AUTHOR_FIELD_NUMBER: _ClassVar[int] + bookId: str + title: str + author: str + def __init__(self, bookId: _Optional[str] = ..., title: _Optional[str] = ..., author: _Optional[str] = ...) -> None: ... + +class SuggestionsResponse(_message.Message): + __slots__ = ("books",) + BOOKS_FIELD_NUMBER: _ClassVar[int] + books: _containers.RepeatedCompositeFieldContainer[SuggestedBook] + def __init__(self, books: _Optional[_Iterable[_Union[SuggestedBook, _Mapping]]] = ...) -> None: ... diff --git a/utils/pb/suggestions/suggestions_pb2_grpc.py b/utils/pb/suggestions/suggestions_pb2_grpc.py new file mode 100644 index 000000000..ca1154a0b --- /dev/null +++ b/utils/pb/suggestions/suggestions_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import suggestions_pb2 as suggestions__pb2 + + +class SuggestionsServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetSuggestions = channel.unary_unary( + '/suggestions.SuggestionsService/GetSuggestions', + request_serializer=suggestions__pb2.SuggestionsRequest.SerializeToString, + response_deserializer=suggestions__pb2.SuggestionsResponse.FromString, + ) + + +class SuggestionsServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def GetSuggestions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_SuggestionsServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetSuggestions': grpc.unary_unary_rpc_method_handler( + servicer.GetSuggestions, + request_deserializer=suggestions__pb2.SuggestionsRequest.FromString, + response_serializer=suggestions__pb2.SuggestionsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'suggestions.SuggestionsService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class SuggestionsService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def GetSuggestions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/suggestions.SuggestionsService/GetSuggestions', + suggestions__pb2.SuggestionsRequest.SerializeToString, + suggestions__pb2.SuggestionsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From b9c7581477b76ffd3a97af35743c7c3260f8c2e9 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 11:11:27 +0200 Subject: [PATCH 08/58] Add fraud detection gRPC check to checkout flow --- fraud_detection/src/app.py | 55 +++++++++++++------ orchestrator/src/app.py | 31 +++++++++++ test_checkout_fraud.json | 20 +++++++ .../pb/fraud_detection/fraud_detection.proto | 18 +++++- .../pb/fraud_detection/fraud_detection_pb2.py | 10 +++- .../fraud_detection/fraud_detection_pb2.pyi | 18 ++++++ .../fraud_detection_pb2_grpc.py | 33 +++++++++++ 7 files changed, 161 insertions(+), 24 deletions(-) create mode 100644 test_checkout_fraud.json diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index b2f1d2fce..d932ef47d 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -1,45 +1,64 @@ import sys import os +from concurrent import futures # This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) +fraud_detection_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/fraud_detection') +) sys.path.insert(0, fraud_detection_grpc_path) + +import grpc import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc -import grpc -from concurrent import futures -# Create a class to define the server functions, derived from -# fraud_detection_pb2_grpc.HelloServiceServicer class HelloService(fraud_detection_grpc.HelloServiceServicer): - # Create an RPC function to say hello def SayHello(self, request, context): - # Create a HelloResponse object response = fraud_detection.HelloResponse() - # Set the greeting field of the response object response.greeting = "Hello, " + request.name - # Print the greeting message print(response.greeting) - # Return the response object return response + def CheckFraud(self, request, context): + print("Received fraud check request") + print("user_name:", request.user_name) + print("card_number:", request.card_number) + print("item_count:", request.item_count) + + is_fraud = False + message = "No fraud detected." + + # Very simple dummy rules for now + if request.item_count > 10: + is_fraud = True + message = "Too many items in order." + elif request.card_number.endswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif "fraud" in request.user_name.lower(): + is_fraud = True + message = "Suspicious user name." + + response = fraud_detection.FraudCheckResponse() + response.is_fraud = is_fraud + response.message = message + + print("Returning fraud result:", response.is_fraud, response.message) + return response + + def serve(): - # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) - # Add HelloService fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) - # Listen on port 50051 + port = "50051" server.add_insecure_port("[::]:" + port) - # Start the server server.start() - print("Server started. Listening on port 50051.") - # Keep thread alive + print("Fraud detection server started. Listening on port 50051.") server.wait_for_termination() + if __name__ == '__main__': serve() \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 94d5e8949..2b5045787 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -44,6 +44,22 @@ def greet(name='you'): return response.greeting +def detect_fraud(user_name, card_number, item_count): + # Establish a connection with the fraud-detection gRPC service. + with grpc.insecure_channel('fraud_detection:50051') as channel: + # Create a stub object. + stub = fraud_detection_grpc.HelloServiceStub(channel) + # Call the service through the stub object. + response = stub.CheckFraud( + fraud_detection.FraudCheckRequest( + user_name=user_name or "", + card_number=card_number or "", + item_count=item_count + ) + ) + return response + + def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): # Establish a connection with the transaction_verification gRPC service. with grpc.insecure_channel('transaction_verification:50052') as channel: @@ -156,6 +172,21 @@ def checkout(): item_count = len(items) + print("Calling fraud_detection service...") + fraud_response = detect_fraud( + user_name=user_name, + card_number=card_number, + item_count=item_count + ) + print("fraud_detection result:", fraud_response.is_fraud, fraud_response.message) + + if fraud_response.is_fraud: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + print("Calling transaction_verification service...") verification_response = verify_transaction( user_name=user_name, diff --git a/test_checkout_fraud.json b/test_checkout_fraud.json new file mode 100644 index 000000000..8c7677348 --- /dev/null +++ b/test_checkout_fraud.json @@ -0,0 +1,20 @@ +{ + "user": { + "name": "Test User", + "contact": "test@example.com", + "creditCard": { + "number": "4111111111110000", + "expirationDate": "12/30", + "cvv": "123" + }, + "userComment": "Please handle with care." + }, + "items": [ + { + "name": "Book A", + "quantity": 1 + } + ], + "shippingMethod": "Standard", + "termsAndConditionsAccepted": true +} diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index db20211d7..d575366b1 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -3,13 +3,25 @@ syntax = "proto3"; package hello; service HelloService { - rpc SayHello (HelloRequest) returns (HelloResponse); + rpc SayHello (HelloRequest) returns (HelloResponse); + rpc CheckFraud (FraudCheckRequest) returns (FraudCheckResponse); } message HelloRequest { - string name = 1; + string name = 1; } message HelloResponse { - string greeting = 1; + string greeting = 1; } + +message FraudCheckRequest { + string user_name = 1; + string card_number = 2; + int32 item_count = 3; +} + +message FraudCheckResponse { + bool is_fraud = 1; + string message = 2; +} \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index cdd0bcae8..d405d9cf5 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t2E\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t\"O\n\x11\x46raudCheckRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x02 \x01(\t\x12\x12\n\nitem_count\x18\x03 \x01(\x05\"7\n\x12\x46raudCheckResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\x88\x01\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponse\x12\x41\n\nCheckFraud\x12\x18.hello.FraudCheckRequest\x1a\x19.hello.FraudCheckResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -25,6 +25,10 @@ _globals['_HELLOREQUEST']._serialized_end=60 _globals['_HELLORESPONSE']._serialized_start=62 _globals['_HELLORESPONSE']._serialized_end=95 - _globals['_HELLOSERVICE']._serialized_start=97 - _globals['_HELLOSERVICE']._serialized_end=166 + _globals['_FRAUDCHECKREQUEST']._serialized_start=97 + _globals['_FRAUDCHECKREQUEST']._serialized_end=176 + _globals['_FRAUDCHECKRESPONSE']._serialized_start=178 + _globals['_FRAUDCHECKRESPONSE']._serialized_end=233 + _globals['_HELLOSERVICE']._serialized_start=236 + _globals['_HELLOSERVICE']._serialized_end=372 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index 30a263856..fe3863b06 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -15,3 +15,21 @@ class HelloResponse(_message.Message): GREETING_FIELD_NUMBER: _ClassVar[int] greeting: str def __init__(self, greeting: _Optional[str] = ...) -> None: ... + +class FraudCheckRequest(_message.Message): + __slots__ = ("user_name", "card_number", "item_count") + USER_NAME_FIELD_NUMBER: _ClassVar[int] + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + user_name: str + card_number: str + item_count: int + def __init__(self, user_name: _Optional[str] = ..., card_number: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... + +class FraudCheckResponse(_message.Message): + __slots__ = ("is_fraud", "message") + IS_FRAUD_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + is_fraud: bool + message: str + def __init__(self, is_fraud: bool = ..., message: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 4e7a27975..3ada2e86e 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -19,6 +19,11 @@ def __init__(self, channel): request_serializer=fraud__detection__pb2.HelloRequest.SerializeToString, response_deserializer=fraud__detection__pb2.HelloResponse.FromString, ) + self.CheckFraud = channel.unary_unary( + '/hello.HelloService/CheckFraud', + request_serializer=fraud__detection__pb2.FraudCheckRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.FraudCheckResponse.FromString, + ) class HelloServiceServicer(object): @@ -30,6 +35,12 @@ def SayHello(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def CheckFraud(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_HelloServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -38,6 +49,11 @@ def add_HelloServiceServicer_to_server(servicer, server): request_deserializer=fraud__detection__pb2.HelloRequest.FromString, response_serializer=fraud__detection__pb2.HelloResponse.SerializeToString, ), + 'CheckFraud': grpc.unary_unary_rpc_method_handler( + servicer.CheckFraud, + request_deserializer=fraud__detection__pb2.FraudCheckRequest.FromString, + response_serializer=fraud__detection__pb2.FraudCheckResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'hello.HelloService', rpc_method_handlers) @@ -64,3 +80,20 @@ def SayHello(request, fraud__detection__pb2.HelloResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CheckFraud(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/hello.HelloService/CheckFraud', + fraud__detection__pb2.FraudCheckRequest.SerializeToString, + fraud__detection__pb2.FraudCheckResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From 0cb5ee217cec1296e99db1fec5375a7647adf863 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 11:20:27 +0200 Subject: [PATCH 09/58] Add threaded orchestration for backend gRPC services --- orchestrator/src/app.py | 160 ++++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 65 deletions(-) diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 2b5045787..64341eb19 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,5 +1,6 @@ import sys import os +import threading # This set of lines are needed to import the gRPC stubs. # The path of the stubs is relative to the current file, or absolute inside the container. @@ -29,27 +30,20 @@ import grpc -# Import Flask. from flask import Flask, request from flask_cors import CORS def greet(name='you'): - # Establish a connection with the fraud-detection gRPC service. with grpc.insecure_channel('fraud_detection:50051') as channel: - # Create a stub object. stub = fraud_detection_grpc.HelloServiceStub(channel) - # Call the service through the stub object. response = stub.SayHello(fraud_detection.HelloRequest(name=name)) return response.greeting def detect_fraud(user_name, card_number, item_count): - # Establish a connection with the fraud-detection gRPC service. with grpc.insecure_channel('fraud_detection:50051') as channel: - # Create a stub object. stub = fraud_detection_grpc.HelloServiceStub(channel) - # Call the service through the stub object. response = stub.CheckFraud( fraud_detection.FraudCheckRequest( user_name=user_name or "", @@ -61,11 +55,8 @@ def detect_fraud(user_name, card_number, item_count): def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): - # Establish a connection with the transaction_verification gRPC service. with grpc.insecure_channel('transaction_verification:50052') as channel: - # Create a stub object. stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - # Call the service through the stub object. response = stub.VerifyTransaction( transaction_verification.TransactionVerificationRequest( user_name=user_name or "", @@ -81,11 +72,8 @@ def verify_transaction(user_name, user_contact, card_number, expiration_date, cv def get_suggestions(user_name, item_count): - # Establish a connection with the suggestions gRPC service. with grpc.insecure_channel('suggestions:50053') as channel: - # Create a stub object. stub = suggestions_grpc.SuggestionsServiceStub(channel) - # Call the service through the stub object. response = stub.GetSuggestions( suggestions.SuggestionsRequest( user_name=user_name or "", @@ -95,29 +83,18 @@ def get_suggestions(user_name, item_count): return response -# Create a simple Flask app. app = Flask(__name__) -# Enable CORS for the app. CORS(app, resources={r'/*': {'origins': '*'}}) -# Define a GET endpoint. @app.route('/', methods=['GET']) def index(): - """ - Responds with 'Hello, [name]' when a GET request is made to '/' endpoint. - """ - # Test the fraud-detection gRPC service. response = greet(name='orchestrator') - # Return the response. return response @app.route('/checkout', methods=['POST']) def checkout(): - """ - Responds with a JSON object containing the order ID, status, and suggested books. - """ request_data = request.get_json(silent=True) if request_data is None: @@ -172,58 +149,114 @@ def checkout(): item_count = len(items) - print("Calling fraud_detection service...") - fraud_response = detect_fraud( - user_name=user_name, - card_number=card_number, - item_count=item_count - ) - print("fraud_detection result:", fraud_response.is_fraud, fraud_response.message) + results = { + "fraud": None, + "verification": None, + "suggestions": None, + "errors": [] + } + + def fraud_worker(): + try: + print("Calling fraud_detection service...") + results["fraud"] = detect_fraud( + user_name=user_name, + card_number=card_number, + item_count=item_count + ) + print( + "fraud_detection result:", + results["fraud"].is_fraud, + results["fraud"].message + ) + except Exception as e: + error_msg = f"fraud_detection failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + def verification_worker(): + try: + print("Calling transaction_verification service...") + results["verification"] = verify_transaction( + user_name=user_name, + user_contact=user_contact, + card_number=card_number, + expiration_date=expiration_date, + cvv=cvv, + item_count=item_count, + terms_accepted=terms_accepted + ) + print( + "transaction_verification result:", + results["verification"].is_valid, + results["verification"].message + ) + except Exception as e: + error_msg = f"transaction_verification failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + def suggestions_worker(): + try: + print("Calling suggestions service...") + results["suggestions"] = get_suggestions( + user_name=user_name, + item_count=item_count + ) + print( + "suggestions returned:", + len(results["suggestions"].books), + "books" + ) + except Exception as e: + error_msg = f"suggestions failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + fraud_thread = threading.Thread(target=fraud_worker) + verification_thread = threading.Thread(target=verification_worker) + suggestions_thread = threading.Thread(target=suggestions_worker) + + print("Starting worker threads...") + fraud_thread.start() + verification_thread.start() + suggestions_thread.start() + + fraud_thread.join() + verification_thread.join() + suggestions_thread.join() + print("All worker threads finished.") + + if results["errors"]: + return { + "error": { + "code": "INTERNAL_ERROR", + "message": "; ".join(results["errors"]) + } + }, 500 - if fraud_response.is_fraud: + if results["fraud"] and results["fraud"].is_fraud: return { "orderId": "12345", "status": "Order Rejected", "suggestedBooks": [] }, 200 - print("Calling transaction_verification service...") - verification_response = verify_transaction( - user_name=user_name, - user_contact=user_contact, - card_number=card_number, - expiration_date=expiration_date, - cvv=cvv, - item_count=item_count, - terms_accepted=terms_accepted - ) - print( - "transaction_verification result:", - verification_response.is_valid, - verification_response.message - ) - - if not verification_response.is_valid: + if results["verification"] and not results["verification"].is_valid: return { "orderId": "12345", "status": "Order Rejected", "suggestedBooks": [] }, 200 - print("Calling suggestions service...") - suggestions_response = get_suggestions( - user_name=user_name, - item_count=item_count - ) - print("suggestions returned:", len(suggestions_response.books), "books") - suggested_books = [] - for book in suggestions_response.books: - suggested_books.append({ - "bookId": book.bookId, - "title": book.title, - "author": book.author - }) + if results["suggestions"]: + for book in results["suggestions"].books: + suggested_books.append({ + "bookId": book.bookId, + "title": book.title, + "author": book.author + }) return { "orderId": "12345", @@ -233,7 +266,4 @@ def checkout(): if __name__ == '__main__': - # Run the app in debug mode to enable hot reloading. - # This is useful for development. - # The default port is 5000. app.run(host='0.0.0.0') \ No newline at end of file From 4cd2062cf4f07e3e354643595a91ca2a6ec97177 Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Sat, 28 Feb 2026 11:23:01 +0200 Subject: [PATCH 10/58] Fraud Detection --- fraud_detection/src/app.py | 24 +++++----- orchestrator/src/app.py | 91 ++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 53 deletions(-) diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index b2f1d2fce..b85b64b9d 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -15,23 +15,25 @@ # Create a class to define the server functions, derived from # fraud_detection_pb2_grpc.HelloServiceServicer -class HelloService(fraud_detection_grpc.HelloServiceServicer): +class FraudService(fraud_detection_grpc.FraudDetectionServiceServicer): # Create an RPC function to say hello - def SayHello(self, request, context): - # Create a HelloResponse object - response = fraud_detection.HelloResponse() - # Set the greeting field of the response object - response.greeting = "Hello, " + request.name - # Print the greeting message - print(response.greeting) - # Return the response object - return response + def CheckFraud(self, request, context): + card_number = request.card_number + order_amount = request.order_amount + + print(f"Checking fraud for card number: {card_number} and order amount: {order_amount}") + # Dummy Logic if amount > 1000 or card_number starts with 999 then it is fraud + is_fraud = False + if order_amount > 1000 or card_number.startswith("999"): + is_fraud = True + + return fraud_detection.FraudResponse(is_fraud=is_fraud) def serve(): # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) # Add HelloService - fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) + fraud_detection_grpc.add_FraudDetectionServiceServicer_to_server(FraudService(), server) # Listen on port 50051 port = "50051" server.add_insecure_port("[::]:" + port) diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 9d8e24d4d..89bdf0a12 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,9 +1,8 @@ import sys import os +import logging +logging.basicConfig(level=logging.INFO) -# This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) sys.path.insert(0, fraud_detection_grpc_path) @@ -13,66 +12,74 @@ import grpc def greet(name='you'): - # Establish a connection with the fraud-detection gRPC service. with grpc.insecure_channel('fraud_detection:50051') as channel: - # Create a stub object. stub = fraud_detection_grpc.HelloServiceStub(channel) - # Call the service through the stub object. response = stub.SayHello(fraud_detection.HelloRequest(name=name)) return response.greeting -# Import Flask. -# Flask is a web framework for Python. -# It allows you to build a web application quickly. -# For more information, see https://flask.palletsprojects.com/en/latest/ +def call_fraud_detection(card_number, order_amount): + try: + with grpc.insecure_channel('fraud_detection:50051') as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + request_obj = fraud_detection.FraudRequest(card_number=card_number, order_amount=order_amount) + response = stub.CheckFraud(request_obj) + return response.is_fraud + except Exception as e: + logging.error(f"gRPC call failed: {e}") + return True + from flask import Flask, request from flask_cors import CORS -import json -# Create a simple Flask app. app = Flask(__name__) -# Enable CORS for the app. CORS(app, resources={r'/*': {'origins': '*'}}) -# Define a GET endpoint. @app.route('/', methods=['GET']) def index(): - """ - Responds with 'Hello, [name]' when a GET request is made to '/' endpoint. - """ - # Test the fraud-detection gRPC service. response = greet(name='orchestrator') - # Return the response. return response @app.route('/checkout', methods=['POST']) def checkout(): - """ - Responds with a JSON object containing the order ID, status, and suggested books. - """ - # Get request object data to json - request_data = json.loads(request.data) - # Print request object data - print("Request Data:", request_data.get('items')) + from concurrent.futures import ThreadPoolExecutor + try: + # force=True parses JSON regardless of Content-Type header + # silent=True returns None instead of error if parsing fails + request_data = request.get_json(force=True, silent=True) or {} + logging.info(f"Checkout request received: {request_data}") - # Dummy response following the provided YAML specification for the bookstore - order_status_response = { - 'orderId': '12345', - 'status': 'Order Approved', - 'suggestedBooks': [ - {'bookId': '123', 'title': 'The Best Book', 'author': 'Author 1'}, - {'bookId': '456', 'title': 'The Second Best Book', 'author': 'Author 2'} - ] - } + card_info = request_data.get("creditCard", {}) + card_number = card_info.get("number", "") + order_amount = request_data.get("totalAmount", 0.0) - return order_status_response + with ThreadPoolExecutor(max_workers=3) as executor: + fraud_future = executor.submit(call_fraud_detection, card_number, order_amount) + is_fraud = fraud_future.result() + logging.info(f"Fraud detection result: {is_fraud}") + if is_fraud: + order_status_response = { + "orderId": "1234567890", + "status": "Order Rejected", + "suggestedBooks": [] + } + else: + order_status_response = { + "orderId": "1234567890", + "status": "Order Approved", + "suggestedBooks": [ + {'bookId': '123', 'title': 'Book 1', 'author': 'Author 1'}, + {'bookId': '456', 'title': 'Book 2', 'author': 'Author 2'}, + {'bookId': '789', 'title': 'Book 3', 'author': 'Author 3'} + ] + } -if __name__ == '__main__': - # Run the app in debug mode to enable hot reloading. - # This is useful for development. - # The default port is 5000. - app.run(host='0.0.0.0') + logging.info(f"Checkout completed: {order_status_response}") + return order_status_response + except Exception as e: + logging.error(f"Checkout failed: {e}") + return {"error": {"message": str(e)}}, 500 -# Hello \ No newline at end of file +if __name__ == '__main__': + app.run(host='0.0.0.0') \ No newline at end of file From 702d31fa07fb894feeffdc1810176c248897f59f Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Sat, 28 Feb 2026 11:27:58 +0200 Subject: [PATCH 11/58] protobuf version Update --- fraud_detection/requirements.txt | 2 +- orchestrator/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fraud_detection/requirements.txt b/fraud_detection/requirements.txt index a80eedef7..b20ca4bc3 100644 --- a/fraud_detection/requirements.txt +++ b/fraud_detection/requirements.txt @@ -1,4 +1,4 @@ grpcio==1.60.0 grpcio-tools==1.60.0 -protobuf==4.25.2 +protobuf==4.25.0 watchdog==6.0.0 diff --git a/orchestrator/requirements.txt b/orchestrator/requirements.txt index 5ba8e254b..f8c1ab8cb 100644 --- a/orchestrator/requirements.txt +++ b/orchestrator/requirements.txt @@ -6,7 +6,7 @@ grpcio-tools==1.60.0 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.3 -protobuf==4.25.2 +protobuf>=4.25.0 Werkzeug==3.0.1 Flask-CORS==4.0.0 watchdog==6.0.0 \ No newline at end of file From 06c1f459f157ba435e3d55f3eb90d8f73131e50c Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Sat, 28 Feb 2026 12:12:19 +0200 Subject: [PATCH 12/58] Fix frontend checkout JSON request and response handling --- frontend/src/index.html | 54 ++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/frontend/src/index.html b/frontend/src/index.html index 15c47351f..2ace91b7d 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -22,26 +22,32 @@

Items

+
+
+
+
+
+
@@ -50,6 +56,7 @@

Items

+
+
+
- + + @@ -90,17 +102,18 @@

Items

event.preventDefault(); const formData = new FormData(event.target); + const data = { user: { name: formData.get('name'), contact: formData.get('contact'), + creditCard: { + number: formData.get('creditCard'), + expirationDate: formData.get('expirationDate'), + cvv: formData.get('cvv'), + }, + userComment: formData.get('userComment'), }, - creditCard: { - number: formData.get('creditCard'), - expirationDate: formData.get('expirationDate'), - cvv: formData.get('cvv'), - }, - userComment: formData.get('userComment'), items: items, billingAddress: { street: formData.get('billingStreet'), @@ -111,12 +124,15 @@

Items

}, shippingMethod: formData.get('shippingMethod'), giftWrapping: formData.get('giftWrapping') === 'on', - termsAccepted: formData.get('terms') === 'on', + termsAndConditionsAccepted: formData.get('terms') === 'on', }; try { const response = await fetch('http://localhost:8081/checkout', { method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, body: JSON.stringify(data), }); @@ -124,25 +140,29 @@

Items

const responseDiv = document.getElementById('response'); if (response.ok) { - const suggestedBooks = result.suggestedBooks.map(book => `
  • ${book.title} by ${book.author}
  • `).join(''); + const suggestedBooks = (result.suggestedBooks || []) + .map(book => `
  • ${book.title} by ${book.author}
  • `) + .join(''); + responseDiv.innerHTML = ` Order status: ${result.status}
    Order ID: ${result.orderId}
    - ${result.status === "Order Approved" ? - `Suggested Books: -
      ${suggestedBooks}
    ` - : `` + ${result.status === "Order Approved" + ? `Suggested Books: +
      ${suggestedBooks}
    ` + : `` } `; - color = result.status === 'Order Approved' ? 'green' : 'red'; + + const color = result.status === 'Order Approved' ? 'green' : 'red'; responseDiv.className = `mt-6 p-4 border rounded-lg bg-${color}-100 text-${color}-700`; } else { - responseDiv.textContent = `Error: ${result.error.message}`; + const message = result?.error?.message || 'Unknown error'; + responseDiv.textContent = `Error: ${message}`; responseDiv.className = "mt-6 p-4 border rounded-lg bg-red-100 text-red-700"; } responseDiv.style.display = 'block'; - // scroll to the bottom of the page window.scrollTo(0, document.body.scrollHeight); } catch (error) { const responseDiv = document.getElementById('response'); @@ -153,4 +173,4 @@

    Items

    }); - + \ No newline at end of file From ccba99c5bd72406f5b1a64027b1e53cfae70b004 Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Sat, 28 Feb 2026 12:14:32 +0200 Subject: [PATCH 13/58] Creating Proto file for Transaction Verification --- docker-compose.yaml | 25 ++++- transaction_verification/Dockerfile | 15 +++ transaction_verification/requirements.txt | 4 + transaction_verification/src/app.py | 88 +++++++++++++++++ .../transaction_verification.proto | 26 +++++ .../transaction_verification_pb2.py | 42 ++++++++ .../transaction_verification_pb2.pyi | 39 ++++++++ .../transaction_verification_pb2_grpc.py | 97 +++++++++++++++++++ 8 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 transaction_verification/Dockerfile create mode 100644 transaction_verification/requirements.txt create mode 100644 transaction_verification/src/app.py create mode 100644 utils/pb/transaction_verification/transaction_verification.proto create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2.py create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2.pyi create mode 100644 utils/pb/transaction_verification/transaction_verification_pb2_grpc.py diff --git a/docker-compose.yaml b/docker-compose.yaml index b4a60a537..dfa6d438b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -56,4 +56,27 @@ services: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils # Mount the fraud_detection/src directory in the current directory to the /app/fraud_detection/src directory in the container - - ./fraud_detection/src:/app/fraud_detection/src \ No newline at end of file + - ./fraud_detection/src:/app/fraud_detection/src + + transaction_verification: + build: + # Use the current directory as the build context + # This allows us to access the files in the current directory inside the Dockerfile + context: ./ + # Use the Dockerfile in the transaction_verification directory + dockerfile: ./transaction_verification/Dockerfile + ports: + # Expose port 50052 on the host, and map port 50052 of the container to port 50052 on the host + - 50052:50052 + environment: + # Pass the environment variables to the container + # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console + - PYTHONUNBUFFERED=TRUE + # The PYTHONFILE environment variable specifies the absolute entry point of the application + # Check app.py in the transaction_verification directory to see how this is used + - PYTHONFILE=/app/transaction_verification/src/app.py + volumes: + # Mount the utils directory in the current directory to the /app/utils directory in the container + - ./utils:/app/utils + # Mount the transaction_verification/src directory in the current directory to the /app/transaction_verification/src directory in the container + - ./transaction_verification/src:/app/transaction_verification/src diff --git a/transaction_verification/Dockerfile b/transaction_verification/Dockerfile new file mode 100644 index 000000000..341df7f6f --- /dev/null +++ b/transaction_verification/Dockerfile @@ -0,0 +1,15 @@ +# Use an official Python runtime as the base image +FROM python:3.11 + +# Set the working directory in the container +# Both the utils and src folders will be mounted as volumes, please see docker-compose.yaml +WORKDIR /app + +# Copy the requirements file to the working directory +COPY ./fraud_detection/requirements.txt . + +# Install the Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Set the command to run the application +CMD python utils/other/hotreload.py "fraud_detection/src/app.py" \ No newline at end of file diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt new file mode 100644 index 000000000..2c6bbca24 --- /dev/null +++ b/transaction_verification/requirements.txt @@ -0,0 +1,4 @@ +grpcio==1.60.0 +grpcio-tools==1.60.0 +protobuf>=4.25.0 +watchdog==6.0.0 diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py new file mode 100644 index 000000000..b3c8eda1d --- /dev/null +++ b/transaction_verification/src/app.py @@ -0,0 +1,88 @@ +import sys +import os + +# This set of lines are needed to import the gRPC stubs. +# The path of the stubs is relative to the current file, or absolute inside the container. +# Change these lines only if strictly needed. +FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) +sys.path.insert(0, fraud_detection_grpc_path) +import transaction_verification_pb2 as transaction_verification +import transaction_verification_pb2_grpc as transaction_verification_grpc + +import grpc +from concurrent import futures + +class TransactionVerificationService(transaction_verification_grpc.TransactionVerificationServiceServicer): + def VerifyTransaction(self, request, context): + print(f"Verifying transaction for card number: {request.card_number}") + + #Check 1: items list must not be empty + if len(request.items) == 0: + return transaction_verification.TransactionResponse( + is_valid=False, + reason="Order must contain at least one item" + ) + #Check 2: user data must be filled in + if not request.user_name or not request.user_contact: + return transaction_verification.TransactionResponse( + is_valid=False, + reason="User name and contact are required" + ) + + #Check 3: card number must be 16 digits + card_number = request.card_number.replace(" ", "") + if not card_number.isdigit() or len(card_number) != 16: + return transaction_verification.TransactionResponse( + is_valid=False, + reason="Invalid card number format" + ) + + #Check 4: CVV must be 3 digits + if not request.cvv.isdigit() or len(request.cvv) != 3: + return transaction_verification.TransactionResponse( + is_valid=False, + reason="Invalid CVV format" + ) + + # Check 5: expiration date format MM/YY + expiration = request.card_expiration + if len(expiration) != 5 or expiration[2] != '/' or \ + not expiration[:2].isdigit() or not expiration[3:].isdigit(): + return transaction_verification.TransactionResponse( + is_valid=False, + reason="Invalid expiration date format. Use MM/YY" + ) + + # Check 6: expiration date must be in the future + month = int(expiration[:2]) + year = int(expiration[3:]) + + current_year = datetime.now().year % 100 + current_month = datetime.now().month + + if year < current_year or (year == current_year and month < current_month): + return transaction_verification.TransactionResponse( + is_valid=False, + reason="Card has expired" + ) + + print("Transaction verified successfully") + return transaction_verification.TransactionResponse( + is_valid=True, + reason="Transaction is valid" + ) + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor()) + transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( + TransactionVerificationService(), server + ) + port = "50052" + server.add_insecure_port("[::]:" + port) + server.start() + print("Server started. Listening on port 50052.") + server.wait_for_termination() + +if __name__ == '__main__': + serve() \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification.proto b/utils/pb/transaction_verification/transaction_verification.proto new file mode 100644 index 000000000..bfe033a8e --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package transaction_verification; + +service TransactionVerificationService { + rpc VerifyTransaction (TransactionRequest) returns (TransactionResponse); +} + +message TransactionRequest { + string card_number = 1; + string card_expiration = 2; + string card_cvv = 3; + repeated Item items = 4; + string user_name = 5; + string user_contact = 6; +} + +message Item { + string name = 1; + int32 quantity = 2; +} + +message TransactionResponse { + bool is_valid = 1; + string reason = 2; +} \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py new file mode 100644 index 000000000..9441e2d5b --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: transaction_verification.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'transaction_verification.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xac\x01\n\x12TransactionRequest\x12\x13\n\x0b\x63\x61rd_number\x18\x01 \x01(\t\x12\x17\n\x0f\x63\x61rd_expiration\x18\x02 \x01(\t\x12\x10\n\x08\x63\x61rd_cvv\x18\x03 \x01(\t\x12-\n\x05items\x18\x04 \x03(\x0b\x32\x1e.transaction_verification.Item\x12\x11\n\tuser_name\x18\x05 \x01(\t\x12\x14\n\x0cuser_contact\x18\x06 \x01(\t\"&\n\x04Item\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"7\n\x13TransactionResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t2\x92\x01\n\x1eTransactionVerificationService\x12p\n\x11VerifyTransaction\x12,.transaction_verification.TransactionRequest\x1a-.transaction_verification.TransactionResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_TRANSACTIONREQUEST']._serialized_start=61 + _globals['_TRANSACTIONREQUEST']._serialized_end=233 + _globals['_ITEM']._serialized_start=235 + _globals['_ITEM']._serialized_end=273 + _globals['_TRANSACTIONRESPONSE']._serialized_start=275 + _globals['_TRANSACTIONRESPONSE']._serialized_end=330 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=333 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=479 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.pyi b/utils/pb/transaction_verification/transaction_verification_pb2.pyi new file mode 100644 index 000000000..03f9d43be --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2.pyi @@ -0,0 +1,39 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class TransactionRequest(_message.Message): + __slots__ = ("card_number", "card_expiration", "card_cvv", "items", "user_name", "user_contact") + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + CARD_EXPIRATION_FIELD_NUMBER: _ClassVar[int] + CARD_CVV_FIELD_NUMBER: _ClassVar[int] + ITEMS_FIELD_NUMBER: _ClassVar[int] + USER_NAME_FIELD_NUMBER: _ClassVar[int] + USER_CONTACT_FIELD_NUMBER: _ClassVar[int] + card_number: str + card_expiration: str + card_cvv: str + items: _containers.RepeatedCompositeFieldContainer[Item] + user_name: str + user_contact: str + def __init__(self, card_number: _Optional[str] = ..., card_expiration: _Optional[str] = ..., card_cvv: _Optional[str] = ..., items: _Optional[_Iterable[_Union[Item, _Mapping]]] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ...) -> None: ... + +class Item(_message.Message): + __slots__ = ("name", "quantity") + NAME_FIELD_NUMBER: _ClassVar[int] + QUANTITY_FIELD_NUMBER: _ClassVar[int] + name: str + quantity: int + def __init__(self, name: _Optional[str] = ..., quantity: _Optional[int] = ...) -> None: ... + +class TransactionResponse(_message.Message): + __slots__ = ("is_valid", "reason") + IS_VALID_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + is_valid: bool + reason: str + def __init__(self, is_valid: bool = ..., reason: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py new file mode 100644 index 000000000..1219fd0a7 --- /dev/null +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -0,0 +1,97 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import transaction_verification_pb2 as transaction__verification__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in transaction_verification_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class TransactionVerificationServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.VerifyTransaction = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/VerifyTransaction', + request_serializer=transaction__verification__pb2.TransactionRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.TransactionResponse.FromString, + _registered_method=True) + + +class TransactionVerificationServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def VerifyTransaction(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_TransactionVerificationServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'VerifyTransaction': grpc.unary_unary_rpc_method_handler( + servicer.VerifyTransaction, + request_deserializer=transaction__verification__pb2.TransactionRequest.FromString, + response_serializer=transaction__verification__pb2.TransactionResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'transaction_verification.TransactionVerificationService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('transaction_verification.TransactionVerificationService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class TransactionVerificationService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def VerifyTransaction(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/VerifyTransaction', + transaction__verification__pb2.TransactionRequest.SerializeToString, + transaction__verification__pb2.TransactionResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) From ca7831f5cfa4eb4257565de5510a931f9d1066a0 Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Mon, 2 Mar 2026 10:19:41 +0200 Subject: [PATCH 14/58] transaction_verification --- orchestrator/src/app.py | 84 ++++++++++++++----- test_path.py | 8 ++ transaction_verification/Dockerfile | 4 +- transaction_verification/src/app.py | 55 ++++++------ .../pb/fraud_detection/fraud_detection.proto | 15 ++-- .../pb/fraud_detection/fraud_detection_pb2.py | 14 ++-- .../fraud_detection/fraud_detection_pb2.pyi | 22 ++--- .../fraud_detection_pb2_grpc.py | 36 ++++---- .../transaction_verification_pb2.py | 16 +--- .../transaction_verification_pb2_grpc.py | 39 +-------- 10 files changed, 152 insertions(+), 141 deletions(-) create mode 100644 test_path.py diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 89bdf0a12..bb6e43330 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,15 +1,23 @@ import sys import os -import logging -logging.basicConfig(level=logging.INFO) FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") + +# Import fraud detection stubs fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) sys.path.insert(0, fraud_detection_grpc_path) import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc +# Import transaction verification stubs +transaction_verification_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) +sys.path.insert(0, transaction_verification_grpc_path) +import transaction_verification_pb2 as transaction_verification +import transaction_verification_pb2_grpc as transaction_verification_grpc + import grpc +from flask import Flask, request +from flask_cors import CORS def greet(name='you'): with grpc.insecure_channel('fraud_detection:50051') as channel: @@ -21,15 +29,45 @@ def call_fraud_detection(card_number, order_amount): try: with grpc.insecure_channel('fraud_detection:50051') as channel: stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) - request_obj = fraud_detection.FraudRequest(card_number=card_number, order_amount=order_amount) + request_obj = fraud_detection.FraudRequest( + card_number=card_number, + order_amount=order_amount + ) response = stub.CheckFraud(request_obj) return response.is_fraud except Exception as e: - logging.error(f"gRPC call failed: {e}") + print(f"Fraud detection gRPC call failed: {e}") return True -from flask import Flask, request -from flask_cors import CORS +def call_transaction_verification(request_data): + try: + with grpc.insecure_channel('transaction_verification:50052') as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + + card_info = request_data.get("creditCard", {}) + user_info = request_data.get("user", {}) + items = request_data.get("items", []) + + grpc_items = [ + transaction_verification.Item( + name=item.get("name", ""), + quantity=item.get("quantity", 0) + ) for item in items + ] + + request_obj = transaction_verification.TransactionRequest( + card_number=card_info.get("number", ""), + card_expiration=card_info.get("expirationDate", ""), + card_cvv=card_info.get("cvv", ""), + items=grpc_items, + user_name=user_info.get("name", ""), + user_contact=user_info.get("contact", "") + ) + response = stub.VerifyTransaction(request_obj) + return response.is_valid + except Exception as e: + print(f"Transaction verification gRPC call failed: {e}") + return False app = Flask(__name__) CORS(app, resources={r'/*': {'origins': '*'}}) @@ -43,42 +81,44 @@ def index(): def checkout(): from concurrent.futures import ThreadPoolExecutor try: - # force=True parses JSON regardless of Content-Type header - # silent=True returns None instead of error if parsing fails request_data = request.get_json(force=True, silent=True) or {} - logging.info(f"Checkout request received: {request_data}") + print("Request Data:", request_data.get('items')) card_info = request_data.get("creditCard", {}) card_number = card_info.get("number", "") order_amount = request_data.get("totalAmount", 0.0) + # Run fraud detection and transaction verification in parallel with ThreadPoolExecutor(max_workers=3) as executor: fraud_future = executor.submit(call_fraud_detection, card_number, order_amount) + verify_future = executor.submit(call_transaction_verification, request_data) + is_fraud = fraud_future.result() - logging.info(f"Fraud detection result: {is_fraud}") + is_valid = verify_future.result() + + print(f"Fraud: {is_fraud}, Valid: {is_valid}") - if is_fraud: + # Consolidate results + if is_fraud or not is_valid: order_status_response = { - "orderId": "1234567890", - "status": "Order Rejected", - "suggestedBooks": [] + 'orderId': '12345', + 'status': 'Order Rejected', + 'suggestedBooks': [] } else: order_status_response = { - "orderId": "1234567890", - "status": "Order Approved", - "suggestedBooks": [ - {'bookId': '123', 'title': 'Book 1', 'author': 'Author 1'}, - {'bookId': '456', 'title': 'Book 2', 'author': 'Author 2'}, - {'bookId': '789', 'title': 'Book 3', 'author': 'Author 3'} + 'orderId': '12345', + 'status': 'Order Approved', + 'suggestedBooks': [ + {'bookId': '123', 'title': 'The Best Book', 'author': 'Author 1'}, + {'bookId': '456', 'title': 'The Second Best Book', 'author': 'Author 2'} ] } - logging.info(f"Checkout completed: {order_status_response}") return order_status_response except Exception as e: - logging.error(f"Checkout failed: {e}") + print(f"Checkout failed: {e}") return {"error": {"message": str(e)}}, 500 if __name__ == '__main__': diff --git a/test_path.py b/test_path.py new file mode 100644 index 000000000..6154cafa5 --- /dev/null +++ b/test_path.py @@ -0,0 +1,8 @@ +import os +import sys + +FILE = __file__ +print(f"FILE: {FILE}") +path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) +print(f"Path: {path}") +print(f"Directory exists: {os.path.exists(path)}") diff --git a/transaction_verification/Dockerfile b/transaction_verification/Dockerfile index 341df7f6f..2adbd4e7d 100644 --- a/transaction_verification/Dockerfile +++ b/transaction_verification/Dockerfile @@ -6,10 +6,10 @@ FROM python:3.11 WORKDIR /app # Copy the requirements file to the working directory -COPY ./fraud_detection/requirements.txt . +COPY ./transaction_verification/requirements.txt . # Install the Python dependencies RUN pip install --no-cache-dir -r requirements.txt # Set the command to run the application -CMD python utils/other/hotreload.py "fraud_detection/src/app.py" \ No newline at end of file +CMD python utils/other/hotreload.py "transaction_verification/src/app.py" \ No newline at end of file diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index b3c8eda1d..ad0d9158b 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -5,68 +5,64 @@ # The path of the stubs is relative to the current file, or absolute inside the container. # Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) -sys.path.insert(0, fraud_detection_grpc_path) +transaction_verification_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) +sys.path.insert(0, transaction_verification_grpc_path) import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc import grpc from concurrent import futures +# Create a class to define the server functions, derived from +# transaction_verification_pb2_grpc.TransactionVerificationServiceServicer class TransactionVerificationService(transaction_verification_grpc.TransactionVerificationServiceServicer): + # Create an RPC function to verify transaction def VerifyTransaction(self, request, context): - print(f"Verifying transaction for card number: {request.card_number}") + print(f"Verifying transaction for card: {request.card_number}") - #Check 1: items list must not be empty + # Check 1: items list must not be empty if len(request.items) == 0: + print("Verification failed: No items in order") return transaction_verification.TransactionResponse( is_valid=False, reason="Order must contain at least one item" ) - #Check 2: user data must be filled in + + # Check 2: user data must be filled in if not request.user_name or not request.user_contact: + print("Verification failed: Missing user data") return transaction_verification.TransactionResponse( is_valid=False, reason="User name and contact are required" ) - #Check 3: card number must be 16 digits + # Check 3: card number must be 16 digits card_number = request.card_number.replace(" ", "") if not card_number.isdigit() or len(card_number) != 16: + print("Verification failed: Invalid card number") return transaction_verification.TransactionResponse( is_valid=False, - reason="Invalid card number format" + reason="Invalid card number format, must be 16 digits" ) - #Check 4: CVV must be 3 digits - if not request.cvv.isdigit() or len(request.cvv) != 3: + # Check 4: CVV must be 3 digits + if not request.card_cvv.isdigit() or len(request.card_cvv) != 3: + print("Verification failed: Invalid CVV") return transaction_verification.TransactionResponse( is_valid=False, - reason="Invalid CVV format" + reason="Invalid CVV, must be 3 digits" ) # Check 5: expiration date format MM/YY expiration = request.card_expiration if len(expiration) != 5 or expiration[2] != '/' or \ not expiration[:2].isdigit() or not expiration[3:].isdigit(): + print("Verification failed: Invalid expiration date") return transaction_verification.TransactionResponse( is_valid=False, - reason="Invalid expiration date format. Use MM/YY" - ) - - # Check 6: expiration date must be in the future - month = int(expiration[:2]) - year = int(expiration[3:]) - - current_year = datetime.now().year % 100 - current_month = datetime.now().month - - if year < current_year or (year == current_year and month < current_month): - return transaction_verification.TransactionResponse( - is_valid=False, - reason="Card has expired" + reason="Invalid expiration date, use MM/YY format" ) - + print("Transaction verified successfully") return transaction_verification.TransactionResponse( is_valid=True, @@ -74,15 +70,20 @@ def VerifyTransaction(self, request, context): ) def serve(): + # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) + # Add TransactionVerificationService transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( TransactionVerificationService(), server ) + # Listen on port 50052 port = "50052" server.add_insecure_port("[::]:" + port) + # Start the server server.start() - print("Server started. Listening on port 50052.") + print("Transaction verification server started. Listening on port 50052.") + # Keep thread alive server.wait_for_termination() - + if __name__ == '__main__': serve() \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index db20211d7..54bdcb878 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -1,15 +1,16 @@ syntax = "proto3"; -package hello; +package fraud_detection; -service HelloService { - rpc SayHello (HelloRequest) returns (HelloResponse); +service FraudDetectionService { + rpc CheckFraud (FraudRequest) returns (FraudResponse); } -message HelloRequest { - string name = 1; +message FraudRequest { + string card_number = 1; + float order_amount = 2; } -message HelloResponse { - string greeting = 1; +message FraudResponse { + bool is_fraud = 1; } diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index cdd0bcae8..4bb850ad5 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -14,17 +14,17 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t2E\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x0f\x66raud_detection\"9\n\x0c\x46raudRequest\x12\x13\n\x0b\x63\x61rd_number\x18\x01 \x01(\t\x12\x14\n\x0corder_amount\x18\x02 \x01(\x02\"!\n\rFraudResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x32\x64\n\x15\x46raudDetectionService\x12K\n\nCheckFraud\x12\x1d.fraud_detection.FraudRequest\x1a\x1e.fraud_detection.FraudResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'fraud_detection_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _globals['_HELLOREQUEST']._serialized_start=32 - _globals['_HELLOREQUEST']._serialized_end=60 - _globals['_HELLORESPONSE']._serialized_start=62 - _globals['_HELLORESPONSE']._serialized_end=95 - _globals['_HELLOSERVICE']._serialized_start=97 - _globals['_HELLOSERVICE']._serialized_end=166 + _globals['_FRAUDREQUEST']._serialized_start=42 + _globals['_FRAUDREQUEST']._serialized_end=99 + _globals['_FRAUDRESPONSE']._serialized_start=101 + _globals['_FRAUDRESPONSE']._serialized_end=134 + _globals['_FRAUDDETECTIONSERVICE']._serialized_start=136 + _globals['_FRAUDDETECTIONSERVICE']._serialized_end=236 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index 30a263856..08a4f9a0f 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -4,14 +4,16 @@ from typing import ClassVar as _ClassVar, Optional as _Optional DESCRIPTOR: _descriptor.FileDescriptor -class HelloRequest(_message.Message): - __slots__ = ("name",) - NAME_FIELD_NUMBER: _ClassVar[int] - name: str - def __init__(self, name: _Optional[str] = ...) -> None: ... +class FraudRequest(_message.Message): + __slots__ = ("card_number", "order_amount") + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + ORDER_AMOUNT_FIELD_NUMBER: _ClassVar[int] + card_number: str + order_amount: float + def __init__(self, card_number: _Optional[str] = ..., order_amount: _Optional[float] = ...) -> None: ... -class HelloResponse(_message.Message): - __slots__ = ("greeting",) - GREETING_FIELD_NUMBER: _ClassVar[int] - greeting: str - def __init__(self, greeting: _Optional[str] = ...) -> None: ... +class FraudResponse(_message.Message): + __slots__ = ("is_fraud",) + IS_FRAUD_FIELD_NUMBER: _ClassVar[int] + is_fraud: bool + def __init__(self, is_fraud: bool = ...) -> None: ... diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 4e7a27975..6824e421a 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -5,7 +5,7 @@ import fraud_detection_pb2 as fraud__detection__pb2 -class HelloServiceStub(object): +class FraudDetectionServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,42 +14,42 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.SayHello = channel.unary_unary( - '/hello.HelloService/SayHello', - request_serializer=fraud__detection__pb2.HelloRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.HelloResponse.FromString, + self.CheckFraud = channel.unary_unary( + '/fraud_detection.FraudDetectionService/CheckFraud', + request_serializer=fraud__detection__pb2.FraudRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.FraudResponse.FromString, ) -class HelloServiceServicer(object): +class FraudDetectionServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def SayHello(self, request, context): + def CheckFraud(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') -def add_HelloServiceServicer_to_server(servicer, server): +def add_FraudDetectionServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'SayHello': grpc.unary_unary_rpc_method_handler( - servicer.SayHello, - request_deserializer=fraud__detection__pb2.HelloRequest.FromString, - response_serializer=fraud__detection__pb2.HelloResponse.SerializeToString, + 'CheckFraud': grpc.unary_unary_rpc_method_handler( + servicer.CheckFraud, + request_deserializer=fraud__detection__pb2.FraudRequest.FromString, + response_serializer=fraud__detection__pb2.FraudResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'hello.HelloService', rpc_method_handlers) + 'fraud_detection.FraudDetectionService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class HelloService(object): +class FraudDetectionService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def SayHello(request, + def CheckFraud(request, target, options=(), channel_credentials=None, @@ -59,8 +59,8 @@ def SayHello(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/hello.HelloService/SayHello', - fraud__detection__pb2.HelloRequest.SerializeToString, - fraud__detection__pb2.HelloResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/fraud_detection.FraudDetectionService/CheckFraud', + fraud__detection__pb2.FraudRequest.SerializeToString, + fraud__detection__pb2.FraudResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py index 9441e2d5b..db0e5922d 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -1,22 +1,12 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE # source: transaction_verification.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'transaction_verification.proto' -) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -29,8 +19,8 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None _globals['_TRANSACTIONREQUEST']._serialized_start=61 _globals['_TRANSACTIONREQUEST']._serialized_end=233 _globals['_ITEM']._serialized_start=235 diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py index 1219fd0a7..95871b9bf 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -1,29 +1,9 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc -import warnings import transaction_verification_pb2 as transaction__verification__pb2 -GRPC_GENERATED_VERSION = '1.78.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in transaction_verification_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) - class TransactionVerificationServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -38,7 +18,7 @@ def __init__(self, channel): '/transaction_verification.TransactionVerificationService/VerifyTransaction', request_serializer=transaction__verification__pb2.TransactionRequest.SerializeToString, response_deserializer=transaction__verification__pb2.TransactionResponse.FromString, - _registered_method=True) + ) class TransactionVerificationServiceServicer(object): @@ -62,7 +42,6 @@ def add_TransactionVerificationServiceServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'transaction_verification.TransactionVerificationService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('transaction_verification.TransactionVerificationService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -80,18 +59,8 @@ def VerifyTransaction(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/transaction_verification.TransactionVerificationService/VerifyTransaction', + return grpc.experimental.unary_unary(request, target, '/transaction_verification.TransactionVerificationService/VerifyTransaction', transaction__verification__pb2.TransactionRequest.SerializeToString, transaction__verification__pb2.TransactionResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From ffbbf84856edef245e050b1f78534b5b6edd62ec Mon Sep 17 00:00:00 2001 From: mdmuminul Date: Mon, 2 Mar 2026 10:52:18 +0200 Subject: [PATCH 15/58] Suggestions --- docker-compose.yaml | 21 ++++++ orchestrator/src/app.py | 46 ++++++++++-- suggestions/Dockerfile | 15 ++++ suggestions/requirements.txt | 4 ++ suggestions/src/app.py | 76 ++++++++++++++++++++ utils/pb/suggestions/suggestions.proto | 27 +++++++ utils/pb/suggestions/suggestions_pb2.py | 34 +++++++++ utils/pb/suggestions/suggestions_pb2_grpc.py | 66 +++++++++++++++++ 8 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 suggestions/Dockerfile create mode 100644 suggestions/requirements.txt create mode 100644 suggestions/src/app.py create mode 100644 utils/pb/suggestions/suggestions.proto create mode 100644 utils/pb/suggestions/suggestions_pb2.py create mode 100644 utils/pb/suggestions/suggestions_pb2_grpc.py diff --git a/docker-compose.yaml b/docker-compose.yaml index dfa6d438b..9729cb909 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -80,3 +80,24 @@ services: - ./utils:/app/utils # Mount the transaction_verification/src directory in the current directory to the /app/transaction_verification/src directory in the container - ./transaction_verification/src:/app/transaction_verification/src + + suggestions: + build: + # Use the current directory as the build context + # This allows us to access the files in the current directory inside the Dockerfile + context: ./ + # Use the Dockerfile in the suggestions directory + dockerfile: ./suggestions/Dockerfile + ports: + # Expose port 50053 on the host, and map port 50053 of the container to port 50053 on the host + - 50053:50053 + environment: + # Pass the environment variables to the container + # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/suggestions/src/app.py + volumes: + # Mount the utils directory in the current directory to the /app/utils directory in the container + - ./utils:/app/utils + # Mount the suggestions/src directory in the current directory to the /app/suggestions/src directory in the container + - ./suggestions/src:/app/suggestions/src diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index bb6e43330..b962da36a 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -15,6 +15,12 @@ import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc +# Import suggestions stubs +suggestions_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/suggestions')) +sys.path.insert(0, suggestions_grpc_path) +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc + import grpc from flask import Flask, request from flask_cors import CORS @@ -69,6 +75,35 @@ def call_transaction_verification(request_data): print(f"Transaction verification gRPC call failed: {e}") return False +def call_suggestions(request_data): + try: + with grpc.insecure_channel('suggestions:50053') as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + + user_info = request_data.get("user", {}) + items = request_data.get("items", []) + + grpc_items = [ + suggestions.Item( + name=item.get("name", ""), + quantity=item.get("quantity", 0) + ) for item in items + ] + + request_obj = suggestions.SuggestionsRequest( + items=grpc_items, + user_name=user_info.get("name", "") + ) + response = stub.GetSuggestions(request_obj) + + return [ + {"bookId": book.book_id, "title": book.title, "author": book.author} + for book in response.books + ] + except Exception as e: + print(f"Suggestions gRPC call failed: {e}") + return [] + app = Flask(__name__) CORS(app, resources={r'/*': {'origins': '*'}}) @@ -88,15 +123,17 @@ def checkout(): card_number = card_info.get("number", "") order_amount = request_data.get("totalAmount", 0.0) - # Run fraud detection and transaction verification in parallel + # Run all 3 services in parallel with ThreadPoolExecutor(max_workers=3) as executor: fraud_future = executor.submit(call_fraud_detection, card_number, order_amount) verify_future = executor.submit(call_transaction_verification, request_data) + suggestions_future = executor.submit(call_suggestions, request_data) is_fraud = fraud_future.result() is_valid = verify_future.result() + suggested_books = suggestions_future.result() - print(f"Fraud: {is_fraud}, Valid: {is_valid}") + print(f"Fraud: {is_fraud}, Valid: {is_valid}, Books: {len(suggested_books)}") # Consolidate results if is_fraud or not is_valid: @@ -109,10 +146,7 @@ def checkout(): order_status_response = { 'orderId': '12345', 'status': 'Order Approved', - 'suggestedBooks': [ - {'bookId': '123', 'title': 'The Best Book', 'author': 'Author 1'}, - {'bookId': '456', 'title': 'The Second Best Book', 'author': 'Author 2'} - ] + 'suggestedBooks': suggested_books } return order_status_response diff --git a/suggestions/Dockerfile b/suggestions/Dockerfile new file mode 100644 index 000000000..c618243eb --- /dev/null +++ b/suggestions/Dockerfile @@ -0,0 +1,15 @@ +# Use an official Python runtime as the base image +FROM python:3.11 + +# Set the working directory in the container +# Both the utils and src folders will be mounted as volumes, please see docker-compose.yaml +WORKDIR /app + +# Copy the requirements file to the working directory +COPY ./suggestions/requirements.txt . + +# Install the Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Set the command to run the application +CMD python utils/other/hotreload.py "suggestions/src/app.py" \ No newline at end of file diff --git a/suggestions/requirements.txt b/suggestions/requirements.txt new file mode 100644 index 000000000..2c6bbca24 --- /dev/null +++ b/suggestions/requirements.txt @@ -0,0 +1,4 @@ +grpcio==1.60.0 +grpcio-tools==1.60.0 +protobuf>=4.25.0 +watchdog==6.0.0 diff --git a/suggestions/src/app.py b/suggestions/src/app.py new file mode 100644 index 000000000..b0d6f43fd --- /dev/null +++ b/suggestions/src/app.py @@ -0,0 +1,76 @@ +import sys +import os + +# This set of lines are needed to import the gRPC stubs. +# The path of the stubs is relative to the current file, or absolute inside the container. +# Change these lines only if strictly needed. +FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +suggestions_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/suggestions')) +sys.path.insert(0, suggestions_grpc_path) +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc + +import grpc +from concurrent import futures + +# Static books list +BOOKS = [ + {"book_id": "1", "title": "The Pragmatic Programmer", "author": "David Thomas"}, + {"book_id": "2", "title": "Clean Code", "author": "Robert C. Martin"}, + {"book_id": "3", "title": "Design Patterns", "author": "Gang of Four"}, + {"book_id": "4", "title": "The Hobbit", "author": "J.R.R. Tolkien"}, + {"book_id": "5", "title": "Harry Potter", "author": "J.K. Rowling"}, + {"book_id": "6", "title": "The Great Gatsby", "author": "F. Scott Fitzgerald"}, + {"book_id": "7", "title": "1984", "author": "George Orwell"}, + {"book_id": "8", "title": "To Kill a Mockingbird", "author": "Harper Lee"}, + {"book_id": "9", "title": "The Alchemist", "author": "Paulo Coelho"}, + {"book_id": "10", "title": "Dune", "author": "Frank Herbert"}, +] + +# Create a class to define the server functions +class SuggestionsService(suggestions_grpc.SuggestionsServiceServicer): + + def GetSuggestions(self, request, context): + print(f"Getting suggestions for user: {request.user_name}") + print(f"Items ordered: {[item.name for item in request.items]}") + + # Simple logic: exclude books that match ordered items + # and return first 3 suggestions + ordered_names = [item.name.lower() for item in request.items] + + suggested = [ + book for book in BOOKS + if book["title"].lower() not in ordered_names + ][:3] + + # Build response + response_books = [ + suggestions.Book( + book_id=book["book_id"], + title=book["title"], + author=book["author"] + ) + for book in suggested + ] + + print(f"Suggesting {len(response_books)} books") + return suggestions.SuggestionsResponse(books=response_books) + +def serve(): + # Create a gRPC server + server = grpc.server(futures.ThreadPoolExecutor()) + # Add SuggestionsService + suggestions_grpc.add_SuggestionsServiceServicer_to_server( + SuggestionsService(), server + ) + # Listen on port 50053 + port = "50053" + server.add_insecure_port("[::]:" + port) + # Start the server + server.start() + print("Suggestions server started. Listening on port 50053.") + # Keep thread alive + server.wait_for_termination() + +if __name__ == '__main__': + serve() \ No newline at end of file diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto new file mode 100644 index 000000000..8757abf1c --- /dev/null +++ b/utils/pb/suggestions/suggestions.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package suggestions; + +service SuggestionsService { + rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); +} + +message SuggestionsRequest { + repeated Item items = 1; + string user_name = 2; +} + +message Item { + string name = 1; + int32 quantity = 2; +} + +message SuggestionsResponse { + repeated Book books = 1; +} + +message Book { + string book_id = 1; + string title = 2; + string author = 3; +} diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py new file mode 100644 index 000000000..733f559fc --- /dev/null +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: suggestions.proto +# Protobuf Python Version: 4.25.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\"I\n\x12SuggestionsRequest\x12 \n\x05items\x18\x01 \x03(\x0b\x32\x11.suggestions.Item\x12\x11\n\tuser_name\x18\x02 \x01(\t\"&\n\x04Item\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"7\n\x13SuggestionsResponse\x12 \n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x11.suggestions.Book\"6\n\x04\x42ook\x12\x0f\n\x07\x62ook_id\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'suggestions_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_SUGGESTIONSREQUEST']._serialized_start=34 + _globals['_SUGGESTIONSREQUEST']._serialized_end=107 + _globals['_ITEM']._serialized_start=109 + _globals['_ITEM']._serialized_end=147 + _globals['_SUGGESTIONSRESPONSE']._serialized_start=149 + _globals['_SUGGESTIONSRESPONSE']._serialized_end=204 + _globals['_BOOK']._serialized_start=206 + _globals['_BOOK']._serialized_end=260 + _globals['_SUGGESTIONSSERVICE']._serialized_start=262 + _globals['_SUGGESTIONSSERVICE']._serialized_end=367 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/suggestions/suggestions_pb2_grpc.py b/utils/pb/suggestions/suggestions_pb2_grpc.py new file mode 100644 index 000000000..ca1154a0b --- /dev/null +++ b/utils/pb/suggestions/suggestions_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import suggestions_pb2 as suggestions__pb2 + + +class SuggestionsServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetSuggestions = channel.unary_unary( + '/suggestions.SuggestionsService/GetSuggestions', + request_serializer=suggestions__pb2.SuggestionsRequest.SerializeToString, + response_deserializer=suggestions__pb2.SuggestionsResponse.FromString, + ) + + +class SuggestionsServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def GetSuggestions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_SuggestionsServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetSuggestions': grpc.unary_unary_rpc_method_handler( + servicer.GetSuggestions, + request_deserializer=suggestions__pb2.SuggestionsRequest.FromString, + response_serializer=suggestions__pb2.SuggestionsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'suggestions.SuggestionsService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class SuggestionsService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def GetSuggestions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/suggestions.SuggestionsService/GetSuggestions', + suggestions__pb2.SuggestionsRequest.SerializeToString, + suggestions__pb2.SuggestionsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From 2f82c3fa0e79d3d66a08a31d0b0064155a82f9f6 Mon Sep 17 00:00:00 2001 From: anupkumar Date: Mon, 2 Mar 2026 12:53:22 +0200 Subject: [PATCH 16/58] Revert "Merge branch 'Mumin' into anup_dev" This reverts commit 6e6d4fafea1c121dd48a64a53c08757752b6bbf6, reversing changes made to 06c1f459f157ba435e3d55f3eb90d8f73131e50c. --- docker-compose.yaml | 27 +- fraud_detection/requirements.txt | 2 +- fraud_detection/src/app.py | 63 ++-- orchestrator/requirements.txt | 2 +- orchestrator/src/app.py | 330 ++++++++++++------ suggestions/Dockerfile | 7 - suggestions/requirements.txt | 4 +- suggestions/src/app.py | 78 ++--- test_path.py | 8 - transaction_verification/Dockerfile | 7 - transaction_verification/requirements.txt | 4 +- transaction_verification/src/app.py | 98 ++---- .../pb/fraud_detection/fraud_detection.proto | 27 +- .../pb/fraud_detection/fraud_detection_pb2.py | 18 +- .../fraud_detection/fraud_detection_pb2.pyi | 32 +- .../fraud_detection_pb2_grpc.py | 36 +- utils/pb/suggestions/suggestions.proto | 23 +- utils/pb/suggestions/suggestions_pb2.py | 18 +- .../transaction_verification.proto | 28 +- .../transaction_verification_pb2.py | 16 +- .../transaction_verification_pb2.pyi | 46 +-- .../transaction_verification_pb2_grpc.py | 12 +- 22 files changed, 477 insertions(+), 409 deletions(-) delete mode 100644 test_path.py diff --git a/docker-compose.yaml b/docker-compose.yaml index 9729cb909..e4818a9c8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,6 +13,7 @@ services: volumes: # Mount the frontend directory - ./frontend/src:/usr/share/nginx/html + orchestrator: build: # Use the current directory as the build context @@ -35,12 +36,13 @@ services: - ./utils:/app/utils # Mount the orchestrator/src directory in the current directory to the /app/orchestrator/src directory in the container - ./orchestrator/src:/app/orchestrator/src + fraud_detection: build: # Use the current directory as the build context # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the fraud_detection directorys + # Use the Dockerfile in the fraud_detection directory dockerfile: ./fraud_detection/Dockerfile ports: # Expose port 50051 on the host, and map port 50051 of the container to port 50051 on the host @@ -61,7 +63,6 @@ services: transaction_verification: build: # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ # Use the Dockerfile in the transaction_verification directory dockerfile: ./transaction_verification/Dockerfile @@ -70,34 +71,24 @@ services: - 50052:50052 environment: # Pass the environment variables to the container - # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console - PYTHONUNBUFFERED=TRUE # The PYTHONFILE environment variable specifies the absolute entry point of the application - # Check app.py in the transaction_verification directory to see how this is used - PYTHONFILE=/app/transaction_verification/src/app.py volumes: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils - # Mount the transaction_verification/src directory in the current directory to the /app/transaction_verification/src directory in the container + # Mount the transaction_verification/src directory in the current directory to the container - ./transaction_verification/src:/app/transaction_verification/src suggestions: build: - # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the suggestions directory dockerfile: ./suggestions/Dockerfile ports: - # Expose port 50053 on the host, and map port 50053 of the container to port 50053 on the host - - 50053:50053 + - 50053:50053 environment: - # Pass the environment variables to the container - # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console - - PYTHONUNBUFFERED=TRUE - - PYTHONFILE=/app/suggestions/src/app.py + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/suggestions/src/app.py volumes: - # Mount the utils directory in the current directory to the /app/utils directory in the container - - ./utils:/app/utils - # Mount the suggestions/src directory in the current directory to the /app/suggestions/src directory in the container - - ./suggestions/src:/app/suggestions/src + - ./utils:/app/utils + - ./suggestions/src:/app/suggestions/src \ No newline at end of file diff --git a/fraud_detection/requirements.txt b/fraud_detection/requirements.txt index b20ca4bc3..a80eedef7 100644 --- a/fraud_detection/requirements.txt +++ b/fraud_detection/requirements.txt @@ -1,4 +1,4 @@ grpcio==1.60.0 grpcio-tools==1.60.0 -protobuf==4.25.0 +protobuf==4.25.2 watchdog==6.0.0 diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index b85b64b9d..d932ef47d 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -1,47 +1,64 @@ import sys import os +from concurrent import futures # This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) +fraud_detection_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/fraud_detection') +) sys.path.insert(0, fraud_detection_grpc_path) + +import grpc import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc -import grpc -from concurrent import futures -# Create a class to define the server functions, derived from -# fraud_detection_pb2_grpc.HelloServiceServicer -class FraudService(fraud_detection_grpc.FraudDetectionServiceServicer): - # Create an RPC function to say hello +class HelloService(fraud_detection_grpc.HelloServiceServicer): + def SayHello(self, request, context): + response = fraud_detection.HelloResponse() + response.greeting = "Hello, " + request.name + print(response.greeting) + return response + def CheckFraud(self, request, context): - card_number = request.card_number - order_amount = request.order_amount + print("Received fraud check request") + print("user_name:", request.user_name) + print("card_number:", request.card_number) + print("item_count:", request.item_count) - print(f"Checking fraud for card number: {card_number} and order amount: {order_amount}") - # Dummy Logic if amount > 1000 or card_number starts with 999 then it is fraud is_fraud = False - if order_amount > 1000 or card_number.startswith("999"): + message = "No fraud detected." + + # Very simple dummy rules for now + if request.item_count > 10: is_fraud = True - - return fraud_detection.FraudResponse(is_fraud=is_fraud) + message = "Too many items in order." + elif request.card_number.endswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif "fraud" in request.user_name.lower(): + is_fraud = True + message = "Suspicious user name." + + response = fraud_detection.FraudCheckResponse() + response.is_fraud = is_fraud + response.message = message + + print("Returning fraud result:", response.is_fraud, response.message) + return response + def serve(): - # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) - # Add HelloService - fraud_detection_grpc.add_FraudDetectionServiceServicer_to_server(FraudService(), server) - # Listen on port 50051 + fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) + port = "50051" server.add_insecure_port("[::]:" + port) - # Start the server server.start() - print("Server started. Listening on port 50051.") - # Keep thread alive + print("Fraud detection server started. Listening on port 50051.") server.wait_for_termination() + if __name__ == '__main__': serve() \ No newline at end of file diff --git a/orchestrator/requirements.txt b/orchestrator/requirements.txt index f8c1ab8cb..5ba8e254b 100644 --- a/orchestrator/requirements.txt +++ b/orchestrator/requirements.txt @@ -6,7 +6,7 @@ grpcio-tools==1.60.0 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.3 -protobuf>=4.25.0 +protobuf==4.25.2 Werkzeug==3.0.1 Flask-CORS==4.0.0 watchdog==6.0.0 \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index b962da36a..64341eb19 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,159 +1,269 @@ import sys import os +import threading +# This set of lines are needed to import the gRPC stubs. +# The path of the stubs is relative to the current file, or absolute inside the container. +# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -# Import fraud detection stubs -fraud_detection_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) +fraud_detection_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/fraud_detection') +) sys.path.insert(0, fraud_detection_grpc_path) import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc -# Import transaction verification stubs -transaction_verification_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) +transaction_verification_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/transaction_verification') +) sys.path.insert(0, transaction_verification_grpc_path) import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc -# Import suggestions stubs -suggestions_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/suggestions')) +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/suggestions') +) sys.path.insert(0, suggestions_grpc_path) import suggestions_pb2 as suggestions import suggestions_pb2_grpc as suggestions_grpc import grpc + from flask import Flask, request from flask_cors import CORS + def greet(name='you'): with grpc.insecure_channel('fraud_detection:50051') as channel: stub = fraud_detection_grpc.HelloServiceStub(channel) response = stub.SayHello(fraud_detection.HelloRequest(name=name)) return response.greeting -def call_fraud_detection(card_number, order_amount): - try: - with grpc.insecure_channel('fraud_detection:50051') as channel: - stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) - request_obj = fraud_detection.FraudRequest( - card_number=card_number, - order_amount=order_amount + +def detect_fraud(user_name, card_number, item_count): + with grpc.insecure_channel('fraud_detection:50051') as channel: + stub = fraud_detection_grpc.HelloServiceStub(channel) + response = stub.CheckFraud( + fraud_detection.FraudCheckRequest( + user_name=user_name or "", + card_number=card_number or "", + item_count=item_count ) - response = stub.CheckFraud(request_obj) - return response.is_fraud - except Exception as e: - print(f"Fraud detection gRPC call failed: {e}") - return True - -def call_transaction_verification(request_data): - try: - with grpc.insecure_channel('transaction_verification:50052') as channel: - stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - - card_info = request_data.get("creditCard", {}) - user_info = request_data.get("user", {}) - items = request_data.get("items", []) - - grpc_items = [ - transaction_verification.Item( - name=item.get("name", ""), - quantity=item.get("quantity", 0) - ) for item in items - ] - - request_obj = transaction_verification.TransactionRequest( - card_number=card_info.get("number", ""), - card_expiration=card_info.get("expirationDate", ""), - card_cvv=card_info.get("cvv", ""), - items=grpc_items, - user_name=user_info.get("name", ""), - user_contact=user_info.get("contact", "") + ) + return response + + +def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): + with grpc.insecure_channel('transaction_verification:50052') as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + response = stub.VerifyTransaction( + transaction_verification.TransactionVerificationRequest( + user_name=user_name or "", + user_contact=user_contact or "", + card_number=card_number or "", + expiration_date=expiration_date or "", + cvv=cvv or "", + item_count=item_count, + terms_accepted=terms_accepted ) - response = stub.VerifyTransaction(request_obj) - return response.is_valid - except Exception as e: - print(f"Transaction verification gRPC call failed: {e}") - return False - -def call_suggestions(request_data): - try: - with grpc.insecure_channel('suggestions:50053') as channel: - stub = suggestions_grpc.SuggestionsServiceStub(channel) - - user_info = request_data.get("user", {}) - items = request_data.get("items", []) - - grpc_items = [ - suggestions.Item( - name=item.get("name", ""), - quantity=item.get("quantity", 0) - ) for item in items - ] - - request_obj = suggestions.SuggestionsRequest( - items=grpc_items, - user_name=user_info.get("name", "") + ) + return response + + +def get_suggestions(user_name, item_count): + with grpc.insecure_channel('suggestions:50053') as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + response = stub.GetSuggestions( + suggestions.SuggestionsRequest( + user_name=user_name or "", + item_count=item_count ) - response = stub.GetSuggestions(request_obj) + ) + return response - return [ - {"bookId": book.book_id, "title": book.title, "author": book.author} - for book in response.books - ] - except Exception as e: - print(f"Suggestions gRPC call failed: {e}") - return [] app = Flask(__name__) CORS(app, resources={r'/*': {'origins': '*'}}) + @app.route('/', methods=['GET']) def index(): response = greet(name='orchestrator') return response + @app.route('/checkout', methods=['POST']) def checkout(): - from concurrent.futures import ThreadPoolExecutor - try: - request_data = request.get_json(force=True, silent=True) or {} - print("Request Data:", request_data.get('items')) - - card_info = request_data.get("creditCard", {}) - card_number = card_info.get("number", "") - order_amount = request_data.get("totalAmount", 0.0) - - # Run all 3 services in parallel - with ThreadPoolExecutor(max_workers=3) as executor: - fraud_future = executor.submit(call_fraud_detection, card_number, order_amount) - verify_future = executor.submit(call_transaction_verification, request_data) - suggestions_future = executor.submit(call_suggestions, request_data) - - is_fraud = fraud_future.result() - is_valid = verify_future.result() - suggested_books = suggestions_future.result() - - print(f"Fraud: {is_fraud}, Valid: {is_valid}, Books: {len(suggested_books)}") - - # Consolidate results - if is_fraud or not is_valid: - order_status_response = { - 'orderId': '12345', - 'status': 'Order Rejected', - 'suggestedBooks': [] + request_data = request.get_json(silent=True) + + if request_data is None: + return { + "error": { + "code": "BAD_REQUEST", + "message": "Request body must be valid JSON." + } + }, 400 + + user = request_data.get("user", {}) + items = request_data.get("items", []) + shipping_method = request_data.get("shippingMethod") + terms_accepted = request_data.get("termsAndConditionsAccepted", False) + + user_name = user.get("name") + user_contact = user.get("contact") + user_comment = user.get("userComment", "") + + credit_card = user.get("creditCard", {}) + card_number = credit_card.get("number") + expiration_date = credit_card.get("expirationDate") + cvv = credit_card.get("cvv") + + print("FULL REQUEST DATA:", request_data) + print("USER NAME:", user_name) + print("USER CONTACT:", user_contact) + print("USER COMMENT:", user_comment) + print("ITEMS:", items) + print("SHIPPING METHOD:", shipping_method) + print("TERMS ACCEPTED:", terms_accepted) + print("CARD NUMBER:", card_number) + print("EXPIRATION DATE:", expiration_date) + print("CVV:", cvv) + + # Keep these simple bad-request checks locally + if not user_name: + return { + "error": { + "code": "BAD_REQUEST", + "message": "User name is required." } - else: - order_status_response = { - 'orderId': '12345', - 'status': 'Order Approved', - 'suggestedBooks': suggested_books + }, 400 + + if not user_contact: + return { + "error": { + "code": "BAD_REQUEST", + "message": "User contact is required." + } + }, 400 + + item_count = len(items) + + results = { + "fraud": None, + "verification": None, + "suggestions": None, + "errors": [] + } + + def fraud_worker(): + try: + print("Calling fraud_detection service...") + results["fraud"] = detect_fraud( + user_name=user_name, + card_number=card_number, + item_count=item_count + ) + print( + "fraud_detection result:", + results["fraud"].is_fraud, + results["fraud"].message + ) + except Exception as e: + error_msg = f"fraud_detection failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + def verification_worker(): + try: + print("Calling transaction_verification service...") + results["verification"] = verify_transaction( + user_name=user_name, + user_contact=user_contact, + card_number=card_number, + expiration_date=expiration_date, + cvv=cvv, + item_count=item_count, + terms_accepted=terms_accepted + ) + print( + "transaction_verification result:", + results["verification"].is_valid, + results["verification"].message + ) + except Exception as e: + error_msg = f"transaction_verification failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + def suggestions_worker(): + try: + print("Calling suggestions service...") + results["suggestions"] = get_suggestions( + user_name=user_name, + item_count=item_count + ) + print( + "suggestions returned:", + len(results["suggestions"].books), + "books" + ) + except Exception as e: + error_msg = f"suggestions failed: {e}" + print(error_msg) + results["errors"].append(error_msg) + + fraud_thread = threading.Thread(target=fraud_worker) + verification_thread = threading.Thread(target=verification_worker) + suggestions_thread = threading.Thread(target=suggestions_worker) + + print("Starting worker threads...") + fraud_thread.start() + verification_thread.start() + suggestions_thread.start() + + fraud_thread.join() + verification_thread.join() + suggestions_thread.join() + print("All worker threads finished.") + + if results["errors"]: + return { + "error": { + "code": "INTERNAL_ERROR", + "message": "; ".join(results["errors"]) } + }, 500 + + if results["fraud"] and results["fraud"].is_fraud: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + + if results["verification"] and not results["verification"].is_valid: + return { + "orderId": "12345", + "status": "Order Rejected", + "suggestedBooks": [] + }, 200 + + suggested_books = [] + if results["suggestions"]: + for book in results["suggestions"].books: + suggested_books.append({ + "bookId": book.bookId, + "title": book.title, + "author": book.author + }) - return order_status_response + return { + "orderId": "12345", + "status": "Order Approved", + "suggestedBooks": suggested_books + }, 200 - except Exception as e: - print(f"Checkout failed: {e}") - return {"error": {"message": str(e)}}, 500 if __name__ == '__main__': app.run(host='0.0.0.0') \ No newline at end of file diff --git a/suggestions/Dockerfile b/suggestions/Dockerfile index c618243eb..1c35664df 100644 --- a/suggestions/Dockerfile +++ b/suggestions/Dockerfile @@ -1,15 +1,8 @@ -# Use an official Python runtime as the base image FROM python:3.11 -# Set the working directory in the container -# Both the utils and src folders will be mounted as volumes, please see docker-compose.yaml WORKDIR /app -# Copy the requirements file to the working directory COPY ./suggestions/requirements.txt . - -# Install the Python dependencies RUN pip install --no-cache-dir -r requirements.txt -# Set the command to run the application CMD python utils/other/hotreload.py "suggestions/src/app.py" \ No newline at end of file diff --git a/suggestions/requirements.txt b/suggestions/requirements.txt index 2c6bbca24..43fdaf26f 100644 --- a/suggestions/requirements.txt +++ b/suggestions/requirements.txt @@ -1,4 +1,4 @@ grpcio==1.60.0 grpcio-tools==1.60.0 -protobuf>=4.25.0 -watchdog==6.0.0 +protobuf==4.25.2 +watchdog==6.0.0 \ No newline at end of file diff --git a/suggestions/src/app.py b/suggestions/src/app.py index b0d6f43fd..258c2fb0c 100644 --- a/suggestions/src/app.py +++ b/suggestions/src/app.py @@ -1,76 +1,60 @@ import sys import os +from concurrent import futures -# This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -suggestions_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/suggestions')) +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/suggestions') +) sys.path.insert(0, suggestions_grpc_path) -import suggestions_pb2 as suggestions -import suggestions_pb2_grpc as suggestions_grpc import grpc -from concurrent import futures +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc -# Static books list -BOOKS = [ - {"book_id": "1", "title": "The Pragmatic Programmer", "author": "David Thomas"}, - {"book_id": "2", "title": "Clean Code", "author": "Robert C. Martin"}, - {"book_id": "3", "title": "Design Patterns", "author": "Gang of Four"}, - {"book_id": "4", "title": "The Hobbit", "author": "J.R.R. Tolkien"}, - {"book_id": "5", "title": "Harry Potter", "author": "J.K. Rowling"}, - {"book_id": "6", "title": "The Great Gatsby", "author": "F. Scott Fitzgerald"}, - {"book_id": "7", "title": "1984", "author": "George Orwell"}, - {"book_id": "8", "title": "To Kill a Mockingbird", "author": "Harper Lee"}, - {"book_id": "9", "title": "The Alchemist", "author": "Paulo Coelho"}, - {"book_id": "10", "title": "Dune", "author": "Frank Herbert"}, -] -# Create a class to define the server functions class SuggestionsService(suggestions_grpc.SuggestionsServiceServicer): - def GetSuggestions(self, request, context): - print(f"Getting suggestions for user: {request.user_name}") - print(f"Items ordered: {[item.name for item in request.items]}") + print("Received suggestions request") + print("user_name:", request.user_name) + print("item_count:", request.item_count) + + static_books = [ + {"bookId": "101", "title": "Distributed Systems Basics", "author": "A. Author"}, + {"bookId": "102", "title": "Designing Data-Intensive Applications", "author": "Martin Kleppmann"}, + {"bookId": "103", "title": "Clean Code", "author": "Robert C. Martin"}, + {"bookId": "104", "title": "The Pragmatic Programmer", "author": "Andrew Hunt"}, + ] - # Simple logic: exclude books that match ordered items - # and return first 3 suggestions - ordered_names = [item.name.lower() for item in request.items] + response = suggestions.SuggestionsResponse() - suggested = [ - book for book in BOOKS - if book["title"].lower() not in ordered_names - ][:3] + if request.item_count > 0: + chosen = static_books[:2] + else: + chosen = [] - # Build response - response_books = [ - suggestions.Book( - book_id=book["book_id"], - title=book["title"], - author=book["author"] - ) - for book in suggested - ] + for book in chosen: + b = response.books.add() + b.bookId = book["bookId"] + b.title = book["title"] + b.author = book["author"] + + print("Returning", len(response.books), "suggested books") + return response - print(f"Suggesting {len(response_books)} books") - return suggestions.SuggestionsResponse(books=response_books) def serve(): - # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) - # Add SuggestionsService suggestions_grpc.add_SuggestionsServiceServicer_to_server( SuggestionsService(), server ) - # Listen on port 50053 + port = "50053" server.add_insecure_port("[::]:" + port) - # Start the server server.start() print("Suggestions server started. Listening on port 50053.") - # Keep thread alive server.wait_for_termination() + if __name__ == '__main__': serve() \ No newline at end of file diff --git a/test_path.py b/test_path.py deleted file mode 100644 index 6154cafa5..000000000 --- a/test_path.py +++ /dev/null @@ -1,8 +0,0 @@ -import os -import sys - -FILE = __file__ -print(f"FILE: {FILE}") -path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/fraud_detection')) -print(f"Path: {path}") -print(f"Directory exists: {os.path.exists(path)}") diff --git a/transaction_verification/Dockerfile b/transaction_verification/Dockerfile index 2adbd4e7d..ad4936c4a 100644 --- a/transaction_verification/Dockerfile +++ b/transaction_verification/Dockerfile @@ -1,15 +1,8 @@ -# Use an official Python runtime as the base image FROM python:3.11 -# Set the working directory in the container -# Both the utils and src folders will be mounted as volumes, please see docker-compose.yaml WORKDIR /app -# Copy the requirements file to the working directory COPY ./transaction_verification/requirements.txt . - -# Install the Python dependencies RUN pip install --no-cache-dir -r requirements.txt -# Set the command to run the application CMD python utils/other/hotreload.py "transaction_verification/src/app.py" \ No newline at end of file diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt index 2c6bbca24..43fdaf26f 100644 --- a/transaction_verification/requirements.txt +++ b/transaction_verification/requirements.txt @@ -1,4 +1,4 @@ grpcio==1.60.0 grpcio-tools==1.60.0 -protobuf>=4.25.0 -watchdog==6.0.0 +protobuf==4.25.2 +watchdog==6.0.0 \ No newline at end of file diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index ad0d9158b..36f592052 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -1,89 +1,67 @@ import sys import os +from concurrent import futures -# This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") -transaction_verification_grpc_path = os.path.abspath(os.path.join(FILE, '../../../utils/pb/transaction_verification')) +transaction_verification_grpc_path = os.path.abspath( + os.path.join(FILE, '../../../utils/pb/transaction_verification') +) sys.path.insert(0, transaction_verification_grpc_path) + +import grpc import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc -import grpc -from concurrent import futures -# Create a class to define the server functions, derived from -# transaction_verification_pb2_grpc.TransactionVerificationServiceServicer -class TransactionVerificationService(transaction_verification_grpc.TransactionVerificationServiceServicer): - # Create an RPC function to verify transaction +class TransactionVerificationService( + transaction_verification_grpc.TransactionVerificationServiceServicer +): def VerifyTransaction(self, request, context): - print(f"Verifying transaction for card: {request.card_number}") - - # Check 1: items list must not be empty - if len(request.items) == 0: - print("Verification failed: No items in order") - return transaction_verification.TransactionResponse( - is_valid=False, - reason="Order must contain at least one item" - ) + print("Received transaction verification request") + print("user_name:", request.user_name) + print("user_contact:", request.user_contact) + print("item_count:", request.item_count) + print("terms_accepted:", request.terms_accepted) - # Check 2: user data must be filled in - if not request.user_name or not request.user_contact: - print("Verification failed: Missing user data") - return transaction_verification.TransactionResponse( - is_valid=False, - reason="User name and contact are required" - ) + is_valid = True + message = "Transaction is valid." - # Check 3: card number must be 16 digits - card_number = request.card_number.replace(" ", "") - if not card_number.isdigit() or len(card_number) != 16: - print("Verification failed: Invalid card number") - return transaction_verification.TransactionResponse( - is_valid=False, - reason="Invalid card number format, must be 16 digits" - ) + if not request.user_name: + is_valid = False + message = "Missing user name." + elif not request.user_contact: + is_valid = False + message = "Missing user contact." + elif request.item_count <= 0: + is_valid = False + message = "No items in order." + elif not request.terms_accepted: + is_valid = False + message = "Terms and conditions not accepted." + elif not request.card_number or not request.expiration_date or not request.cvv: + is_valid = False + message = "Missing credit card information." - # Check 4: CVV must be 3 digits - if not request.card_cvv.isdigit() or len(request.card_cvv) != 3: - print("Verification failed: Invalid CVV") - return transaction_verification.TransactionResponse( - is_valid=False, - reason="Invalid CVV, must be 3 digits" - ) + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = is_valid + response.message = message - # Check 5: expiration date format MM/YY - expiration = request.card_expiration - if len(expiration) != 5 or expiration[2] != '/' or \ - not expiration[:2].isdigit() or not expiration[3:].isdigit(): - print("Verification failed: Invalid expiration date") - return transaction_verification.TransactionResponse( - is_valid=False, - reason="Invalid expiration date, use MM/YY format" - ) + print("Returning verification result:", response.is_valid, response.message) + return response - print("Transaction verified successfully") - return transaction_verification.TransactionResponse( - is_valid=True, - reason="Transaction is valid" - ) def serve(): - # Create a gRPC server server = grpc.server(futures.ThreadPoolExecutor()) - # Add TransactionVerificationService transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( TransactionVerificationService(), server ) - # Listen on port 50052 + port = "50052" server.add_insecure_port("[::]:" + port) - # Start the server server.start() print("Transaction verification server started. Listening on port 50052.") - # Keep thread alive server.wait_for_termination() + if __name__ == '__main__': serve() \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index 54bdcb878..d575366b1 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -1,16 +1,27 @@ syntax = "proto3"; -package fraud_detection; +package hello; -service FraudDetectionService { - rpc CheckFraud (FraudRequest) returns (FraudResponse); +service HelloService { + rpc SayHello (HelloRequest) returns (HelloResponse); + rpc CheckFraud (FraudCheckRequest) returns (FraudCheckResponse); } -message FraudRequest { - string card_number = 1; - float order_amount = 2; +message HelloRequest { + string name = 1; } -message FraudResponse { - bool is_fraud = 1; +message HelloResponse { + string greeting = 1; } + +message FraudCheckRequest { + string user_name = 1; + string card_number = 2; + int32 item_count = 3; +} + +message FraudCheckResponse { + bool is_fraud = 1; + string message = 2; +} \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index 4bb850ad5..d405d9cf5 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -14,17 +14,21 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x0f\x66raud_detection\"9\n\x0c\x46raudRequest\x12\x13\n\x0b\x63\x61rd_number\x18\x01 \x01(\t\x12\x14\n\x0corder_amount\x18\x02 \x01(\x02\"!\n\rFraudResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x32\x64\n\x15\x46raudDetectionService\x12K\n\nCheckFraud\x12\x1d.fraud_detection.FraudRequest\x1a\x1e.fraud_detection.FraudResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t\"O\n\x11\x46raudCheckRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x02 \x01(\t\x12\x12\n\nitem_count\x18\x03 \x01(\x05\"7\n\x12\x46raudCheckResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\x88\x01\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponse\x12\x41\n\nCheckFraud\x12\x18.hello.FraudCheckRequest\x1a\x19.hello.FraudCheckResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'fraud_detection_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _globals['_FRAUDREQUEST']._serialized_start=42 - _globals['_FRAUDREQUEST']._serialized_end=99 - _globals['_FRAUDRESPONSE']._serialized_start=101 - _globals['_FRAUDRESPONSE']._serialized_end=134 - _globals['_FRAUDDETECTIONSERVICE']._serialized_start=136 - _globals['_FRAUDDETECTIONSERVICE']._serialized_end=236 + _globals['_HELLOREQUEST']._serialized_start=32 + _globals['_HELLOREQUEST']._serialized_end=60 + _globals['_HELLORESPONSE']._serialized_start=62 + _globals['_HELLORESPONSE']._serialized_end=95 + _globals['_FRAUDCHECKREQUEST']._serialized_start=97 + _globals['_FRAUDCHECKREQUEST']._serialized_end=176 + _globals['_FRAUDCHECKRESPONSE']._serialized_start=178 + _globals['_FRAUDCHECKRESPONSE']._serialized_end=233 + _globals['_HELLOSERVICE']._serialized_start=236 + _globals['_HELLOSERVICE']._serialized_end=372 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index 08a4f9a0f..fe3863b06 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -4,16 +4,32 @@ from typing import ClassVar as _ClassVar, Optional as _Optional DESCRIPTOR: _descriptor.FileDescriptor -class FraudRequest(_message.Message): - __slots__ = ("card_number", "order_amount") +class HelloRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class HelloResponse(_message.Message): + __slots__ = ("greeting",) + GREETING_FIELD_NUMBER: _ClassVar[int] + greeting: str + def __init__(self, greeting: _Optional[str] = ...) -> None: ... + +class FraudCheckRequest(_message.Message): + __slots__ = ("user_name", "card_number", "item_count") + USER_NAME_FIELD_NUMBER: _ClassVar[int] CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] - ORDER_AMOUNT_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + user_name: str card_number: str - order_amount: float - def __init__(self, card_number: _Optional[str] = ..., order_amount: _Optional[float] = ...) -> None: ... + item_count: int + def __init__(self, user_name: _Optional[str] = ..., card_number: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... -class FraudResponse(_message.Message): - __slots__ = ("is_fraud",) +class FraudCheckResponse(_message.Message): + __slots__ = ("is_fraud", "message") IS_FRAUD_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] is_fraud: bool - def __init__(self, is_fraud: bool = ...) -> None: ... + message: str + def __init__(self, is_fraud: bool = ..., message: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 65c6272e6..3ada2e86e 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -5,7 +5,7 @@ import fraud_detection_pb2 as fraud__detection__pb2 -class FraudDetectionServiceStub(object): +class HelloServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,10 +14,10 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.CheckFraud = channel.unary_unary( - '/fraud_detection.FraudDetectionService/CheckFraud', - request_serializer=fraud__detection__pb2.FraudRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.FraudResponse.FromString, + self.SayHello = channel.unary_unary( + '/hello.HelloService/SayHello', + request_serializer=fraud__detection__pb2.HelloRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.HelloResponse.FromString, ) self.CheckFraud = channel.unary_unary( '/hello.HelloService/CheckFraud', @@ -26,10 +26,10 @@ def __init__(self, channel): ) -class FraudDetectionServiceServicer(object): +class HelloServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def CheckFraud(self, request, context): + def SayHello(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -42,12 +42,12 @@ def CheckFraud(self, request, context): raise NotImplementedError('Method not implemented!') -def add_FraudDetectionServiceServicer_to_server(servicer, server): +def add_HelloServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'CheckFraud': grpc.unary_unary_rpc_method_handler( - servicer.CheckFraud, - request_deserializer=fraud__detection__pb2.FraudRequest.FromString, - response_serializer=fraud__detection__pb2.FraudResponse.SerializeToString, + 'SayHello': grpc.unary_unary_rpc_method_handler( + servicer.SayHello, + request_deserializer=fraud__detection__pb2.HelloRequest.FromString, + response_serializer=fraud__detection__pb2.HelloResponse.SerializeToString, ), 'CheckFraud': grpc.unary_unary_rpc_method_handler( servicer.CheckFraud, @@ -56,16 +56,16 @@ def add_FraudDetectionServiceServicer_to_server(servicer, server): ), } generic_handler = grpc.method_handlers_generic_handler( - 'fraud_detection.FraudDetectionService', rpc_method_handlers) + 'hello.HelloService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class FraudDetectionService(object): +class HelloService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def CheckFraud(request, + def SayHello(request, target, options=(), channel_credentials=None, @@ -75,9 +75,9 @@ def CheckFraud(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/fraud_detection.FraudDetectionService/CheckFraud', - fraud__detection__pb2.FraudRequest.SerializeToString, - fraud__detection__pb2.FraudResponse.FromString, + return grpc.experimental.unary_unary(request, target, '/hello.HelloService/SayHello', + fraud__detection__pb2.HelloRequest.SerializeToString, + fraud__detection__pb2.HelloResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto index 8757abf1c..12f15f4cb 100644 --- a/utils/pb/suggestions/suggestions.proto +++ b/utils/pb/suggestions/suggestions.proto @@ -3,25 +3,20 @@ syntax = "proto3"; package suggestions; service SuggestionsService { - rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); + rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); } message SuggestionsRequest { - repeated Item items = 1; - string user_name = 2; + string user_name = 1; + int32 item_count = 2; } -message Item { - string name = 1; - int32 quantity = 2; +message SuggestedBook { + string bookId = 1; + string title = 2; + string author = 3; } message SuggestionsResponse { - repeated Book books = 1; -} - -message Book { - string book_id = 1; - string title = 2; - string author = 3; -} + repeated SuggestedBook books = 1; +} \ No newline at end of file diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py index 733f559fc..550a4d48e 100644 --- a/utils/pb/suggestions/suggestions_pb2.py +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\"I\n\x12SuggestionsRequest\x12 \n\x05items\x18\x01 \x03(\x0b\x32\x11.suggestions.Item\x12\x11\n\tuser_name\x18\x02 \x01(\t\"&\n\x04Item\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"7\n\x13SuggestionsResponse\x12 \n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x11.suggestions.Book\"6\n\x04\x42ook\x12\x0f\n\x07\x62ook_id\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\";\n\x12SuggestionsRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x12\n\nitem_count\x18\x02 \x01(\x05\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"@\n\x13SuggestionsResponse\x12)\n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x1a.suggestions.SuggestedBook2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,13 +22,11 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _globals['_SUGGESTIONSREQUEST']._serialized_start=34 - _globals['_SUGGESTIONSREQUEST']._serialized_end=107 - _globals['_ITEM']._serialized_start=109 - _globals['_ITEM']._serialized_end=147 - _globals['_SUGGESTIONSRESPONSE']._serialized_start=149 - _globals['_SUGGESTIONSRESPONSE']._serialized_end=204 - _globals['_BOOK']._serialized_start=206 - _globals['_BOOK']._serialized_end=260 - _globals['_SUGGESTIONSSERVICE']._serialized_start=262 - _globals['_SUGGESTIONSSERVICE']._serialized_end=367 + _globals['_SUGGESTIONSREQUEST']._serialized_end=93 + _globals['_SUGGESTEDBOOK']._serialized_start=95 + _globals['_SUGGESTEDBOOK']._serialized_end=157 + _globals['_SUGGESTIONSRESPONSE']._serialized_start=159 + _globals['_SUGGESTIONSRESPONSE']._serialized_end=223 + _globals['_SUGGESTIONSSERVICE']._serialized_start=225 + _globals['_SUGGESTIONSSERVICE']._serialized_end=330 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification.proto b/utils/pb/transaction_verification/transaction_verification.proto index bfe033a8e..44ba9cec7 100644 --- a/utils/pb/transaction_verification/transaction_verification.proto +++ b/utils/pb/transaction_verification/transaction_verification.proto @@ -3,24 +3,20 @@ syntax = "proto3"; package transaction_verification; service TransactionVerificationService { - rpc VerifyTransaction (TransactionRequest) returns (TransactionResponse); + rpc VerifyTransaction (TransactionVerificationRequest) returns (TransactionVerificationResponse); } -message TransactionRequest { - string card_number = 1; - string card_expiration = 2; - string card_cvv = 3; - repeated Item items = 4; - string user_name = 5; - string user_contact = 6; +message TransactionVerificationRequest { + string user_name = 1; + string user_contact = 2; + string card_number = 3; + string expiration_date = 4; + string cvv = 5; + int32 item_count = 6; + bool terms_accepted = 7; } -message Item { - string name = 1; - int32 quantity = 2; -} - -message TransactionResponse { - bool is_valid = 1; - string reason = 2; +message TransactionVerificationResponse { + bool is_valid = 1; + string message = 2; } \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py index db0e5922d..447731158 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -14,19 +14,17 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xac\x01\n\x12TransactionRequest\x12\x13\n\x0b\x63\x61rd_number\x18\x01 \x01(\t\x12\x17\n\x0f\x63\x61rd_expiration\x18\x02 \x01(\t\x12\x10\n\x08\x63\x61rd_cvv\x18\x03 \x01(\t\x12-\n\x05items\x18\x04 \x03(\x0b\x32\x1e.transaction_verification.Item\x12\x11\n\tuser_name\x18\x05 \x01(\t\x12\x14\n\x0cuser_contact\x18\x06 \x01(\t\"&\n\x04Item\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"7\n\x13TransactionResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t2\x92\x01\n\x1eTransactionVerificationService\x12p\n\x11VerifyTransaction\x12,.transaction_verification.TransactionRequest\x1a-.transaction_verification.TransactionResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xb0\x01\n\x1eTransactionVerificationRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x14\n\x0cuser_contact\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x03 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x04 \x01(\t\x12\x0b\n\x03\x63vv\x18\x05 \x01(\t\x12\x12\n\nitem_count\x18\x06 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x07 \x01(\x08\"D\n\x1fTransactionVerificationResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xab\x01\n\x1eTransactionVerificationService\x12\x88\x01\n\x11VerifyTransaction\x12\x38.transaction_verification.TransactionVerificationRequest\x1a\x39.transaction_verification.TransactionVerificationResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _globals['_TRANSACTIONREQUEST']._serialized_start=61 - _globals['_TRANSACTIONREQUEST']._serialized_end=233 - _globals['_ITEM']._serialized_start=235 - _globals['_ITEM']._serialized_end=273 - _globals['_TRANSACTIONRESPONSE']._serialized_start=275 - _globals['_TRANSACTIONRESPONSE']._serialized_end=330 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=333 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=479 + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_start=61 + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_end=237 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_start=239 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_end=307 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=310 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=481 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.pyi b/utils/pb/transaction_verification/transaction_verification_pb2.pyi index 03f9d43be..5bb457074 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.pyi +++ b/utils/pb/transaction_verification/transaction_verification_pb2.pyi @@ -1,39 +1,31 @@ -from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from collections.abc import Iterable as _Iterable, Mapping as _Mapping -from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union +from typing import ClassVar as _ClassVar, Optional as _Optional DESCRIPTOR: _descriptor.FileDescriptor -class TransactionRequest(_message.Message): - __slots__ = ("card_number", "card_expiration", "card_cvv", "items", "user_name", "user_contact") - CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] - CARD_EXPIRATION_FIELD_NUMBER: _ClassVar[int] - CARD_CVV_FIELD_NUMBER: _ClassVar[int] - ITEMS_FIELD_NUMBER: _ClassVar[int] +class TransactionVerificationRequest(_message.Message): + __slots__ = ("user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") USER_NAME_FIELD_NUMBER: _ClassVar[int] USER_CONTACT_FIELD_NUMBER: _ClassVar[int] - card_number: str - card_expiration: str - card_cvv: str - items: _containers.RepeatedCompositeFieldContainer[Item] + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_DATE_FIELD_NUMBER: _ClassVar[int] + CVV_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] user_name: str user_contact: str - def __init__(self, card_number: _Optional[str] = ..., card_expiration: _Optional[str] = ..., card_cvv: _Optional[str] = ..., items: _Optional[_Iterable[_Union[Item, _Mapping]]] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ...) -> None: ... - -class Item(_message.Message): - __slots__ = ("name", "quantity") - NAME_FIELD_NUMBER: _ClassVar[int] - QUANTITY_FIELD_NUMBER: _ClassVar[int] - name: str - quantity: int - def __init__(self, name: _Optional[str] = ..., quantity: _Optional[int] = ...) -> None: ... + card_number: str + expiration_date: str + cvv: str + item_count: int + terms_accepted: bool + def __init__(self, user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... -class TransactionResponse(_message.Message): - __slots__ = ("is_valid", "reason") +class TransactionVerificationResponse(_message.Message): + __slots__ = ("is_valid", "message") IS_VALID_FIELD_NUMBER: _ClassVar[int] - REASON_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] is_valid: bool - reason: str - def __init__(self, is_valid: bool = ..., reason: _Optional[str] = ...) -> None: ... + message: str + def __init__(self, is_valid: bool = ..., message: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py index 95871b9bf..32d7e091c 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -16,8 +16,8 @@ def __init__(self, channel): """ self.VerifyTransaction = channel.unary_unary( '/transaction_verification.TransactionVerificationService/VerifyTransaction', - request_serializer=transaction__verification__pb2.TransactionRequest.SerializeToString, - response_deserializer=transaction__verification__pb2.TransactionResponse.FromString, + request_serializer=transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.TransactionVerificationResponse.FromString, ) @@ -35,8 +35,8 @@ def add_TransactionVerificationServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'VerifyTransaction': grpc.unary_unary_rpc_method_handler( servicer.VerifyTransaction, - request_deserializer=transaction__verification__pb2.TransactionRequest.FromString, - response_serializer=transaction__verification__pb2.TransactionResponse.SerializeToString, + request_deserializer=transaction__verification__pb2.TransactionVerificationRequest.FromString, + response_serializer=transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -60,7 +60,7 @@ def VerifyTransaction(request, timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/transaction_verification.TransactionVerificationService/VerifyTransaction', - transaction__verification__pb2.TransactionRequest.SerializeToString, - transaction__verification__pb2.TransactionResponse.FromString, + transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + transaction__verification__pb2.TransactionVerificationResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) From c9fadc80f410c550f79b21997624269246f19df8 Mon Sep 17 00:00:00 2001 From: anupkumar Date: Mon, 2 Mar 2026 14:18:03 +0200 Subject: [PATCH 17/58] initial commit --- README.md | 12 +++++++++++ architecture_diagram.png | Bin 0 -> 138538 bytes fraud_detection/src/app.py | 30 ++++++++++++++++++++++++++-- orchestrator/src/app.py | 20 +++++++++---------- system_diagram.png | Bin 0 -> 239025 bytes transaction_verification/src/app.py | 22 +++++++++++++++++++- 6 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 architecture_diagram.png create mode 100644 system_diagram.png diff --git a/README.md b/README.md index f7f53570f..009c93749 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,18 @@ The code consists of multiple services. Each service is located in a separate fo There is also a `utils` folder that contains some helper code or specifications that are used by multiple services. Check the `utils` folder for more information. +### Architecture + +The following diagrams give a high-level view of the system and how the services interact. + +**System overview** + +![System diagram](../system_diagram.png) + +**Service architecture** + +![Service architecture diagram](../architecture_diagram.png) + ### Running the code with Docker Compose [recommended] To run the code, you need to clone this repository, make sure you have Docker and Docker Compose installed, and run the following command in the root folder of the repository: diff --git a/architecture_diagram.png b/architecture_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2071d8908c5e0f672577a734431fedeccd67b7ef GIT binary patch literal 138538 zcmeFZbzD?y6gD~ras&l~5D-vl5ReurmG176mK?gn1SMrC=@My%?otG#o1sC8A%`4# zhWpMrM?K&DzFYs@-~HWvJnAreX7+yHwVw4n&syVq6(#Ab1e6322;{1)jD#8lg5M2+ zoR|LV68H&vyFeWLamiF(S^{#0`!B5_I|>511Cf<@qT!jeKJAs%V{z8LeGu=*lgMTH z-C5r0@YxniNTOF^uc5<4?o`*YNN0BCMp${nrEBpE?&V zyZm|lLh+sn(VvIKvh>~O{ygM=|3>i7qh`j)2l#&;j?v#(`tt~qDDn07pNG$)WIkT_ z^U&_b|No(2HU8hYyn;GH;L3KQVk2`uhRD|5N-Y>JSFkWQ*A=4wwSRWOuU{t{BvWhj zPAV>T)pia1NG;^aUpvU(S(!9K z&D3RX?$YRwu%cZj+2AmFHTS@pq=>|OsKN}nMUg_PXIvjZR8EoIP#@fzAB}qC&u9c& zSX<1yboU%V?oT#yD}ISz5k2SrAt|BgH4~>bBK=k?lTi#kp7!>Lco>!eA+)u5gbRHw zj~~vZjTm(C4cOqqx;b3Rgmk!AD{XxPwQg_l>xvGuJ3F~;4}djpASL@d6Jkr!;i17S4KX znvs;ej@H?|qe)clQu&+-BxG?y898?FRVon+50AW?P4p1B$ksgPUoS>IporH!HtD)d zE$k($M6|z(m9alMsn!*`A2Oy3w)2I3EzDC>ew0Sj(lT~6=;Cr@+lWl;uY7dGcP_3{ zKf$+QE~rf%$YjawVq&|YWq80|j>DE`{6$^YiE#pUqs|GRvLa-6(LJzi?rs+Q}@8T1X4on_TAp?#0M`{uAHvGv!l1&zaBC0i*ubYD67QI=JTmlU7P@Q09)kg4hEkzx~Z#pFkt z6ciMK6E)lGD-80n%=hmrgolUgl|NT)Jd$H8m`}yu8xuKYsjJ?N5Kb?Z5rARZGx}1{W-w*t5lR z`!w$>oayl%T!5&^Vc#{Hrabh>KPt=eDWJv?jbQ7}XckLZOWCERp2S(#fYxBrHa{O9 zpWPTX?QYAm@p`v4Ethz1i%=f{EbsIF=A;iFo|TumAeG7@B9x0P8tUsqog*V7+t3s| zmLJvC)q8M(RCM+B!~UemNE80Vo~4b+szebHk=@WA=|MEyoik(Zh$6!tcX#);ledKA zOM+5hPb~!m1TxnPbj$8jPJRs^0KpOFV_56Tm8kIf_F^Kxy$ubw`FF*QH*em292y#O z@#{rR*ClRjY(VpPtOq~cYmJYIu^=KM>MgTt52p@uClnI%I>Mf8x^!#qE5nT%LJ`-m zUzbEx706srh?=<0D`t=Dqh> zcL8qMjF(!pp~2F2+3|y4Ysl1qUXOLKgN{Bt@#eRCyjdB)bPg#^fUdP2G5U6=dsQVV;k3|4GRGASi78yg#sYn+}7YuD8>Bioi@Km(misVd-2J3 zvejd`|4C6%(aymE;`C@Y%-M@!^>}Y3)2P8aS0SGJE%l8M68BK&5V&fl6gUUGYqe7Y zbhfefV8(~^)~y`No&*ZS+2MSI!Q=`8o(gcjh})ub8DIogBP22@so*LVKglDdfPesy z%StfUx#n{{$wJyx0uIqtRaI>fv~ZBKGOXBBb0PoJ>XO20fb%nc2ZmlgK5PInIU5gu zDlUdhLZ5)`$ml*OHfae(jL+})2+T6`@TkM~hSil9C$v??s9LELnNoe#>?bQ@;^N}G z_t(ADPry6c<+TFkF#Cw(%7(JBO>fe>N-<)Y^vjuRYS!AG#>`N6W z!n<^-bNpyCfLtqAt<{tI=FO;iF@{{z*5IA}{mPupPoL;zVcFSM;TPw^?3b8MrnadJ zo17c(oI7{U*~Nut#)^r0SAC4DR&~GAMAq5Ojn7CMZZz$=F*oePVNj6WdYOnAPIt2bj` zzu$8)i+LB*FHXz?RtUx$fb|8rx10}egwYEM2_a?VjNv&rcvb{37=}Qh0s_1q!TWGv zjMc5j#t|Q~9qnTdXn|yToGYu`RQdDEz`#srBzS&KtFyCn&}Xy3sy^b~yARpf*`0Bm zP=k>0@X?Wc?XZJ!{YrW6PP~g3Zy|b9MDh<0JwuLhCnhH+7a7!KynFYTq@*Op!6^n4 zhIsk%C3rLL%78{J@|ZmXjs(SA^~$)#WEB)zpUP{k_EPpMfZ4DjWzU&g@|9HDXUL^K zv9s=N>~Uik2$oL|0p{j`I?T+^uLa^|nB3Sm9h761N00PgU%EQ@{w7nfh!~>%BRfAo zf8Sv#Y1QnjHa1SlKRA>$gs|SPZ-Me=Q zE?gl;>R8S?qe`zWxzZW3%$zF|!C+*sIsC!(z&DBw@5Lk?8irw1ow8``Jdr z={k=-zHk72%kvRpu^r}vS@Lnid75l=*8YB11_lNwxy|1xrHU3tN0U$sI!hS)p<@8* zd0M7>v8;8iM1J9|SAa+;!Nr!F|k z^#1+(Cd+3+X<(hfTe?Ygszcbdl5Z7_Sug8b_~Ep1r4C$a2LhoD&KU;C$sl_$CfUjfS+{O(;pD9#Lq@(z|vC00ENS%Bhn0UGz^ z%a_%G%pk2oy{ypC&>IwqX=!)Z0k77%TW{Gz`8Fu1tF=`Ui^bXuetQ2|DLHRpp{Z{d z)TL`KxB41XM^+Uw8pE9w{=^cW`jP!6>*P23b*2QJb;i z^iQzF)Q%`7D&tX5f%ANQeM!j3V&_j$Yoyu6^0;V?hmw<%U!~-g8Jsm%C<4*|e8|p5 zevSirQvsy!;^HEMY;bTe3}712mu+EG+V{3`LgxqvWw;}_2Pg}0Yk=CRf!fPwJxEXe z$W^9LucA}6$Z5U}ym+wr-kK54dDBqYu(m5J33^aJb9A9Wfo}Uy^?jWy~Y;7(XjQEFi#tvA}k`ZxV)Sr7sHw> z9YF(XSyV}9^`|9dNjuFD*mPV-1Nh4a@o>C1q`YhJ)-cJRodRA@;?_tZiSxFYr?Yv8(M#?iZ zy`w}pXQS6s02^rC{gG>wh!C7tZT8!)LOlS=B2PU#oQz%X(=mEY|8Idf7v4&- z1bLy>0)GGZ7z-Eram7XJ|4Y^tzU^<%&LhEvJSzHQQY;cf;7_yT=<-PvoooF4c;DT9 zXV*P4F32SXeGbC>#{*3JU^)+T6Xg)>J29f^Jz~6)tL(RmG@PBTNU9g!slN(&{Qi%9 zc!u+WI+iaUMLWg9v*lA3&ts-`4bLSSmwq@N&i0={^YD}{te~Qyejt4Q_<SLA|; zlHRFzj$HAo7F@;FlA3pB+IbmQnW82AR@Ix9fgLd(zjM5C5t8P(UO@e8uXHAJKQ@)` zsuZXdS>}W$M%iehdw6t-n!P!`Wj`NEagmdgt#W6Eya@Q~_kI@ZTFT-px2^0QHPOrR zp?0(?f{jeKSsi_j=M7%L)OpI$k3&(HS>rYbE(4?V73r_Iup1o^<6Hx;GPklCoaV!5 zU^rll%^L;&@0vPKYFX&$vUB|#bsqtfPSKy&iwuM z2BbT?yYCl=cbLQK>gs^9&jhk)4OEPpeR5h_I3T*p0L!9MQ&W?Yl1KpO1>kABK34L` z17%<>%#G3Qdyk5fvk82PW-amN`LGkM9>ef0h2D_uCa+8HTYyi^Y zidZ3aY!7#05AK;tyYUR*dn^OVlJLmQ<|P^y6@`;u8?#NuA>^E+px`eoE?P{@Ry$?Z zx^DnuWcfDQ)YO#Z=FLn{?pYr^P`PmF3iGzr(=SQ4*GV+SrL+6a7>Ko{|6U^TlHcC- z{o#5qm-rsbtn6%C?CDV(I@SMJwmpKj%GA7)#YYs!;v(s!DG^q^N%6?Mvo)k6xc8l{ ztq-3PVe0^PHS63rLe0HErKzy!BFp;xnHA76pcRMu)5RB;mfQ(bi(N5Dv9ri=yV!BN z(GPdU3#h{-S)*OFGm9Z!wL-c=B#>t6V8FEgIY?ljDAamvaxi5#MY)o5V*SE*KFLNo zOnjYgZPr~`>jS(v_L`cSj=nx+P#}kZyR#FT{}1MchRafSP3|V(op~lL zfjIvJy*k3@v%mf{%&Za+izgrqpyBQWE@ZWU6&rai#@sej>(^QVf*)rrf!bwhFbSyG z_HhNmNd4*4SM)5*%rf~}g=#=CB_$`5Ub~hCv=UB&0R|%dvd?36m<2da%9?o^k-+-t zjAhRUTx+2=%g1TS4(K`pLc*xb%uF1jU#FA0b&#!`nv#jikPHd+0d%yhqG3(<_~^dE z>01hN2*iQ(cg=Y56h!kdI4?RjmQfZb1OXM;xt|JX9-!;?GxfTO0?<*wN%)+8O3Y)6 z4C`Z`#-o!H^4t`9T%aE9EViLRSS}qp zOPI`iAN~k(1Ln!Ro`FHqw(eMsOLpvi-FvdF31!9s7|jqe_RejK(7WP+&w;B1@+*PI zD*PTDU6>Cd;9N%9$U>~YDB;ZjlQukHki7qmfzQ(2)$+N4AZQT9)bN=H^Z`_*6NG?(eM(g_)PrqgRI7Wuz({ru9w^HnmMnGjOR4NZ}Ht zyt8xZBiH3;^j*@xbpYNjMaOWX-wEs8yEwtES7{forlv+hMP*y?4!e?_%IkreGMF@~ z_bdT-lL7V$y)lWX_V<+J z^wp>gfyeros*{rwa3%Y3l-6dL8K_`Nix3{G|H-<+iT=OcRO^%djYpnaY1fAUJ-#E0 zBWWPtqBnVT{u7!Ef6CM4_;^eEDHp92RBtU)wZZ!eA7C~0#Pdi4=!?$J&;OgeCp<8D zg(K)<*h4z$9nIR+Gcz;Z+pXk$o?CP0+u7@qfmD)(85kP!dF`0j)Yf(a zkm79EgQfT%ulTN61-!hQ2XzwBSpGPAdT4k1sB0$2BwHn?6x`I`^D2c2;rGwM|HH;R5LCpNk1(wT{(qfz;Q8T1Bu+g6mPy88kONX)zP1wMjS~sr z;xWLQ6kzBovw$LLZr(IA$k!>!hzz|iqoxM?WQ+6=upPbM-mo^=-4~{BWtDGOSGheg zJ&pPNk<+01nZfSC0nV@9ZUkhU49&hMe9+)~w9`gTM4t;>JF==M;9jwDaSi<30g(>u z=}G{Yq?r9lXxkY6#f#s2dU}}SlKm9GvVy@r8W?I@T80KCmJNql5MFOpqe+Jxd0CBfUzNa)aY# zR=dLRQz?L1rDbJ901`>AUr*1;VV<8sV=i90#LC9T)=suPE(VsetHNGdT6*5iA-|+6 z=EUc%znxt{kx_$9y_nl-wB$DVu+qNG{wXhX#sF{tfCe8Z`R$m20}3)ZUpZCuXtcv0 z3v!YAq2uG5j0)c)|8jSA)c|P~b`Zs=$lZ8fg#^g75pbgbDDpw_u7+?nssKbMp{6c; z^7_)sxA!+09$5((ZO%3|(XUL^xFqme!}iz5=~gCC=RmjVsLf#oS;oAr&&>gWOxgTy zf2Vx_VG&Lvk_T1R4Q6qe!+x>tK+;_+ZPvq&AeIDoqMpqu2cF}W?x(N@R zG9>LkDlp>V#Km$io*q*6F68weHkF3yFAjaKkZ3rSzRpAEfMc>r-rnR zF@`R&RLS?VFsBj?HM!c#^^nmLmxI_8uA)cSSsGMcE+XXZt-p@fZsZDTHk_%+G8Tx2ASAB7QKC`&ESh&xiuPd5GJ=7cp zeqY$wC8$|;#C52^ z;sR%He)){8utZkMhoKo&oCZ}A0Ipcyf!i-BI5S#+Zv6&Uch%+1BSK+@IwggtUzPR z`^Q^y;Kqq~d3$#b45;F)od_C{a_b>R(Cw2OWKS;3PW6A(?jx@iXH~Yht2=hcPK{%M z3}E(vUtj1i&>%ltsAC2r03*%qYaNp<;}bvrt`&9)Pt_7YxW;DJMNuLym%Be?ZpJk~ zfaxY%TN!p(?KEF-`?`bGssDxMjg|k|yeRlRAfwZBw2K^R^z*@hL=6rJ83I|w3u=bn z>2^DeUPwp?y*@tFsd^shQ`Y}T^sFz_BdTF*7dvN)=5~u!y#WYm73f5+jqWl=$pW{i z5iG97+wY)v#s2K=FO87+3~B`fS3G4%T}Vw(bQ4f%oXrF}QMfX3o0?iP{VAT?%Ah1p zhk)CGZd^MDX%@T1_IuVxYXr1)fIl-aVFK2H>OZ4AQv(m3&p}i)egh*=C+sKRL8F-< zgy;9`!HfRy&I;K*{euYLYQZZVZvICL%86UX>Ry($9woxLcvaVblOym5^4~EcN9lQR z6^~U*7;y6|e;?JEP7CKDX*oLoT7Q54@c&}w#wqakD$b!+bK2a_TR^Q!fo}i|Bt_iZ z!r#ZUo9i12JFXt3?zfI`@*$qb%5`Osfq{t$SV@mz=1?dnF)^`bfsSfpVzA-01A#QUQh@=z;g39>Bo3)>pbIgl%fCAKH z&$x`C%IMWWDSNtv<*rt-=V;KoEVGKhHReGW>k0{jAbK0{_S`>f{FnuI#~_JuX);1| z9A~NMuSrd7EmeBkS}7O*1JX6}6wn-bagZ6`;Ls8w4UCeT$RppLc*T32i{f?r%(5;Rcn8Z~dN2GY&^^NeID39X625uvHt<(G9uQ#G#g*W;Ne{Lc(1Bf@_`P%Ay4pR@o zWLC6X6L4bje_wgEBsVuV&TsXKbwb#-oOBHg-0ds95EwrCdc!-;)HMs4dTNLFe!@fL zj7Cgz)|X3DH;#DawTIL!%-^bQsRIlKKEFkk530a*g{X$3AM3u*15 zq2qTsLkHHqLdqS6pxj>gT?^C&h&I1Fva5X0Kk2!9s%6f28X zPigU~CfBE2sZf9E++1x!w1U^s=Bq4cR`NVT@_A>22(Z|Uo6ibo>qXjkd3}qc3_1~! z#uZZDmfxu4WEC4&!l{sp$5!4MXN0TRVMhx~OUgcfOdndm6v^iw+x=yOw+%NE$9ve( z9s|aFfSA+K$gVDM1MoK2EBzZ;99{y4gbjd$p-?3(*$^8LoGiGgC^AwPk|0o{ymZ*Y z-A+4NjR>ST2$(KbB z*bOZnUMw^gG!CHA$n%X8UGms9Rj0W&od9#jQBtD}P*4Ysb&Pl#8KJ*j$9s=nty zWyN^JPUJ-J!~1V0Pp&M?7?&?1@c>ATz^N)p5A6^qTk~F&m{F22YD_&x@~NV+|07*v zcB7*2k%HQ?$ODC=m8p`7DFZ`3=$8Y*qP~#Ow_$2x#1G7DJq%9E!Zl73Ic2NIa2B7a&+w3(LSk; zyIM18F{qOm&@|qd4QtA&&C3m23dS)92kNcOJGd9qSdWye^c{_UTM52BrmpPjkj+hl z^4@xRvbVq1fZ;Ko#7(nhn;P0t0*sb?J=%a!1%buy4vII+s=Q^ zQ4bpRb~}6)gGOs@e4tDK``?4{ey^1B$ZVMXxzT;qvF$Es5xY}<`=K{6Uz2i2M+fj{ zJksZZn_m9AzP-2+8yT7Ice1~d^5}`PV=2;BUazL@-*sdZewh4 zSr{Rj3|G(YTV&QQ^B%d)Hq~|PEf41I!sFl(amV}1pL!dD@&Xo^{kYEblLg7ubNrQ( zMYBO(443M9mZWe!T6)FN+L|xxiMfrS{R@hlce#Kcp}VJmw=$bwwofaZw^6q*3yUo{ z)YZOmV!9t(mv8vR^Y=WH-r>W7=yeQG-uue5_|3n5!iDeHSWUfpyOyTg1^zhRD@Ib5ILngL+%Pk?D+1_h@ReYW61` z$-DkAO)}*)7{sAc;Ur-d8r?V>k%MfGoYpQ=>{r+|w>f!V@Aq!$R$c1J7Ig_$r|)JWkhA?w;&;XZNI_if>uZK3^aP=&#tS@~{)qEEDMUi@2XV8%?$^c5iQk_V&_?RtB<{rxt!VpB{QZ@@6Ut%D=b7%HjIC8T z(JtuwZ`YqK+wFO268WgyJrK68}$?|mE0!}}?40t{_QVNVQW-~&2tsLeFcl_@kh zlubXT065Zg@BaAC|9pM}$ zYG-A=6pQdNGadx&Mz6urEM?J|DnFM8X>+t{TyZ=(?!EKAeL;Iv+M%c@`1y=ZV!)|h zPvVreUc}3%ps|XG^&6P*2sH%n|5p4r9l<`a(a66qd%WvfPUc&u1~?LQLe94xg#lr2 zyIVO;x%2fFpbIi<{1{6+ZR|ufacHTv^$Mtvxdb--dZ?G^VdkG_dy~BlZGNCAu+aLY zC!qDaat(4=19?Z@4Qw*=q4QKfBhn&m#(#A9PIf42X~h~*9lSjMD&hOMn8HEjGOjjp z0`g2mEvP!DW6+Rbxve8R2r^$IQL9H2u2h)VSzG;ZZ6&)FIX*hNq3T_12=&smN!#Ju z)UNVx*=tX=a)~}#s?)SNe8yB04X10e3ES&_ClC!wu$k(k$yS9nzzOaWf1jy}HS#ip zC3U`C{n}c6+g@@*h#7qBD0IfCVE=&je*DHs;YO+_PL8_%sj>kd=9!e`VXWE&8rejJ zi(q+EdohgC@f2mqe23@7D5z@$AlK>uTr-vwQ9PAwvlzT5^kw#P`E@98s=>T4@-n??xu#k9{~W%ETFCJ=3idXYUakmVZ?-eSh(f0*;}RLz|F%m z(m45STD>Y(31yfmJ{h2bXrFw(UoR;YKfX(rtKVd@db-^5$j9;ZDEw3{u@W`N(*mWz z75Z|sjjGO2p&Tx_FdKOL9{nx9aOW~4yIPoy=I}b!CuGE5r!e8IzY&f zwncA1N^nbMuP=ZjJihfJBS!;4eC~U@zySUlU_V9Tw%V@;C!#F?JTZ@2O`IpYKDWl0 zbD%@rDzx%^=gCTD2Q=sDizfPxkyc3@^XogS8)J@&jV(GNjLQX8pw$7C5&b_jAN35u z6p|=BWoTq%vDY;y2W1w zyVu2Ww6pz(Z@JO@;akH>4xsL`qIO895or$LYc+)s55O1%vBlgRn4;vh#+ZnzY}`^> zmI>l0b?0Q4I?R>f2WUtxNHvNYtxje}JvyMt!$xNAwVp!VmJC9j&W2Z9f&~LqH)y>c zNH>6@7t_#?oUu>MTl99gfKggH==S3A!D;~pKf>mu$Pf03fz5w3=8Xh;W!v+8IWaJ` z=zdu@&5YG`kMf+7ACDCNu-Z_O({L{x1N@##n%{aH!K0b&{=rG?L0S?G^z4IWcM7L$ z!qK?)>d1y_%(i1yj-QZ_`b!Ii&q)>z-_`Y}lu-6a-&Fm{anPI9JVUAUVEZQZO1ujJ zmAl)HIc>`;fLCEP7dh;w8?$Ac{ILT-{h+gK@U(}%GKl50i4d=Zd5A{4Zrpd5U8NQ5 zlo?B`f4!(uSvfewlcg$b1T-$Euu!PiYf ze}nP=VAjdy`naob7Gf z7$bQ*ny0$(k?rDP7Z4gi&L++uS5o6KmFx2pY6H16awy9~Fk-dPoT7)`eY7ziu>9Ev z2vk0^eeI)BxOR4|k*~uGEngn_&4>Wa`I*FYpeQu|;ehCsp!u*j7_^u;{sG5>O8s3TRagAF22ji~{A46=CX259 ztiR^t*wZS^v3z6#wj=Hsfa)ZX?zR=&cx7LZ0!c{Q#ygwZB%e zX;BY4yD2^FG6%j1WnTq@sk@5qXYe~r=d#AIe(&f|1z%-IcwqSk2ijzuhJ{_*muq2L zaR9H2+I^%}?e>n&o2~s%4DbOeMgj~;kFgT5{%cY}sJ6Oa(BKN0$aGV?;=v`r?a+xH z%;l;=t;Vwb%aO{qQ?@PlZ9JCeW;(k&2bW9Khi&_1V%XO5nI2UQ9R@V1tba82MX@lP z#^m@%!dbC$uU3bN26L{9^X^o55KigU+pjIhS`{!He2L>PHmV8zhc#}Dd&oFA6xj^sG-`323e}uxY;x?dtz{m3_RC|! zZ@O<~B|S||mSNJjrwOzuUYk~LpEfm(buKgxlk+1IPI9x+j<4u{r zCb&oRn8fLa5g2#kb=gc_`mR9dlBE?pS*oDOUeO(Ct=r_BDCDI7lMJ7EUZ-mF0N-2E z8=uwr9UM9T<-%rTYTv;w)?f0a&+G~2uJg-DkFw&OZ1t_(oyTyk7(?n_abO*T7Un;P z>g71cZ2~gi5&|=$%@YNNF6CsAi1$OtAA>P#-@kW%9W{10?F=Fus5)U&h`o9Jx@!6} zd3Rvl{&Qs9xL@n(V z7&q*V!0`A`=K;=Uo(Gfz=t;AV;LT@c<0+`HJNNEooHzT=BHZ&fr9nBH&%DEj&>Pgh z5^ZVAw4R)K=(B9~oG-6kbo0e^Cz)zbQlY9M)_%`ijq{j|D541C#)NlEoClIkG}I;b z7&Y7r-3ax_#=>D&c?wOpP)L0xER&cqp*s+Ixw8WP(60e*Q@CMja7} zr@|A3TRac@z&A>#4T;KHcO(#xiT{_E;_+jU5AryE@SM)p5$iX_3#Q>)-D8rFfK;4^=gS`?N2GL zt`0kTUj^Z;;rc%=liQC$&Crww7er_D%I(KV)W!7PAn@I#-A6Gn>Z{X6w z_FOWMueVM`ZX&~!#o|LB2h6tXY3FWzt0(^HXxGPRAXG+37qs7|e^c!1hXL@Vdj7<# z-VNutbN61>aOaYY3ku>wWn2q?_9i&kDBoWQi06eRU!%(7S}os|^uRNR-$UIAnEGjE*17Bw)X!x?#Y|!up;!$(^{L4GDTzH0Svw@*0 zZ_YNxg4k#M!*>>6(VkF(OwDoRGQo3i0NDA@zYFjO1^V!aYi_w^oWFB}zyOHRg&!Q! zOJRrjDNSB%6p%aD78*ABCto8!$T7U7(XL?qQ9N!}?Ywd6)VJX}r5dGn(ad+ArM5!) zj|!G=0;4azI3LFHUGJcQ;^17PUceYmO!+GoASvqk~yw@+wLNR}_qo`VTDb(F*D^E~}xH+LOe*@M#-r2t#JY z-a*OQ%-SQ!w-MVy-+OtVpjci!ZjLkF^np&T>69-}`BrUPnVVah9MA@B_ePH|Phv=Y zgxsZ{ip%}jB#d*K*HUX-bESPqCgw01z97URBdge63P+eNt@q*Jee0mB@Q_BNfOyc2 z0*8g)yF1&q)sYXjF2y98IY5Wcch~{#O@yj|7s4Q>25P9>iJp8`7m; zkS?)j(5WCWw~#)$-?#t6&E7Fg`*n#?hp=*2>Yr^y zttXq|-pgZ7bokGX+fz;EsBWess8ovVD7sU)XfAlWRYqK9x%p<+|BxVaHvZgZ|AYz) z>GD7gwPn4)Qe+#6d0~8+SkQ=#B>e7tzwP&G;Sx)Fn%CJuJ})=#3bwP8gH&<4Y*!hy zK_U+F`Y_fmR9f%zxeG!P;u{^6uK$*<>Qe5|n!*8f^miGa^ZZmcysV3kzG|k7u>rm$ z1Ysm{6(n+DR|qOdSPQQZ#E|IG3f7;}aHiK-_0izI`~4Byt0JLYv{fA^vDcj_xXJb{ zwebaR>SiSU+Vx1nsm8>&F=ENDQseNwEArM?9chC9yTl5eGCa>VtlHD%`R>s5K+48# z#26!kRai9^c}-AM3-7CtysW`0xbm3u@xLK&@YV7{sq3ZX!7CuX)8xAfuQbh4PTQ4c zoK;g2A>|hx{`yNR<0&aiG$l*4jV{V6j6{vX_!w;Qy^PcCY^h)M_#UgP)N8z2jSKY{ z>MCA=#QTTO_Bc+=;_IlQDJh~?r$oN+%{XV<%6z(4zM_@eZ_0>!10?ay{{+bxkweoZ z!vvbz=qb7F6Q6ff&fVAkdLf(I1Q8JUA&v}YxOXKf4Edrya5)yrZL1&-jeurKkwO=KL?E83`hkoSk`_s+6w47hf%O54XIG*q& zg(ypDeZFqPNx_QP6YuIJ?RPQfyOOAZ|1`aM<#cYx#`C033~eq5(JA0xE)D(O%_V}a zAZR)4pcPLmdVaRWLLS0s@BQ<@@FY2$Fh_@YqxMSciVJc#v`nw?(0TFEuulnLbe3t_ zUwMIf=!qZ6xn)jk`^o3q(au#S?{cO)e{A2)Wcngh&L7&=-qa^9rTi2VYq%>cfNqie zt3ZSHsUkxtrP%b!X?MZ8@WHo%_DHjA8lkqI>m~=_t90*Z!vC}VeIK}6f(+O z&F1A}spRr+RPrp|<ca%)MIW#AzIL=bV0@!}|DfOL zGC4f0uXy|UM5RD4%`t^u7y(%bUb1(?OEDtqmICYf{yZbkDtGZo>Pgv09`pbl1a zEs-#P_7@RK_d}#V^jvsogUCrhel&tMS5f&tg%@#(-@8seXb8ay`%^|)_O_?k=c~fq zuUtHjgnrxFa|~kN;5?ak2{4xMEQ~95X!UiqIWtXfU9`C%_3iGDpPK7awVk-=zb;bt zpGxW-XknG$Jeiig92x1q>7wW0<7${k1HYQP>Powvc8%dwo-J*#GfhPu`9v$!<20$pi~EN&oUuh=+(T(e>L>YJhaA1N<3);IW-9qT4ei zGy|$X_DgywN50TD6}9d_`AHszYxPil0d*3v#>2;7eTPP8Kg1S^&!XVMGmA2I+KGl$XwI=A+BAbu+eJAXVXDwP51E1`fO;_(u$LU2yWP@V=0d`E_FrS0jd>pAwk+t8Nh$hM7Q>q{5A$w6kXYI=uFigqsSO6NKajS z!#q*tEKhe!hrw*b@-2;b&Iat`joKG4K7E`O`GghO<=6T)Q6Dw?w!@sG+-0UZ=1mo& z|3yG*s3$8NAMJn5vW2|h&br_zz;KnE(^&j0C7FOdwo%0B3lm|S35boY3AV!ZZA_($ zn58l;F+`_!)#u8s>p>KoVP80@Vt=>~QoC-5U217l__CvHHPKgL*7qcuP5+X#yTjwO zrp*hsg@*k2_C|I%{m|ljaOK6+$#G9@4*(Vq&x1mf{gPC6!!XuJQq?N=it~o;Eu%%Z zjcB6UBOnlS3*;OCr2(@!o#(@>uBa`wl$yvNizO%7BiBNvAFTIuERVTd}2MwOT7+7w%))!R*W6Z)Q2>J*!z zrPjw>u7w~KUXI&wsL1c*rNW<14GZd2KEI^bGBCO zfGd%GKY$qIKFRyR}Yv`(sGOl#JAkcKqucQ|A5T?-X%YO1VHMsXc z=ki;rP*)<~1Dp{;PJrz}U?tHiX?P8G4C> z-VLDXwooT7TXK_RA0JpgfHceJxBV1US81{ks6W_WW2Q17fXw~F%ui)5>z0gNeSKUX z$k}*IbhGVBpbY&}AOdU=XK$20HyO_DE>u+p)!oKv;S|FOGjuKOE`7In=S7nle5Y#! zE_Dy`z4$8f}ffZ9<##{WIxa^rw6jkY$3n&!}7tg&_`jDi*opQ3qFeBm5}esQkIpN z*$Y6cJAHfeTOAJE9K#GTd^>ZxxsvOB%Nd)?NLF(>T}5_o5sBh8!8&ig%lIoQSVu+W z8f0!FY*N{khS)=p8~Uxqv}Eou*98|sINB#xWO(J}<7c!7o8M|v;Q}^o5LOS3fn4w)MlkU)*0z~ed78hRGG|d4PMue%KEIc4S(bJM8s~x_PxM1s=Q{b@dS)A zGPKzBPLus;+F0+3BP?NxJvX+Ao*w1}B`rvF+UQAp3SED$pFD4Y_61Jfq7b#0SUe+S zX9bl^W7Mn9SIuaD6bUac6`D^e%8ECPeH8BdBY4&?yI3Lis*4zBq$JE2+f$T*?k(r~ zgCl)>Aacuv1pBincFHCCFm@7lLxVY5oc%w1ImJ9QidOL7>JemtUs9LAUzD>x{RMwT z@Ku~cDme%8?#-)He6hQQT7*z2G)Xv7g&BGCy~Mj*%J*4>R%yC*Gdfk)`YATZllSPL z?ZG@%{%R7U+oDFyB~yqBb=%PQiSIRMu`i_1Hb19k$+x%5r&b;EM+hLey~q7e+})7k z*@d;r-P^_%UOYTW6Hi<^v|F($_sL};ES~?V?=qnhq(d7 zQXmLdXq(>)WB=JitE301@XW>3-f!R28@8ILPl_Fe_^(iiT%MOEF0C@@gHk8uvbChBk+m-K2+bEbLmG$I=uO=2ze{F z>Ntf;RycB^;wS86A5HjXYZHPc4f2OiN0&3+sgkNS8oB9)wX>PBS;IXr2n*tSKx^t|xxw;FcIxV!pvcMP*TT5uye!h{oUP^q5YgSL7C z!&HiYhk74+S(-YRwc>q^_Z<2ed1oi_!90@TO8X+`N1*I;UR){+R@>MAfu3Bm`Js|m zT)0`!byA-JnC)2Hkg%4r2L~nefGaxso4t1`k{v%$M1{edT1BbeJuIBo$_*6^5WWDj z!ku8;OEg*zR^Dc3=SUG|tnr-m{=RL=wfJ)<^w2BzYEI`P{sr11KO z;bUC->8B)$dPh>#@5=$R_+y})PiFU~fq(_P;t4Tv5V!^OApFHt0Boc`Q zki~_(^@7iuIgg!WlUWJCbXAYc5W)IvD4VO15|h{MLR~_zX6v(O@!qG!r)k)doN$_?bYFPTP76K zp(l(MxHgnxl?c!jG?=Uq2#lp~vz_&^!DDs92)aB_rQXqMvmgsPC({ z$kmIE0?1v=cLZI7<^=#85tW#b2IS7_G2^?Id6w&sOR?`X%|5L#f{GdPT6*56!g}l3 zUbvR><4+Bg&Jxcyu{l54J&&o;qF~SouCEk@a6!Z&W?ie973!rSX&mtUaFK!0NjN$@ zwLKT1d0DG++;uleOOmZ=VKMw}{59;4rPO~xa;&(16~9#>2B-}?>Wq!}##vGDX36j^ z|M{xZ9}H}-3TI``F=OvYbdeaZOvLy!pzX*DN4TXRXMdROK2mt5{TB^lYXJc&q= zxZA0-?a|{h)Izb;+ck3fYeVd^thtBTyBy^;X_=9q*_>t_f~08Y+e38Kp0lBV3v;-$ z!BKs5MDo%l-ktjPqongQwd%jIYYeke-AYpGmxQUC%?DSBM)XD%e#c@sqS1MBI#7D^T3D0S*j~~})BVQ0pBL;lO`f8@Fu8x<=VI2m}9po8Uu}^;AzFvXm zZv<(-Vm15&-np9|)j&8RAld?DX2^KoMT0|$%o>0W>n8qRg?yo)T7sg%LjfroYPSJ4 zGTeLj4ho8#lMIU%F+O#%%SA!swV#NJ+{_vB?|>CLgFt*MAV(-`5B27uD>qBb~+C->JRqG3-v)-GO;P8=82tuoH0N|hm|Hr6j zI%vF-AY67LO4;jKL^^v5GY)u;@mB^l^JoeZzB6xT?6?G(#X`Yfd_@=n3uCb}#h&t& zikX4V1kfbGQ8x7qeYrw_Do9dV(&@JS56M63ZS}As@O|N)7tBEidOciI!I6`@ar|$i zsraeW*Lv4Ta0(?^_)Hm!w&3~`4YT0WUpt(jP){jQvn$2Zlm}_j`4>tfC8)wBsO~$y zFCIjBW`Y&yNh21>_<03^fq{0r4$!so1nEbKX0k88w6WI0KXmd$j& zJ-a1j3zD~6#L%I25mo<+3!z!?X|S$nJNZ8-g^0;^p<}-RQfJBh?i}DSa~Q0iSFrWM zm67C$n!2^z2w~Nn-%#6OPbx|q`8!)7Z~}A`XkMs-hRzsU@c%w-Vb@=B2(0n*j~X_e z&GnuY>Q*4CpMu(Yyf0fpW=&7?8CdQ>In4n3n zI3H<7Wbl!Epcu&eoTpiYPV`LvrRt8-nbX!S!@b?^bFVo&sD2k^bR3)@u&#pC1(8p89_tMj0FdDye9=UpDtiez(D|>7dlq1m}^9N3wxu;YdaoXApz`N zF_m%cdIuuhV@h%w3fL5EEb1dITtv=tQW`px8G4D?=lz$Jzhw!2Y5!OoZsA--!%mo0 zDnu`k>Y9sCpxnztL)Ul(P7n!!qgRS$+T3cv;MkW6_5Xkabj4-WM!mfNByxP8_KsCL z&NKuNq3bPzed+}%IZStObHt7#XfnR3fzR>t-6X8Refd>Dw>Cyoajxb7b z4qswy`mDn_AWZFS=I)zqxR51y`fDS%w;I#{12ZfjG-MhW&;Pn2-VzP}y67c3)5dbnk8VuxTe zg~&k*%1i6y5>Y@+x*7fpITb4tGd_w)q&}05oLnPbg{pWQP=;`UP^-o9LOF>d#WC#z zBSd7_?4PdUxpWGhC&mNuInl8NiS1eT#bAXf9Ur^~lsH-3rYs_1n_B&+5;+Zysj$cv z+vo*OZc+J?=?e{oC@+OR4H;1Dl#*b`dqv8?Z?9hm9T)MgW|xS9mdpR=W`aWoU3NBk^N?`P$tbvCRv-g7{=UOPULk=3*)M{8kq@Xl+AUv#N_Hk7>rz1?6iHEux<&~SRVS6qJr^^| z_O=f0xzJ-u_TIyNk6!UDN3{`_u0^$`n+cYs?{@KigsYl@ju8q7@lo(-=QvauL=3R` zQZmrU^Wy(^`7r(k#*NhzH3;zUwyF_iN@xpW6C7vuMCeAoe)LXE@ zTY31?^y`jsj*beNoc`7F0nAK^*lYdj+~k3$)!(l#a|!>|_&_L%$@%`uNkO3Gb=FnjAz0>UCeDA!Hsq({!8sy`9T{Z%3u$)rIM<63Qn`hb3>)0p<0(u8|3lh6Cd9K;m(s#%2JQtX% zBoElEoQRDx=Ap;zS|T*(uDjQdPV>UsR0coNrC+?X#@oMKi5`I8S?ekg_)`3fw7y3n zK5Fk}PKe$PhzZUAyBCqg`rOXq&bk~KHleF>B)hzJXsDv8`nU6ckX*Rh-26#Z%GTfk zeC0fwD21jVrwzrJh&}tWmUZTNe{ve%Xe9ZdHd^Wy?t!^x0kqu6DQS6-R(^$8G(9&! zS#D40%vwJo!nC^?g$|g+_f;D6B*kcI;t>NG6zbn?i7hjiLgn7Ud%kQ2Qs$Tw4AJly zXsd0Wy52NUe9eUek0g^8aZ;08MKfiBogWLsm3sD9e8{fiYf8P&-!NLI6DfkL z<&%ag8)dsHrLVr)7^($JY}(s9X{cBTAh*{@tp09qxOU%hSAbLTFHj)@UlkL3?(^U5 zmTS1N-hgFwyZ^$m*e&_J>fO0vC<3@9I!mkEj#}8IaK3p~ArTcwh~N|wL_-u0L}O6wCs#`lb`76Husmh#vIk&?jGX+3(SO5Y z(U;^1)y0McSio2}z6HC|p04vmE$r^OYxcnCH`JYymQ4t0pYWG60ObRC3n}Sst(fT8 zaN$#MI*DhYBMyhRdg`^_Y|gI#*bPEVO}|FD0VdL`Z;|fAep$X?(*w z5g_>J^16;K9tiG;j1i9<)9}>;$C`&Y$CUjlCFPeoA!yN))1d_7>EW7}Qg&?(U9e zi;DrLh^N$tGT`0dGHWqh-&~NW|M12vD_9iqw;xkqaN!}p2a)V;E(Sy62L9sX0lfAs z8_`|jWPrT7OneC^>bIY%ay!`5e}^IN|4n+*F90PpQa~)^7Dg=L-~HH}mWBrUefT5| z6E~o7B_UgKSKUcTo@+R0&SG%h&wp*WBC*&n+bM(Y#L~8*9^eXF53af6Cqx?HosW82 zGD|GwZQI^0Xu9?@jOrBC^I48luds5eUW}-HpU<4B>5`CAp3{b@xMEHOxcV>%i-#|JbShy zcS2_XCzJ=v2HFd5(~#HXMb!Rz z$!Dr-}Vgfl?;z?JFL;H0}ll+1T9qwNOyw{C0R*3n7ol^!FF)$)AlT&19=!%G4Y9#+`qTL)>2|CB( zCyPkXZ_1m_IaT4YvGck)QAv)#0ZZyS>9Sfa#C=|n*EafiYe#oBNd!b1C^SS^NzMYp zy~m;Jvq@s}r3AdWU3SL^8|akeP_A6Fl#gkr(9~XHg&Dw^1^co*tPC|06p`Hj-Kd$*aKZr&g&9}6IQ{Ju z9Nb|ScqMQ7)G;R*x4KOg0Ln%s3h-Mr#onT*nzisQ7)Ho(>1PKj7>DTFf-DyJ^7Nix znQ_VVZ)knRM4oxv`>jnFaF5%f_BK4V1bXUCOQav+bHEjkYKLi-{lJFH5+aZ zjx9)X#XJ*=af^zZKXMD$*jh%Hi`xhF*GQa?0ga1EZy ziHR6&5Cm1Z{Ze^0rC73KE6MkWJ$#ZHwi(`*``Rt!C4|ZgNDpKID&mYo7hQ4uwo4un~ zMP9G<2vR5)dhz`Z5D{d#9Oht3)BLA9C*eB~mjuF0CO-^2;KP)ChqhFNpc@ENUA2roYD0&~=7vrfEpZk1f!3 zv5ni8bU78nso0_g^jM1$4KrbX6N%%+}X~9m~y%-9IWUe00 zlSs63DC4crZsbQes0wxKQIl8aDNrc-e(iBR!Sz9=yjA66aTSj>QCIU$Z6XZtc3A$|Nks}kxf21%b4mP3 z)--=mx+B+Vawt5$J~*km2I5U<#KciR_l;a93n^>LLe#X8rH zH0Rfv0XnwP9TP52%yBHjk756MIlT)~m2}PEUL$J*fwBE#BUmmM-)}e85h3VbgJ{_& z`b|?S8vjdTmw~`>7MRsF9mJi-E1XM);!su=7_{kA{(32Fc@|CZ7Uiyyz;gz7oC-8+ z1~d5ybTw^Xknj}r(Lt(o2~=-n+xK|BKoPrv)s1e79!H%NanW(s&*!X8(9*($E zzXnUF2yN>gwqo=XgGi?({}D4}Y79?$p#Nu+7EYc{0-U*GaqZvj@tabM(R^U?U53}) zr}Ih8`lzfJlWErUcLs0s;wX!2NJS*0*A1wabaFTCk8nt`(=?FoQ_v7lL81Y9Hd^xT}*3#=glBF$6Z3rj@Fa?L`7NX7J3IIjb0 zx^9+~?MxTUziQ9<(;g)?@w^2A`WIyF=Ndv(4a-n{Lx`2ZQbSiQ^OG?u!uWu4H1FK? zGOeu_s8*q2V{eVSO2^V?po4mP*I^K$INOek@sAuw;vnC1Um+;@{OA7+9JLB(QseHy zt_Zy>JOFIjq&2bj>>6Zj7qM>bTJHXFhKzm>2R?uBW`saY>?K8_CHmRAc6`}1iK$Tm z1iI#u|Aef6Y%P{4i`@4mNsuVP4-MG%kkj>;bW{cc$mXGEjM|+0I>KUfSAYygpM;VM zf*INCloqXOp`pOzSk7GPgY?8+M%;ZR%BSiuY$EKz{P@4d0|cQJ*h$ zkq1baqbm;Kf+8LbiRei?COI)8+FLJhxn?2TGzB4e12A|ZYg&9>b+5r(TJ&H1Qd^DB zB=#Z7*$EGX#J7NVl?6|-`0o_dl4WFOhKa}m@fzw*s2H?%Q!-qjblBfWGE|DDW?`&Q zln>&X+zfW;a|Mg^Xz+}Jo7{N`0M?bIqKmj)q^z4uPHUbvOG8%{L|A@2M%^jDx!@Wm zY(YsoTI+R>j1mr{whab1E@JMv`K3<`EnFP@_j42-yHeqq+V}ON>NtcQhsV6Z8MCyr znNVZG>m%AtV#60H>IAy2FTEbLtT|*C8}Yo5m|j?p_OIRWZyHX9BKh=5t!F^cN+#2P zl2C#Wpro=Q7W_f=PoL<`N@f4Dl7G#*YL4*ugfTIMnXAlMF;E7~nlZm(Knn|yYcfk0 za3iQIH|qfI1U4Y-F)rZ>cd$l-%-gmge-ES611{Rk{al%Ox!$=)mL~p2V z3lYy#Bc;i{6$o}=kS%tR_bn|!zLdun*+UL<2BSNZc- zN|TN94VUacF@lcEKVZ`A$XTA1+{yY+4%xxO#wbAL2XBTNQj$aad~fm=CB^F0IX7P6 z3=Qe~^Pi+gTTrGa{Z8WBvppI&Ku~7OJQVUbGEVuNIS>fbqw_nKDNqafCy9_=p^4@v z26TUXkm_uhQroR+}r%uzZ)}mBUER z$guVM<^NJMPvZ?L+B`|noS_IHI#RNtQ(~a=VPQya*?j5GXd^tg&yqxV-e9X6D@pSB z)XZiOqZ`c^OvsiZi^r@F1-uC$1R|i}e@&4k;PE;i{tQ6G`~)QKp@e`UB7}N%+`rHd zte>7Ca!O75y!&~0snu`f@_X;8o6hRa_MM6m6?x;yxbIy3XE!1di%22!r8?4BuH!P$ zCF~~nPm4fYGRr!M@xx0{cR~0p2Z!{ZkEULHU~=_TK{8umOEu84c?-;4`YJKNt~Mu_ zTU7vZ*o*L5;|`VTDd|u;B9#L9{01U zRU%P7xq@*`vg`Z}AlUj^ot<`Az*_vE!4oHA`^GAv- zBxU|a_xp|?f;vh5gE})~b+j1C_9Unv$$0YBmrO&Se)CEa67Cja4`@O)vKMKD$PK}> zAC)pdi%(-v6%Pd`WHCUFE^Fj5CBPDQ;s|@GfG=TQ^{tIE4*C?4n)%7i9>(LM3k=lp z>yp5$*eM${nLz3!d03~VZ4)$&&X-Cl2ue4CM}u2eu`4!)J5S}RwrHCdT>M1jff^V0t^UqJr7+ zWSPje`3!FJcXMey5XOI*ARP!)Sp?AJf%+TTt5=~wRY~A?&0@K8E0cf#F_76B=F}*g zIUor+TM;8iA2dCu=-ohqbkBh>>R6#X$(;ju{PGaD4oOK#$Ra!(0oqhh5_#xt50?Z$ zJx@3Ld~o50!Dj#1u)V~#eMU4qGtcnT_6X(S(-G2R-5Ou>!xFmg5H3247d7@b`cLGU zA({N*En9v-%FPip4kdMgA7p8iW8-Z2sNBw9hWw_;Kh{ zBWqSX{VmnXg-WNhO(W;=I2vo%b~!8HkC$8gNjR(=?5aO!aJ`D=`BSnsyH-`C zu-5;rG9=7TU?C!Y)9Neb7U^4UWEc5fcjE#|v(9YJPR>5eE%KJ^aY+ZQJWCUWZ~0%` zxiXQVkP@mZI^X2(^a=J`&|PRy;YH#A;%B#N2;qgQ(7}Q0%R|pL?e;T&PJ`R4}ni0}*(%XIAJNzI<0 zn^YfQM`=dN2qqkmOdL+*l2%)((9k2 zGj|4e!z-uVW_mfgEkS+#!#z2j@XXe?V)qqmNzg4n-~FsWhxv472wiKvq>(%G)$i`W zm9gBX>c%C_@K zCE8nl_-pJ*%bz8hMZnr z4zo=!EfIk!6ds9a;z*!Ru`QLnQm8_EwiZlcIF>6u+vY6@B!h#2`Aja5%)bVnCr2B! zYGpv5*p%;W0nh>J=ohcL`I4O@wP81DWUcCTvqedh!WK>jeP=}SgJauw~oj_8u66hm- z&CkyP;=hniUm+jx3=Dyg z5&-_NVGlrf)qK8s59W#n{BR(Y;p|FsGVs=TBV#BlSLz1eT&tlSrWaHVegh@w+Paa6 z8FZ_ce`KjA?^^)aA#Bkm(DkEvF5J#8&{LR<|ZIU3Z3vY9O-$pF(3HW+;a(CNjNgu&3E*+FXzlI zxJL^PT`M1xU(Jd`D9hTf-#kz(u_>#D!kb85qGPYAp|sCoka?ehIpsU^?hx~4@>s2=k4S5MdLbkgyOjRlM?NT zmkE|UHYsu=Su?usC_wwT2SoEPF)_YiYx5Agw{>WfqGf}`0L$|P6i4lV6z&BO!2HRA z2+DFiW+))LTlr=%Qi1{qMM4VXZ!ESblN7ShuPBDX@+gAW3& zR&%W1_1Yjw0?aFR04dNTORKpbupm1J&sp_=pc4buAuzl+T&Q~q#I0ezq)CDR(=cIt zC-#V+g^@~N|+9D<`jRaJY!+_h82k=b+83}&6OeY+;XTfX! zcv1tdc0do(7swogqca!BO9bFss@vLsY8-;2HNd>&^z7{OYBNxq1CoRityjMv^b$L* z*o~A*d~c#)q1u#gPE5S^Pp#uizxoE}?rQP~(pJqyfS*RlZT}n+K7mx~mxKh^@b1_r zV{&+7)>rY;uSG;eAUSn*Rz03}t3K`pH-(VUrezr_piGI;{|RhD`a)=zB(vcc7jKJH zEP^*8Bq$2rd%s*M*9#}$1f{y-tE@pyAN(nYtLan$2Ub%LW>}$Pra=G!I!D_nM%zyG z@3?PK4t+rppRGEi?$L5fNBMfGr`@v`^1TO~UcuZBf*mPUg{y08p;1IUM%x$ihwj|- z;C$V+K2jp%6Jc>NO%b66EWi5&!zg>%$~Dn8)AjShk@SG1X#$<5i>`pW6@L}lz#-Gl z4*4C!{-mt+{TezDzS>KOn)JuJ_P+ERe_k)sDtr_ze~MHTtuWkDg7OKbt=l(UoaK^D zy>9KaT)ytSx>;s2t8DvoDH&`@%;N!d@}4;QdZU*#u79+`%SblumdtM;0BhGvk+E3P zpKj+uvcdjYaXOLq;p|0rYiR!jI8eGH_1Es|R2aT&c%$Ym0>wXq-E|Bch($sY@ z^GkjK00P*K2!l!V}DTghA$QgRS9v61#3Q z`9B2Q!vql-Aol*q$afgl=Ex@FgFYBS^jTof!ddHuc)$5uL!gH|`@;kosOFQQA)sNc z`u8EVE4Vf;tU!reaJ`VA0Ls#zE(d8K;1#%$yfZeY1Y-9=z|86IOv1(F-(d+A=)tTe zya)I{V&;KRlSR77EG+C}-%PaL7OhZ^)GxU^laljcv^_Qx7*^ut^}GJ7kWI+RrwfBS1sj>yc_z+S3F@;`z|T znd+bS=zWQIBxt#F>Mr68UOSghf`P)JPmhER-`3y5Eg3B`z&u>4Bk+WG9J#t})vm>} zZDof(k$*rqn6?Q5!I0BZRsSlJ$ox@6#^dq^S~dkIcm#)`Q--Owr}8>)ccP78e0<_` zt|K3|@$tq1x6_J+*DCt4!ZIqPDE;KY9;YdfodE}#$Jxf;M#s%?Xoi3k4WYu}%K5u` z1J+3kFT={6_|xbwy0zVtSEIM;pcbacj=+eSddNPD5j{9SwH9#S#fUked~He&kdFP2 z_m`@hL4<5CKxKhl;zseKaRX_0NsGx4oBYDK zaP}Z=TTH$3T-vINHN;xAKp*!G%@7fjM8$)WimGE`f=&M5&*wogg z057h0i7A?8UK$n}a*J$cG{=b0Ilflc55mi0tMF1uUBQ0uM0xJ%~ zRk@9ol`O#8VD0Xtt#^7l5iFNK4bn-|UeO=n;o+NGS~MyRVL@6k0Tx5ox3`AI#$AB_ z@<|Ur<+NQ^!5z#<|9z!P#A=KHe4va0Zh_>y1Zi;~j9s&Tx84BEiMoKu8GzhKz^4jo zKjmN~o?b)g;h~B^5fcLgGU)|ivHr#Gn3%Y@BCtLJR)2Ce7E`|o?|{Q3b%M0E4sxwi z2k_!hC|0QlzL(Mbbj>Rb4)1iBM;M&@JdS~sa=i6nU!SBCy1<+0^+&_~;RMIhxmby^ z8>Y?te6v$Z0qzC64Ggykz1fHo+}wH(p4gU!@+zZ^tXbZKpQUb6i>Q(F}{CzsH{my zFw;$_f0xAk)$V?lT=@Mq7Kk{~edxAv1jEW#w*_{Kt){ zD3pWR+>#PxpQ<=y2SMGUOiyMWt^hX_z6LM;^f2tD9}yp|j3ZnYP%n132VByo&1jMr z=I1L{C-V30*jQPUd5oZm7F?*f%=w@a6IJOoE7EaF30S=jDYerFm|d2wf>2PDn6OW# z=F_s#jgnaz-p*Lc$jh7E4oY1d@P7H}6SKdO_b#xgE$iXzE!;KyX{-EpY+@unhspj| zjTcC#6pr^;gfa=kSAW`eyDhyGqczR5S3IWtOu^7ry@8#am(4g@16Uh$RuOMIU2bwcxgeluuH6_0?>H#;cCSUSP&d8HL39rEX~cmA|fIJ9u$>7 zemy*VA6h{iDba3V1?zunfT_(G%yI)Wi8nJ(6c#*cpu-5n>CM3SYBLba1`np<)0zl} z&0>)gJ(zMU1eLT`yhTssxZQ-dkAE z*4eJ8t~>{wgEB8!0s#6pz^<8#0w$lSAc$|zzVUxSb)p_x?8`!t>O8T}cau|C-qljT zaLjHkB{Q(vL&BmhfZ5*<<8nhs`z*`rgYFGCyOgxF;d1lmfWSZl>yN|9IjwKY^!QiG zHOQ*-6{xB8YTs@RBo=5Ds(xQ+j>~8xS_2y75ro{aAoJlmhje!|klag3dB}EevsF4A z0MZ(PEIeX69sMQP5sWfTn(PPJ zDzw}#$MkTwaKM3BxzKs~rO?6DRP+7T5pP{cn44G})sTb7{aPnH46iX+B@63xR?&=OTauyfpn@U zCNVKF0bTd5SScTXYd8RYAlmwnqsrWuz*&TSdV2c$`dR_}*l#NR^cFna++#VS@LgTTp?qVYIBK<6kM8-fXnWf~0JNA9UcA@@#x@>6x)>Ipf{e@;&=!M(gW%d5 zpt2pz64c}8xjGolXCj| z_b=xtu#c*MEd}A-cflLBcQ)x}r6a!3i}aFFJ(>k8*5I?yPN=T&rKh=kOoem|oMKoh}I))CwdjeIW!bW6(w5@%#% z%9d6r$k)jhI8Ig{Gt}2f%$Tjr(P)ZkH0E2b*9_gqG7vk3D?n6CR;_Bpa&-FHGXh ztz>R@Y2OW8k>=02!~$E6v;0v<4V}J3oU<)|Xn8sA`=44HyXppp?QTasBOjtqS~?4j zP=AkzDBQic_<}bkJ=m)T3EFE6k+UkCMxNC6S!vJxsbACP*I64{u zjb6Kc2M|m_j%)yxVXbF9(94$rk=lHs00r1=Q-Yr%;Cb7uQc1`f-U3Jvh}w4VtNHm) zc6J7Un}9H`z#-bt&u@28<1Qr#DDk(0%>q`AzJWnI0N6JGrZWTU5{p9+o$mt zY!dDcYYDu&KJBlz49+bfrg+B2O-RbgzNTS`+(#ii9cLE3yWKMPT6V-v$V3I!2p1?H zcK7HM_h?z(stOZxl7A?+4tIbJ_f8^zDr{5AUm6n4*BPQdq7}C$6Dt6okq{5@KO>OF zO-BbbSQb#A)=DUi4h@w`|C&jr8@uZ1s$?81*ol z4kiO2%3hn2@(Qp#2EgrWj@WWE`&kl`-mv{)cQcTCtpqM)(7mY4VD$pp0(d|>3={yC zhz;13S#dTNX%8Wf_L}7_@K1Qsq_alJ(tJUxKi`}YNWhn>)zm}CY9%ZYkr8&ZM}wRPZMkwsZpOyO%-&<#QCVE6W@8#aB=2LK8JpcNuED{fkMs51 zV2`HrKJkVp4Vx`5c5`vXlLNg0(~*=Yvl{%jHk0ucqVUOW%sVfcvPj&=1NI{Di9$*& zKMU-RXMh=m*11#cDSpfG^`+?u65h~$P|Q4&S*keth==pE(|wjbM=A_Qp^ca3we=8aANae2Gt% zUfeK_#ZUr}hvGGNh!irmW! zKExX}<-^yZJ?=85-J9l&D?X~NZ4JP~l*EqG1qzOc+ub-)^0qz1k+inT?)@;WpQ4r$ zWil&Gtjy%JB?fjluYZ<@V-1A+2vfXqSo@9L;k@PL(6=B`JZZ4`^H@ycq5Ip1l249~ zUm?v4B2|{`pY3^XUeh_Q_KbZ_R&T;~77{@?Q;qfeC%hcac_t^6B(2S~y>U5NYX3E! zx<6(4`(VUi^1Gtef!%cZf{4PY0&OF&fxf=vc>%-q=u9-`o9L~%x~t%aiXT5r+jASP zUtXi}dwTMHQ!(28f)3ULH0dXEH2FubjxRrf-UMN|yUEJ}+0_0%#b@}v$1^UZ1qG(` z>Rd^PaXtE4<54y`?qe0gm{4#pUr;hIL>5no?Cq=|TUrtPWNFWl&E%q(xt!mmD~GI#uF;RxXBO}J0`!Noo`^&#DE4P8f7TB1+9QjH z7}D&oDcjt?5*6{-US25fPw771^bT9eA4r+#8+bkQm?Zb^hsji+RWHhm54-y`#hN2> zM(vB!V*x?B!{Fa6Z^wstlov?Plc})zz87a3c#s9S#(~`6=i>tfavBQA0m zJnbB2R@M%Xoq!8Ew2scu6HZ9d)m=-;cY)t9WTkhjR>ez?yMf)K>bN7fm#))wZ(eg} zSSIB*IP%2O?GTSqN0-WyI!N7uKI43yjo1PXimvxhky3Ac>a`i$s*}QUWZro4_ww>d zATmTGGiQO`(HZDA80nL8nztg+K48S#vNlw7q5&9_NS>EX5QYSb&zoxd1r6YudU~=M z+7P;JvIlDUk7JABzK_07ijE>7>)WSe{)2kg=xk=gT@w>k6`h|qfRd~6@T8n91^a7< zf?8?$;8=W9sTI`dt* z=#*M+$=%40O*rX`i}?6rbKz2|sK0o5%}$ow4UYCAD{YAZ;H;2bCK~oYndj~SNo>*v-cjRX) zS5jI|WgrAsdAY4YaNZ@QKnleUiN3}6cb?I?p8k;_nZ75_-Oy;-fUZ^T@tCcZHVBTR zNHJgpR|ho=OG0|Q#AK(dQtZT&hALk|{N`IFCDyjO3YM$?Id)#T zPS{#kgC3Y6(H~S;;mlR{y6DpAd4Chnw!>(kpDjNTo&4mf3-9a|;Icm!v{g&ZC{4unBZgI?2EC)qJ^8V`R=p|G~GpCO?Bop@1xpiSDT z;>IjiuKK2WL${AVT`Dp4YiqXAi4L?xW@l%yoF#1o3t48Ee>gH6G6S+i{h!zbmTn*= z18KfB{%QrvCdJSd;a^#gNGv@B^FTxCrsnk>twgCryMx?R~sBez>=l|~*s zyl>Pe2BnqyHb%CxC5&5R6C?D_2L#5$r_)m(a!bm)>I1e7tBqcHqxNy?@flr=RWr|c|58BavOWkk6*-By^9c)pF_wBu$Uk=)yf}*0< z6E@2${v0i5#$fJL0nabDt7DznH7xgHDS58nCZdCfmP*B2+H(im2xPioBO!yQ4VEXY zQ{`<|hZES_w{WzF8Pc2cJMBkpH%?ky#WWu&`RcGfD;eTTAr{xG)_!$S7}CD$<&$3R zE3acUGdrN0a<@I$qp7TB=^9iz_wqHN)kM3n%L8fFiOtMqCpfT%tcIQb^WSY3qgRP& zN7&}1s6*);ix-{uS!%yQ#o&`D$$r9_klr8XQZ$Ey<*cr*uF)z7WUSQV0(x>{!B7ew zs>IsTRQ-#UjNaWO|14ZIm0SIDwC^kOLApqPL9d^ z)?j|=jNB)IUrY(DG&N06U0X|e+|7*w`QN-GZwlhZ*ytZQu@T9-yoLX=Ct#(|jw|kB zV8(RR$c$0GE_mQo<|V6gwYDfLw|jY*ruMjA^0`n-N^aonj5UFNkF!9v>>)uKR)VT= zcOT!FQT%a4)|wyYHF#693>WM@dbP2y+Z_ljT`IW&63?-ve%gW{Ke4q&9H*p*H8U=^ z4Zb%vdT`m_{OP>@@G6YtBPAUD!m3VQREsKepD!zNZddZXU4-%VVjA_<*~)UV8iR0?rOw#b)*F|+1E=st-44Vby|Ox3kGjpSJk#IH2a3e_59w+U(l?%IzJJ2bi{gE zXe<#jy1FQ)9Taq=z!wsW$04;Ue$HnH_@&^V^U z#b&om95%gPeL2^1g_hR3(hcJ9D^7bTCc66Ga z?PMw(>#55-Wuf3DCo&$cg8Pkp{g6vkO(YgOy!Bi1y>khqtw*YlB%oI)e_u#mWSa$ zbU-EIf8Ses@-3Q#+YT;J#J5R{JM!Tib0jUd+MfaH>STp!Q*v2=)>E0bFxwIzw-JBcM0dV?rbuEH`q-$&= z7!hxK1oZf?4W~HE@0-QDYoDwOWN$caKk_KPH8%cgpdtbP>6^~$mQV|%7$b5%Iq-Y z*`U^sAfmRzH;j)of=@U->49|TQxc#AoQr$MV)N^b)yCKQC67 z{Sivn84lL_5-d3zo|u(3YUJ@Tf5s-PoG#U7+!NuB_;$WQNa2ZY<>xI)GV;d0aaZPZ zU+U@Y#kHrjWil!WtFP0Ei28BOD9#js_5}Mk8Byc#IA0q6TourW$`EkCFk*w9DGO!; z+q+e>Db?TLj)g;6->N`={{m5H8{?2clXK8uQnpu~uzR41#yQHgKE7(JQ$VM|>CQBf z!~ODDLS8S+WuFt8H7Vorw@pt#ztPz#eQ-i4*-5R%&xUhg$tWW&{i|vy+(=M9N5NPN z^;mqqYkg-WoZ~2!?Xv|!88#8MLC)rQJ~AkcPv>H9R7!P;9W42OeW2~W9#sVGyKfFj zTDs@^cRcxpg=|i@&ws_&b#_Q(NxZkLGMrM17{bzS_#NE1lxV#&Rw2beFN3ep7{N~A z7xV@EVqlsulgkAvf3vf%Ccb{w0hD9rmdAY5xuKWYaAxjs8o1G#{SO9zQb(p9-fpR- zotuIJ-Qg1JmuiLD&f0Ee=jUzt2}@mN+0QL6v-%I5%7@(Lz+yxbxxdQYAgOw|7xt@T z_&S#>{9=|lTHqGBapQY)d{gKeh;F%ME?2pZOzUpc{k8hO_B<(!9l`}Yi+lHenUBvHux0C2{Y4$=&pte*5>qR2>03>twy+8rB8 zg0$NdsApaDU+R_W41GnMm^$*hwj)@!<^>&xsb18eqt2fBAU$VdD1B{7v?HF{&5pTG zvhRngZO-p!o4g)FPdIE>^X9-PjSVH)29?9{y~h3R)nq+o6UPqsr%y~K1C1E9CzO|$ zDKZL7I;|na6QUZmo5=Ikm)nDL$`^Yx{e^soL`*_r^#9TI)lpTjUG^w1pdhG(NWVyT zcPR)+3rLrMbaUyt0wNvKol+8)?(XhR>F&5RzX!i}X1=0ea- zgM($tTn3*$oFczTCIPY}p*NPr_uD4tzA5X+tqGgCcDehio?pcdM(gw#c4Z3C`1;%= zqy~RWxY_FZf_OMQoaP$iV0SRWILRrQ8O}2*#%46LN#BVA-$^z|jKdOVg)DzKD>lgE z`V1->8dr^iSp+-tYs+YZkrM(~bsWts7vrFV+vQEJnujtqb`ar+?+=lyRo;mjoRlwo z>zkj&#dcG+Q%(ZJ{Zwdp1Th6x!hMLo1LEGqYM>zSA#Rd79j^jncDT%P{1qCSR9`=X z6U4-yi({V{aqmM63f9vMRUgCE^Jsyi4($BMvYH=T;ijW9eq ztKpcdcNAaY(aAdKjj#NS(F&$Eb>tCIQc@U?m!M~eg(wLBtVS+w;1S@h%vT@^ z)Lb6VXPx20lDHCdMZ6C!XF*#Qrc?PjrE%NqV12Wbc&J*ucIbWk_`W#k?%hQf5=v%s35H|DF7%68Awj4WVWg{q7J(P%L+R-1KU%ek7~ zMVq6W(-XlgkGj}D!JKk3EBM75N2qwR(LE;D-5i#%=}1HihB)9G>Hk$=8v~AB0+#H) zxzOt?dtjfo#20Kbk&l!~NH}hRK_vKAPxx_vLV4dvWk~`bSe-GUgx3kbwX0Ea8IvY! zWGwX&-A>L73t%D%sBeGn4;oIVi1@*Phn zf2pBlDs)#vjBIQe5x)Yq($4O#zH?|~1oi7??50~V2*r=~7iwF_wy=P!^!dvl&&PYG zl+F(Oj%JRr8a*2xfpa#NtHmUh;9|cx%w53=yNt=)I=j$G)cF&Lzt}TuGhVk2TAy|} z%g!}Cd#i+^a^rj4<>|`6ye~H&5A#?fmHzkZjlAF& zgKbnltr_`Iu|%}^d%DpD5GXnKt12&pgy7$759W9^34&U4C*ESC8Z0FFyjW&z3yCGj z%~{d33JMBUj`xK%dnfJ3xNAm`M2NTWsf7O z$Jq=}SLz;u%F#Q-%aheXe)kK$n9zeSEt{ljh9PF7Vtz)+7S%6eQZF61EB%GjyuHPr zHj>X!>@PV;$Y=x~yr?+^ds#{;ncfgAJ;^p`uZSo+RF%}cW0!5UgEP}!rhdni4Z z6MII|KOrM@G@S43#_x#S-5qzmU~zxs%3F32SumK{i|ZeFt#J#{bR)!N(rTJ72$-<2 zCE#l;^IsEna5z_0SGQT6N}~rFi;x$XXWZXU)WrjvId>=8-6LY=s{Fm0yP^h|c4z6^ zdf)+>nBb(pHCF;eaVe}B5wVu2*X&zgqyeM`%FX&Pq| zCl?kTJ!m;8T~|_vfh!wgqh^AY@Nmupk)i#woUI!W3BSE~bHCpqR&u#Z{FtjhbLr;M zv&CbY7~+Md+4@I1m$HTjhX#G-Lb1wG+j{9%pQe^T?c(I~3EA+IF=w!g`}6b>uC=L2 z2A=zsw5Nveg>6%x~lP96<*`srH}hHbj74au?2(Z^GuMx+DxPkP4D<2 z9lw*c&W;U}Hox5BodA!+&66X!75|{1-(5eL6D9dFva>sLN2&b$-27C`4$R$GmhTL$ z-wN$7Hgl;*i&wwgOiARc7Jp-yZ6IUZ zw|e-0kIP=Txt=L8RrY&X`ilWON;a_*1q7b6bJ>STP}OwDk)rNc)hZA*B2 z{-6-a)4NUeh8~)!Y;iTk07+M&z|>w0DOmsg&(z-}3mo2ypQ0DML5)0xi-X4nBo=<1}L*Zh3eqoc1+GmkUAV}Qv*ueRtRk*o^r>>{FJE14>B81P?xpLvHP+(v)nb+6;^d^ z^yo>WN1&ic$8epy6(h-_f2NN^qvHAVXB0PA7U#F8XFJc%%AEyK;Kc8lX-~=OgkCqr zAug^c4|8-n@T}1SRfLfQW99IOtuIONdj&(n=PzHHeykYb3D{svS}o|V;GUqZ;rlo# znbXz#T4cge`JRC*%8HH0RN9>Ra0H*zRVGnzGCsYRO^!`it6t9{Ar#C>~bpw&Njeg~Mr~30_U4}P-q*O8ZQgvL23f}%%l}g_ zc=-@yoYqFzMlQ*gUtPnJ4WSn%j6!w($&xYKAvnegg~*8}EAW z?d=7toLv2NexLgGwo~TvTRYuEZeSC_KV0?%eUeNHaZ))xRi&3RxB>4)@7oI*^3bps zp`_V_bJcJJDtK033K4jtpdMlY*DG9SP-~s)hVT?Sq9qesstrWM#v z77!K$#Fp3AL_dFCf_j|%n^^Gt`VVqx6Z&_tfD_Ijj21!P{lHq*ePg3yY_xk<%iO`Z z&!|F+s=Lemt3}sYZohx#-+fso|Jvw32K=a};63 zn92<6TGBUq&2Gz@*SOGkC(=3I8zmQ8{C1v`hsQ^v^p!%IAzjd(K<@SaHKCBb?5&fS z81hMs2k6)+7%H{N{*);egnH}O)<$lhppEEdvT!F7ys}CzAdm!VtIsNFkFF^4KGw!P zmr^MZ2L+c~YFqJB(NNz) zcX8_zhlzrkhS`b;bs0YW&pV#>(>2fVIDEdfLE%36(%5&1-&r>@kenS~{Yh-;e${~8WD2n~YWl9muE8E#e6P_rKlKD)tIzt2al3?d zVG%~|shcF_ zZN{n=F4lt8U6i;i6K7jVs2m=YnfyDK(whEl7}sd|!4o&$vh{^E zZ^Ee~bl;F@)ajmkt^$HACK?onP< z)FAvUV0mw8t$J#~!ZsG?|Uz^!sdyhTX26g#>wZ`;Mv`;bc<*bJ; zS};VhSry%JG0X$kj?)%dm=)5ja$1@``gqE(r&MH0Q!9zp3K0FNFYf64X(R}1oqs$; zMyEacm(b47CSN4IOU5s%Rz5D!ta$o2$m{yj1mu`;S+d#Q(3|Hom>i4~0to~0%FduY zW|2kE_-_F3raI-Ostiz41_<-UV;N;LRWIbJd3obbV8UYPEv35REwIIwf^@pkb$ZgK zMk=5}Ju;pt!T_Sm67GwZ$Rp24Mg!P5H&0xtHATkvtKoq>JOm4MPD4O)1tlCyL^~YG zVLfEodvil|uX>?EaQ%!5AUM|-{xwz}0v?#+cGC0~8XWQm1P7yO9AKLe<4O;y{x3=4 zEuN7$!mfFN+SL}eojsdS0~b4UYCw- zj4^p^2Wl&Rx){SLC6`b%A&ljTZiA}w#6aiV8Xux)5OX-&cXZzW!4ag%5z)!t{Npf* zGQFsZCCKMPsaQy(+P-09rerN&hIn^)U1lo2B%HEgD{Zp;+0yiZtMvEXU`%}}&A?j! zeJdR%*xmOw_FSCd8C*<7_uDifb5(CmU1+1swGJJu-bNgA9NOi7eN6M%KT$x3zJkF- zW)GkE=Dbx!Rn72D$NqDQW#5$@kI-#N3Wi1e$@upJd0P9;%XcUR9ZUz?>5R6B+cp{Z z0c*{z;_o}#MpCtd>D}=s@+3eo!e#Q!LtBsU=m;4}f6aU&@bS5-ZBI9uGv%?)vDWH_(`-E=s8 zi;tUk>XG%%fIG3+d$6}A(H{1u%A&brOS_l7+g=Wt#|EW1rME7ZhnMleiQMkVGaURE zciB45H{>A;0=)I%M}gi(8jOB1*=ELn9DqvWF}=0o5OWJO!e4e7dM9z4cro<5hkzB2 zqVu??ecXM^#Grqobq6oVmo`ydh>aX|d}FN&fgjzJYgRASKhI zbvu#ACJq&cc5TXV%I}?Kjhm+q6yrKH<2oh<$^uE+})Gy_qc8FNT4Swr+$mzwvt@0cf2= zTvwGtmd5#kz%9LTyk7OGh1VaAVeNn`YxDX2|`Rxjo8R35t(TcVYlIUTmw z^gERsfAiKdJUTY$#C=0WVV0_m=``M8W^g0fJ+H?h+1tD*M?;y7>+_fGvD#k3vD>D9ytJ2AhF&m!- z2(xXG?o(%G2408qR@`IHu?6n3gkpZEJNwr5txkR_ItVA{;42AqkHZ7xpvuSXIaTr##wkIq_U?R>f$2iY4qsfh(G?n5a$!) zQ^Eg4oY+?WVrOPc^3{(YH7f!w5?N`U&d|9oKafnL^1cOxq;0j{nRs@}uV7ZaJb3&7 z%vJA;gT(xoWWkhEOon`Txl@I&3&U6{xo+6zdSzpjGCAIW>IFePTbRUvwz}ii?O0?;bfgjf;4lKvU%) zK-i^;Q72Qo{$NHm<)HnnZzLVDZqMVq%^g-fcBW#c0%0~6>M%qFI)`-9-9gErMuoe0 zD`j~Ry~*1x)`_4e_`nh=9L){n4B=_ibJeNM(Hs+t5MsVWBSz36_C9dpe&NSGtBq+y z-W)jv1&i*nDnEa(u7O!sZ2`XZ{!L@=Qh%a=sJkGYhKT8SUduK|iV=ZPZ^eP}NFC#Z zgV?A1-L^=RwC#;c(cxkFGUNF$%Kn|>P5Gb7&I5EpgU27kHr~K4%DWGG`Jpqb+>Nl9 z4VY9yooq6`mzgMvq$)KqEkFL>JDzb8@DMBiHUUR}jTzCPtbG6fx}E3MR$1gcn@_qtorA5h1DxI0XuFf6vpnwv|S&ag1c4?h0AorMWuYdAGZw8qJ`c1Un(-u8Qfg+`lO~ zL8z&zc{q@v+Jc(=j!B|u(Gl^{@hb|`4}uUdEJ`Qw2a;6WN`LwCSN&}bo8H(9L7f`p zuY<1!S4t^P)asYb;@K_5rqU2k_ZCavUeXvRGDYM8NwTQ8VkssG1pySw?`uOCs;xN* z6&2ZpJLUd&6$YVbJ8%tHW4nS(AX8;WD8TS@Y1pbl2>#A!hPsl(1Jer}UPqHa@hH=L z?P?4W5s`x9L=_J^dwaAS%~hPnic5yglWds~NVtmNal~Yaf!5PmM@&~&6u?_r(-=vG zS~i%gR^s>wH#Z=1>cYY5c%x_JN$0CR)7Y4 zn@#icr!nLA`wE7{q7*Ao0I(WW5Ew90SQ-BmUi=uN9xN%JX1ed=m2E@wGu!C%=N#co zI`|bpN7OZiqVopO3%rm8?m_?Am`6F=zXNn`F~%Ud!UnahK7?sblnesiR7c%DL>+Wz z!;zl_<1zDU-N14!sko!;7QPpo;Z1~B4E+WDH~q)O(xRi= zKO_W$jVk2JdfSg}{a$4&(6O$g_18qCH^jFxuW^!71r!gD+-q1<cnt?P~iK20L0{}qaGaBYW}D&LwVox~pa?vcm0kQ6Wd+!6JZ$?0yN zY7(aR;#8$gZ2zs7&vrNBzllDNipt9LhP~Dv;?hpp=~uQ3r%wgMIH+a&7%jYidv`p2Zv7Kmy4o&63y5DO%J-TyZQttaFI=Yt%1dX3f-{Hg%h` zp+1N_YI?tzQ`Cz6Ch%~l^1{4*@-L**eu$xiyzSDlQp0==VxzX8s)m~N@1dvvDOmC1 z3S*;BhPt(69BNgS-~7eqtL{S-@16LnmiD}3S;(HjirciNk^@8qxA&2Bkc>4<(+4eP0MQ!0K$In$(sXz0 z3OMl(pc@cDNr2z8k6%<&AYCG29Ur!LbHDnNL%*(fcfB!pif(NuJZuz;&m&BEKyzdotlc=<$?A-GHtx0 z-8grv0Pivl$Z?wlh64dy%nEBxtx*roDBAX{wZ` zSe#Daep90eY5>jN=-%?ZK%X!`C-Ha6$;k~m>|Um)S0o3sQ8$IYdo(T7^q^lUAFYbN zTXxI(yLv=ya{u6gOa?Y`YZ}E@`egbYz9D*WD->}v_yan2y}EN6f9clO?ABk8ij1VU7ZtO zoCcnrrGe`2y8^c!#Y*R`KD}!pQAhhF0aBa{%y=X6y~{-o5235Sds*BOmDZ1sztgHi zKPCq#LGBMXmXaDJq(8Jb9WDxf?_fof1cp{@lP#0PT9N^<3Db^e?2{P>x`xKWT1Ji? z<=Zk5wj4z)EKIx1K0`gzMfW%kPmr=c(c+P?hXFwijSpMC4F^25y1<_0kUVxa-`^_K zuratO{N%chw-8;#`WFv^Pe^3Ow-=%@H|P`qV-^hMxmtC|J)v|OyC5W~EG_LE83`ck z#26l^&A0-Oe29Of#;&TaMg+diZXIa*mo3+|9+Jxtag|Idb@hpQXi}19QALIB%fsv!mGCxXBM-RDC6O9UH!Qj$%>b>uQVpg+03L%!&{a2q5{jq86SixL)I_+Gq&$Q zPmy#ybJDn(pb^;Rhkpe@uvR|WXCXWJ?28)^i9a@M`U z@pWBdvXF(X<_o5X$w1t}(MqTsh~L6DqfVfIo7O%x%wVU1C3zw$5?f)$A&0s06jZE2DD--rzmbA0 zE=kWce|;#{e1Z;4|9|;7i=M@}wfQiHE-osE^Kcn=xz9~)$8=Mq8&Qrk}}d% z9G~s|(gSRMGnODF4-bWoMH$cJiSsZJ05s2{rDKb~0;3;{>!mO@YgF=6oOqcG&wGiW z0@_2zw(1rPi6^AC6pC1&^6-N`BYyn>@Xw0bJ|lyTxIm8v=9b`YETdnu|Cd4-=9ZKP zRN58{l2k)2M`EE618HmO2&#cp`2BS4LDjGAd(RaZn&$rFFVEj0wV`|E_`7EEj5LIp zb3@i875m~$)+(e+cN5|kqo-M+=mhE^gjH{mR8p&u-hVl8JvexNO4(iYyD9{Ja&-6- zo;bLX2gek+PYG=r=^CCUcR+dgMw^dl^JFi zYz3nv+i_DodIR-+kDX#BZVqp)4Cjp?pUz$7wQnjRP%Gsb_+X-|CSFuM>};DO)hvc8 zj)g99bFP0(78?D*s~3-g!@W;xi+NL?v( z(_pc|^U^*uG8zN*`!}wR#zIK$Nhq;2IaMh_xw2YW%dN`+oFH#Lsv?{nQvpPuwDN8-O-0Q)_+7H?HbxlK$+%L{*Ilu>`293l) zpGG@$(RqHCWj)bsDbf1Xz*CgJxwt~LT*;g*r^p8qYnwJm7&&%azBz@NB;7pSKf|*& zntcY8=%k;_3tNUc4>UE6G8F1J*2gb)irPH9KSA8VwX;yq7wWj#lJj!q;Gt{Dw}0QS zQMJO26pURUBPVYgS$d+&;#@s>U9mXUDZo|x;=V?9N{Qz7_M#V#0olG#vMoiEniDcN zRlM2ej^Y(EousfWK@EtCoHUNQKZR5(>KSBK?d@O*KY=7lHZ+}AQNLNvn1=3ii)H-y ze1I@BVJaPK_4k>;Ej>|smG~(#nHmq0ss_!F$$lsbS&id?Yz#&}o={y%rbsmdAh8YB zd{YuI*AY$g5{giv0|;vjxnkE1ep5 zCkdzdZs+fHI^G#co&3!t@s)6C&d^lD_opj^P%jCwxlN1uSi?-7}J&-m)`yb99gH#vgGz-m!wkc2st6xh5%C z*}V*`s^&g1PucHtJu?{FPWQb)4tlmRvPRw#|NXGhp5TLYjpRwE@D$F=r(au&F%Z~V z#ZT7r{i8z6dMoDbjIj(QzaiS0E@FB&H>J#yy*`wdEA99c9UN@$uk`hslO5h8=UC+@ zx;){3`ePrKMm~#xVw!Gwdib7vE?8_uh;9ycKUtqhe7Lc!Q0>Op8kuwNcyML|NmxTOx6#A`=w4=)jy;C?>{}%{$JAwt>5~&Q>pVO$&?Nzu4v%LQPn+;6pXSp2s?YiA zbXU_{7JM#Jl0Y_v$LjnwWZ>+e>Rgmq7aU49=eZ#i5BusDj*@+Y87%mxV_Q`S8U=Zo ztuKZ7y~zLRiUUWeP=OlT*iMq2_e2PLpX--y@g%bMkGe!*+Y+>HjtpN{@MyL~Oi5QS z&EH(&a@w+wkS>u&@BB|RmbTTdwnt=kiyt>`DjV=NA$Spxkd%?xioN{9p7xHANiHQcA&*U<#x7Mh zg6cJ2y3pYH7RK9Jd-=9tWs^TrmT0d z6V4khI@(|eLW3rW40i(Rrav9!()Q$Io~4CMhH`G#KsSq75YGlJ=OCG1U7Z_3^7h0J zHN}ok+gIf2z3YmiD}my3h*V%%QLgzl*$DebnYGJdTbiLR?AdF@JQSX_*EXa=7JqHx@V zGqcRE$<_`@!OyrY3MC~FX@gOLIV}@>{5BFHH(?KtM=FX6CU~#Z&E4{fI$j5hg%F3b zQS|Lh9K-BN2vZ$Hm-AYDF*aM>#0t1cx41R1CIUA4u3f(`%{;$G#!IJ~YJcIRu*u&c4uD+)`dF0QZ`#jh2K`qWAj zUyN|VenhK8nh(-4NB!h_9VG3VO(Z!x5?Z`p-}?F#c}$&xw){+}`+UbA?n1g>(xSzW z`AiIv3Ujao9Hu>+AB1w;-UY$oVuIy(vsWg(V@u}CDu+Jh zT5bXD@HvmfKfQGz`WhmN@{D~TAo5LZtl{nYBG znYah}>uVexUt}D!PU!^#D)X6jQbLOd9hwl3%a}0-4hHALs>R7DY}`c5iH%m zzJ2mYeIx#x;4Op7p!wRx!O|CP9?GW>(l#wB@bzB_=EX8`iBgmfV@)qxxz!FKhbd2Z zlG3>PW z$bu8W%!_ZgU9Z%5-;=gk#NYO)W(~S0PzFEVn#o0%7Y||4-=D1;YOKs9bTzqgcb__3 zInb2+kp3&%HBSFJ^vTAw;QSLaOt12C4g%;xix;p*pxwHzY{{1`U$3!eghp0 z1f`U=ApF1@suP#IlRIX6TOYv8&gD|X*1g?xVzapy!D9W*A}m2kH@s&%$U|A8qqFq| z!u(~LZnLAdPJf}&Ie(CSVgr|G0o|2t?sUBCFRU6%n?`^#bJrdVdz8SBt&A+?tB{pP z(;bcR?g=UV`jNH)Y>X~&_z7u$eOEstIaMw7R&rdlxUKN3Y961}jct>KT(P(lT)CML zN&Vn$mqCZ%eX$WOe@QcuTB_S0qV}N`b6)%yT0sV`=Hwc(`(!L+NK^%g1vlPmr(@je z7f8Cp9It=L$K;_4+GY zFk0=|erL!$it$iBU#`hkA8^4Ylu|}>pGQ4yjjOc7A=7q=nw#S{y;9oPyBvKjn3G}> zWMywvakF^u0&1ieG+VU`??)XY|zANdau#75_8 zI}b(={7>oFYIv77L`l+p9!$$26K%%K3I&EZj}x3QFJe*7m3j{L=ss+F+LgDIX=^jRiCAe@_LAO{@9UYN&jnzNv1{R|C4NhaZ@B>5A zCVIpvE2XT4Agc<54__p+7fXo)koN{PG;hj8{tC-L${K6PAR5(#!CJJHvyC6Y0do zkrC1dNAXuR4Lako64T6$S?U5OOX)))fms;bqVdPq!4JK35Dy12K{QHB#HwU-mH6;I z+F*1HS=wa$PmN=j0D1J4@qgM*KMIY{Q#08WbpHn8{cebmu%R}FV90(M`V#~7jb`>- z?dJq^mv1$|txKX}z$Iq*-onqW^vmXW5Koi0CfOw^`uFC1--zy0QsR%H3p$n$aQMY? z$na)l)~fTnhU7>(p6G{KxQ%RJrXv7ZhM!hIC$o6T=dfe-%lwBD+*rxp4MSXEp5VO^ z*jlk&4@PPZ(7dkYTC@s^o4aMC4${yilVD8jVYU<~=}y)5HYb z=T#p$SvS4RqGWpRcz^OlH0lSFvFDiR=waRDFtIh|&E0$LtliF^q9~qU0CLkI4QB!Zza6 z76nI#6?Blk_6zo)ua#CfTMQ4Urr}FQS-PQnjO1V?q{v{b&oJwYtL1qx&c3Kl-k1Rt zJHmw{W{u4;PFq8b=8lFArntPHnQ`Eks<45(O7NjBR+m-;HbN~wvoQTr)Kr%8dVrYX zjbMC1F-moO)c48^f_``J*Ym%g>w3J)F^_aSO#~~vuQSmhjVbHaxiv4vjDMy|XFpXv z-nk&~#1;Pb7qXylaL>YsZh&v(K<2C9Ym48KyObeTaXkai6MoN{Kfddy(lyAauyYdj zL-|JjVD13BBT(wNgY?ExyRBJ5re4f)?D<&}tPo3CIa~nTqa*7gd3E^$!zjr?M>~18 zyWm%=hKrvR?y}ly3u<<8HfSWK-3~hz^k(xd@}-}i{^B5XGPG+|*x1A47Dc%_x~!{BdWoW^nJT@LTRMvK|W`_gT2k0 zB5ejk89b5Bihe1gjjbigQ89EiW+P1W?Fm*3uGi1pZY~}3ZsfB5g-p77%*h1ZaRRkzPp4x{7bgg4lwfX~`A7F86} zR_aNq&HdVbqBAlxR(ZQFxbIx;vli_nR}f83+<>{SQaBA#L1C{d(2OC7Bo$Xn`Bxa-A&fdNdDcIzwYZLeRu27rTB;{y5 zgfEehh#L=gknaF0>`Uy;4K?QzR1)atT>wUFe?IpR3n8eo`Sp+u+f<1K*S#y06u1W| z`1pMN5PhA``qyqMbb%OMFwL^aBw0Iw;$N;Hg#+vObYyCNPUtO|a;sz4JW$Yl7QXK} zRg0vTQFzm8v^;EJk&$NDYcgici$O5kb1z8o&FS>R15N;Oc1}@yeDCO&rCjIz*s8iM z2330D3&`J|XL%va7HS&*0+N-@J(8XofylIKN(-}-o^b~iR2x^Q@N(#F-;dJl*HfDk zi55h|+vZOTSCqDI+5+S9HJo$W+$(6!ZgzOserJkh4h%~7lI?FQ0X+SSKmtxcH(xGk zvh~I?K{n&3a~ST#^I=571#RMog>0B9@?X50Q=`}QC`Gu(uPgmbDKiUS^3}Qf+;vi~ zcGsMJb2_VhZgm?HmI_tYb1n>lXRUa&TaoQAF6uAKOc!3$ z<;~u^-`A3O=#Na{ZcbD)AUN-%a(-56FX*^*<(C5;Ul8Q-1ne!o|L{DH=-|D@3N7xks&1SRYGe79kt;ELVQ z^l(O_;-d(C>{qWP=X9v1f<3V$<*Kr^`9xX5iM!D`p4_4*Y#eS{8d2}deyzb)IEdO#>0|7h4 z9w8P_FrLdurfs&@JINIzJU%)^AIBGSa6EI6nSXx%V)2(xmc>7;Q}K^Rtlv6_fB@OO zq`TN0wW^Aa7XzE&lm;c~_&&IH(V&x~F}s-0vZjUhw(U+tI^pCM0MTj6dB&65EU)^L zysYdGSYc<26w_d+{XzcKH@J{`=ky7#4a8<0K$QH{l0ShFIoU1=zkH z!VObo485Fe*YfJpmu)VG&Tb`0X!?V3l~{`))I4J-HCv_C0Vm$bAoHzR_ItDJzbXUj zjjku{4F{kk*+oNdwacYWPnDx^Za4g@^cQv*ki@T~;xmHkBRn?0-ejeR#@H>po z@ZS2Vg@!=KfB?#tke;niXQU&8OMNm`3SN7YQLJ#<* zz4?34N;4UAbmV%nb@!eRgP6DHq?PJNeDpwk)QQYW#a(#}@|kI|{wOaXhB~B^MN&Lh zc-$|Z=jUS^bm%i?6mQ;_I;NX8p_`(RjA00ZLYBqM?KpSQf0`W` zq1w*|w}*m+WVCWRu_C9TWQS<3)xvv-pxWj7r`q*H5ngVWuDEjw&}rcHSFv~9<~mRH z$ho#SbH)L?`Mx{o^(Cz>n}D~vqA28G(agd;zsROYbmL?37hkA=kzqcy`mK5xjA^3r zfy&jepYGl-izLJ_Z~ROvVFk#Nnn|nO<`aD>9g7qvmWa*zr#XdGGaTxfG+TuuQ>mqu z3@%}B{Su7J)k`3@bnH}ge4S;Oc&P#20vgb(ND#>~2Y7IxAu-v5o&Do+#9F*z@zIv? z8p+DK!7N$H^bl#2PFF}b&^xWFx6Zb4tXR!I>loHo2LazOoBIegf@UJo<>zTlb$jRJ zX~oWEmDcPri2eRhzy%q!Xbu)Zw${l%ohd*dsNt_*ve3Bz?)HN-KTGLE zi?3XFyX^-+5M#S6Vj0DAX1U9s2gf5n)!17GmMY3y?F$N|q{Z&lQi7jyinu|hOGnJsrH4CcxbS{ zP?f?X&Of2!zuj|y6pdGF@Y0ih^Z8TEJ`@8l@Y&B(E&ce8s1&$en19qmlQ%*SR;-VP z6okf-vMk)K9V+KgIKVEqwSEzwBOTDJ@6)KKR0pR{aYj9(th}Qkpl6qdW2ctcMBZ$4 z=Z4=M32VPPk=xCuZij9K0!aD2=NKo;TR+}q4m7?<=~xno3B=sx0!;m8K=?j}tm!UM zeZvBfyI>7Mnc*_G4@UD$*kY(x)wYlc#eA){n8@RM9Ibuy!j6XxY9 zn@4&wLJEL_ufFV%(QhYhof~4I!g~at<&?W!E~<(D;?KMSOEXNQ)m=J>s@DlJhwt6% z04K<7*7G_Xrrla#_!_}zQ0EOPOB_{g86D?2;Dw4SDwaQ<9Wo0`0z#N9+xTD+puKOpZuX^jJMH!`Dn>S=W?$D1TQk7f$iGlN;ph(`0%n z`4Ft=tA-+ESU?N%4-9 z$!&QEV8FGq@j}v1`{Pz7^YE?mcPZlQQ7c&``=-7eA8s{?s2E;s+hg0~laHpf=hyjw&Pi)VS1q*_ zByz=APY7-tfvKpB-Vykpg#-QzTu)!WTVFcoC<+p*1JC17$Be&jhl<%=^Ez}eTr+7( z=Nw*M!*`m0#-UB&I{%zIS=i0mKto_&nCVaJV_Dbkoq^*r>z@A2X*J)@MF3p`I9q?F zmn@FW!Y%hGTI>k+RWe|@N781eBVo?pNwy;iscCU>ahJz2A*vh|W$*(?iXH+dl>wwC2JOS_ZF7XrQ9Sj9oToek-houY=7h~Tnm z{s+HI`~lU+>sJHDr!)K1Rz(h}o3IZLdxYaid->+&5lj9qSNljBKepxRTbN$QR5<27 z0*{)gz?_A7&puW@6MJ}ay!b+9{?6?pT37FIM2tqH9W>va!v!N>W&0*VyzoW&4yI|r z2sXU`siO)Lk)<{-Tj6`^k3+PnFB=KxWzyb-Hh*q=t6R)6p*+EzW6!Txk0f+(6QJKI zi#5Srq_D9wL>a`%5YKA0^-J&R$#D?TaEb{e+7^(;&7Sswd%gYr`T!Na7TDz}4drr~ zg7mrR;|+J-QRaB;DRf>c!#hA6MuN#OmipExlUMW;nt266Z8Q%V1F@ z-4&j_gDv@*x_U7Sm7ZKeL*e#TNJIwWH3o4S#8)|MkBjQ2A5VY39O2tv<7DJH#ERPZ z0}oPCX08>h{z%}|46RiCM1r$(&C>F2SE1hrK=%)qxewj}YdsSBHc}){zi{yMv`(tL;_)5oVUbpSzGDT;P>|Pdx8Ka zBz6p?iEPbQ?vk%~1I_z=J`U<>hlUC}BK22J4g!|S>ksCqMVF%om+ofoGeIl^bX}@z z&vzS9P3m;IJ&K zwkLzmB7K9*-`0IkqwC>9%8W_Dl`$xH4T9zuM~;jXanJZPM zH)}V*@5|BgI1ax4K>1HiDgC9A=XhM{Lg81Ag9PIm+XaiHv*d_ZH`L`f-AA zi+Fa_pJv&d?}>Oif~u`fgzp!!Y){+Y@bCXQAq}$%s*3{Gl-|`}@71({V{}JD1CviLRr!Bxopo51YtZhol~j=~5e1~X zLs3CmkOoQV?gk5KP>^nrF6r)O5z-CP-Q8!Fd++m|bDeWt`;Wc1wOr5p&NFk*J@f0$ z@e~}K&&uIcRFJwNG82^7M|}si)Y>(yVAp!Y{Q#d=noQ6B>|U3K_)GoF8$MI4$srgV zgM$)|E)QK_@Prqa7!L&i?gtys{Ub_EzO#K;it1wV^?}^ZM0ujffAiLy`cU}hm2nO|%j?4W2i{&8CycKvX(vm9LBT^(CJmE6XU zD%>@?GuSnH-5cL`X-L!#jCePbV+P6Z3c3jCGF_FuH?7?t99vdvz5t$nz1Mza5>(uHI{Ot4&+uqX*HrUg&{v z(u3RwAjFtwU0mEvk8t!`!ugaGb5{58OX(rB?zt?FP+J}ad-;l{{HlHJuM)tvs^lM^ zvg3*D&~4Xv#T{+tV3?2|lG`1a<2CMrXt;Em zfDr>6#@i%ejL~9K1Z{4m(6bMYPkf`;4C{eyfY0*Y%W!PU{jw9{?+E-xH*VYjZUwC1 zMhEKNyPh%Y?hqUPLs@`y0#tEkXC6Tva`|}k{Os%qwtg@7r(;`+`=Od-=DoRV6#D~{BKX5lmLV`Vfu{T zv{&+|JNu2oR7g#3Jga_(3OOTz#r?UbR#7Hu8E5>WICviL+DsqqCmlV3c?sl)23VbY z8#bFWF>Qr1!&;b*Xzt)xe&_o{M7|Xjyfqgmw3o--?)aQ$#IWAHuBF8jsHX*H6Ff-b zC6FiV5!~*_^LPMIX$oi72^Vx10D|cs*r2{PoQI?l=D`+rF%4|*(;`R~Qx5SLFKbJ+8b5&midCm41c7{9F3 z{p(kHP92@T&FQH|S}=*J8Y3&;O|OTM#hGd& z;>h5wwX-;NtMo@?2W!H7So{qnFKnXRwxO=>BYe@j8x@qB1#vWK>a!Ih_V&L0fh#zNk(R6Gx$+fJ#_fd z`Sp^i6tju)l!0~lFZxir?v9T4!20b^les1$A`b#VdynD6pF&=KJ$|4d1VwC!#m(@^L@ zp;gGL?e4~kkrDw211x%>+jojjdweQF=K4M|?#GYD-EP4^c;HA$NzqC9!higD(0B3e z+g)|neR(7p5&nj@EB(^|mtZ_RLji6(um{I?_|Y&h*!S5OMT3h4u5)ZZq<>P<&us5o zXYfZ!h>uLO}vO>*`ICK|(Q)~fE!`wT27|Ryo@Ys21yf7+v z+?_`=XL-EV#-d6B2*W6#hwNUAZgLUW7x!Dvbfqhfk!1@@h6yxm-8OiBlU=WtzX>1aebkv20rC?k8Q%&hT-NaclSF-Z3tSr;KLjy0x&#F_YW}#sb&pNsp z^G}oXOq5sZHUd@ep7*^5jlFJwRkf|V+A8o7?n0|1uZZkg_u2lGX4i&0S<-XTO1v4Z zN3{PmIKEJ~;L&1D|4B=9O9QV?%&+{Q`w__JNkC)mGK8auP^Mv|l4YQqZxom@WcrPYM)IB?A7)ja>oyVN;fXjnZ~;s**>biS z@N!;^HD^IUEW4kZ`uO~0|3zw_C2$X*#s zZ@>fukcc?H-~)biU{2T}xF;qiU;s~c5)g6$3mv8Rer|sr2Wc&&Au6-|K~4X#MO6J7%fb+2wy{!?k20KH**x{hAL^ zw@amTK8{|{+|tyPXFSA^qgM7QGn3ZL%#2Ry*9X9t;1ZR%99xAlYG6Kia*fk${B^d< zlfy(QY3aoQMU5u7z9C7JisriVp`@f_WXN*9Jw$cE<>Gh|j^_8?r~#YRbp4I$%L``) z^~%@qcwIrF6Q!i2h=H#bA0Mw8kPYh-VR<+5E{7wI=64RIl#B#kl@ySH5dPVf&I*$k zJKDts1r39PxPXr9H)03Ic1pVVXbrq`5nY*jfVZNlY+!cPw8l`d#?xv?teLcpujO)HrUWr zMB2ZuI~p73>T$M~uh_2t&i~2N1ASsXLTJQ#Wp7-lY+o|vD;V7|;J+64=Xs-99HK7Q zc^3C~h*J!1l4aJJv&Y@~o&O5;mjn+`m$u|2dHCdmfBZBF5T>-`WAV1#BoQ_=ex>GS%lb3l zYc@TTZ#-j{C}29 zrBVP|2h9F6)6;bTWRqM3=uM=u<%Ck5%Wm|QYd4WR_+pb0Apqr|0HxxVm{fBdw{=S6 zmt+5JTE(2}KKIY!Tn`99zgXVK!1M>V8N!CLh_j6%6Wf*GfZ>Pz{hD~ac=JJ-MeoVj)9qBh3mG{p<1I1~`+Apa1f!KyU}NbSRmgIxjp>hneoftZ zogMPe+Col+`vP_-TK?DQaRVI+=NIvQS#@%rZ4bG9`usUUz?Fw4q`g;*f6t`IY}m*= zyWO&2?T1p=hK8uRo=^{Q4DSK5F?UM~&N#`QwNR?0n$veI)ozPCt;ML4kgvg;KKJha z#I7vHGp}3G|23JIf(t4_2j96EPBex274ij$Joz3)?}}*rOtr8lpQDDjQrkR=D&ma8 z<9|QdA4dW8RYaQr3Zo*4{F>Nhyxao9B|_u)IP`U>2106aNXI#R)Q}0aLzQ`HeO*#d zj}oxfDc3gvVk%Cuxv5Uj7G#WG z$b_`adU2;&)!2cq@Cqa5hQDW2LGlaiz;9LGnO%;Sh3^OD^uij(<^U33fB&Bk=%(SG z(w?sOc(RuMxxJi{!EU5Q(327Kc%4|HCDXvY`OVa#wphKRBGKwiu%o)&IZgdnibTr z{yhth8v9IS;JxhX99s2%c^>mpl1wCeZF793+C1^%nXZob;Z6F^SnRuZpMmw9K+ug3 zavmh@1&%F@yQQb+`_`5PAS+vdu=cBn{&X4zMsR`1vNVv12X8Jw&(O`y&0$Hia)}v9 zU0vPY>d6d8?4CgukqGd@l4y7MLXc3pdD5Ch@INCxzF^aN z87nASUHu_4XR=qO$gPh9lgz(qWF!K<-q8|s^X1>ye;0}}F?31k2DDwzXZoX5 z@r|Kq^ZYYdMLb3N6Y%c`2ZeK(5~ATTxTFOy{Acr?VK`ad#g0BcCA57pj+Eh`5Jq}_ z)(XW42fkyCdN}-!7Vr3Dq{1-Ni=Do?=~ogpVMl{*_lkeMw&)QsgnhnK zh{zV`NdirUfWrhIIH$C%_XOO|946gPO@W(@NRvrw=NAF**KoBfuX*(`CGr;Row{}t z9r?~Z=~{+xiu~#U?Otdy5(Lw_E-o$xy+7|j-kV|>=XUZGnWDj~`-GYKdu?s4cJfyZ z$9Wv2zEiPTEe`Z!Z!fPuO?Vpb0d-HzWq|^^LAFW}@{EBH#f1X3f;K!f4$jWAo15KG zwjp!&k$*C|@nTce#KgoU_y~Xo{0Y^Y%lZBgK&(QX_@B5+Yq%ZjLPYlxO8DbV6^n!r z&b!5Zu)Y3=+6$-EYMq_hxv&|(<#>1%(y~PIeF8^Z+s*xgj^p6Ws zGIK}=>{*2v24AAyjfke4eg{GzeEuXLuVtxMMPdk=>o#<&ZfB~8oYnBqHiR-9X#g+WKXqWbaWA(T|=4Vt|)O!^$Ss_sEDAFLiRRP?@i?kX2La zUsN}L_qvV@=HEj-eJesw{3k$j`xsdB!b9-@XMhtA!FDCM6+_?oxjol!`(c?(kn7}X zF4A0L*-fW(Mk?bOz@pnry+E(#T~J`19JJuASa4+X7M+xV=cX-MrFhRIspH<8znvei ziq?{1?CPSJCFWlYuo2A?l!l~R!AGlMti!w+_`Ipyc9^o1($L902JPI|h%2Fwnj1Gx z=q#DdBn#)rSuuT&xrS?th-8&5TQ2`BGq<|Fx%q2X46pUco*@vu+o9Z4T~;E!F+Wk@ zc^+#A;ML*wgE;q#GNIjEy=2R+vpXQnSp6{|_Gh%2+e#@oDz->9PYPf=#Hd$ybvDJ` zu)nytI66ILg_<4iwb!p-KV)Uau2P4v(0ENETVeLkpATVSL)Xrq<^F4H>Af!wUbM6n z$xjj7ro_9rIq!jwi8U`~{hvYG5(_Hrj9Xl?{I%a1&W(aC1U~n*yai9{M&w*SG4Zr3jlDCM6PWjKKdNM<8Ec4oN@}KX;*M+wbzg=~>0& zTOy=#g#I@L1qH=D!AackcVOnC8aR~|(^}Gz2r4S1V0>3d>4QGIvx-fQ%tH7XAAi}1 z$07BjZKk%?E~=F|oC8wHXXf8zv+WU3`a!)84o>ZJ`xbv9{n3lMTVzU1^)nkKr5hy< zq->;JDn|15^|xQPJP8g~+&=V`J~U0t@nrOOS&^f;iHCobN%C;`QF81 z;`s^IvQ!K3%9>-Hiyj>!42w*ze-?~FY*HP_PW1)ixKb7D&JVa8jiZ14x*xBY`^JdF zV!9$wo4Pd&XYlW*x9{`+=?K3mNKRC+M!A=Kg86?(YXMv6HtuSSwy)ec9WFQ~CP;UL zXwCO9pe0q4iO{lGaVqS|Q@{W!=fA%LMP1#%01niGm6er99m>3N`zFNSd>b#UpAb*m zetg&%E6IgPm%|Sl^0zV^hu>zFV;hb6;M}VWif9iFKE?lChP$ERJ`Ff#Mavs`kFR*CM#5_27T z2c*a0%iX}Qp6rGUPe%Tl7Qd8A-{9DOmYp{Z_E@AT1tw%>&)0p7Q8c4yL!oe zPJhqO(dFpQX=N6sDA;r?)1j5}P)3hAHy+=67}O&{)QL6)pRew$ zuNWBJDwE}H-DeO11OuqS23va;6oNG$Hl#70TDog%KPu4gUZ}Y|->kw|WBAV-c=zyW z(cstG6w>P)`|%Goq)j^H171<)=_9x=&T8K7k>Lqdy|EYfe7Zp){BT2v2Bj#2d(!?K zp`)X-48wdNLZa9#{r(8*VNaS2Mb5j5W~A&3S%9yC>b82vVK6MP&$pTv6Zf?AJ!NOG z+?-!UJKFdi$A^95kT*~+G)|w+O|Zu6><5XbT{O>2Sz%Num0OVtxh?;V61|%z+d%~@ z!Q3=$WAo#%D5mcJ!~7nk24+!Y>kaMmDeRe*R8bN3K25?Tn;$JQKIF=%n7ugJd-?a} zx-Ii4Ih%d|#l$i%-#5d}LY>yR=A(h^@eHBuEiq99Id92b|Mr{a_PogRf}A)aXuimJ zXdz6)jhv1SJw(hSH5Dm1XaP8#@A7Q7X`!j5h1IB^8g5c=m29M-4yhU_rLSR!F8yM9 z>yi6m&t$13#mA2yfqRbRuJ)BmMsf7~6vc*G8i`kjq99pa->|CevdR@ycs9e{>!ALD z&T}*uEbT+04~k&QHmb)}V`Jm!=xBox*cqabU9ZU$-u%@ASt(-mt!`GIZiHT}=QHoUjrBahbgDL4Ws-x@c zXZUM7&qo~Yn(~xK6-G;(e6{&yLp?*T%S6Z(dbhjfo*>In0e7M5#iLvkmoS|>+k1D+ zx=-*H@=WV#LJuOJO?|^G38AY==>GMrTu`xO9AY9;hhecsH9JMRhUvOBnYMab;`;I> z?TMq$^XzvhxC%*F^*RAm+}1lCQOTu;nmsCDxXiQ>{=!q|4fO=KWv>;z3~8~mQwS@flFv!Hb0;{5zQ9Gq9sgP3wVrH9M8 zX>zh!cGkSuc2xn6YfRhQ-~(jKAJwazB8rjZ?&$_kR?XTEJ`-P})+Vc>!qlB$z9{ia zXP!Tl09g#=809V^ohDGTtSSudxO2(N`= zf{D;SOQpyM1lyxn^h6HQm~kv)@G@%n1^E zlggC;C`=5gHIpJl6S2w!p)kLoKr)wz%|LUo5>MTs?nkTZa&lOy(>@IpJ8Wj-_|#%{ z3`)P^d*RCmwy?nQ95x_LnF;;(*2nNZz_S{0KN*`+;t#yjFGWPK@78hI3l&8kBQ?-l4`Ae$h4H$HI%juZyJjx~yUZ5*sib~HQE0`QPdD5K|BkGBx z<8VI2{&P`}xzJI1u!bp6Lzqz?t{$75v9%mZKm=X&QDK0#mV}L3K1`S#w(i&q&ZWk2 z?vAD860rRu%gO07C?e#sGw9r?;pi!rL=*Qk*r_`eA1*sj-MNKIPA7C9x%U;$h40IF zrOg*cLrKqAc2VihS;Qe0>t(Xwv5Dh@(-Mt`wERS!KYRFrH5MB*!7 z3mY3G`T6PF6uFbpn;3*Ip?Q=hn~VxOBREY3trz^3S2GoJ?}M%gaSd|Zv;iYRcB7ZD znE(Zvi)z&_WxdR`>r<@OIZ25$RukA%Qf!^7MMgL%8t&)pPF18w`nJc&3kAa6mDaYl z1gN>73K*Z5fVw+o89$j{?M-iY^XC=c^P1nkc|X)b=W!1*~riyu0I`pXs`cLVWZ3UbMD^E_~QG?{|D?qkW? z+e~>sQuga8NjpWTipRkg$(rs{1d@mv6Rruoy*8<+Bd97!BonA`R?7>m0tHSEXGY2 zPM7n}%buUgRQ+}WrA~ud9i@sFaaE@Q87oVq0vA;_r8YlOuKGNbe;{6)c|X zmf-+%(qMJ$zTLD{&Mc108C&P%hF?SoD@O?5QT&kRBeO|{ zU+%D_ji3NmOR)*fRabZAxabkn())m7G}h+qG1q4`ACs*5d2M;Fd*rYWnSWU%dZG1z z@+)*IQ>3B-O?9PhHLBGMm|J#sj{SqFE8dIR{S6!B?bOz^FV0kX{!I0LoNFOa<0cas zZo@w~bBGvDYbjin-x7#}3E$jQ|7!K+sCSr}#>T$TigXG+hCY?S?}wKJWPpQN$z5Mt z`#m(IcsT*90?z>kV3XG_*gZCO>Y-z4X}OY{ot?dFQjo^S$Jehx8zQzpw7e{>uGiMp zl>)d`D8U4#>|#S1bHYdpKAp(Oy;`?Ngwk*C>^!2Q^P^WS{sphWzSY{v$;rxNH}!47 z&Exsexc2o)YJZl3e5hcXpGkX;7vHapatH(lC{s`9x!XYh>wmc4-E=?g-IOz#-O8tAhw??HX;Avd9PD2U8 zaH#kRj5;JK#w9#{%=iAh1kpc_NzCVyDuRJYHtdm;!_Xjd<9GHc%0-n^5W5EBBUV<{ zq4|1!iBms>ShEt7Z(>J#m|lo3D^WTI5=2V9HGj4<7pc+ZP#&}VzgTzDL>%{SgN_u5%J>9 zo4Y9Q%5Ca;ciKoNF0+mG^+iDG3(%d4yExv=KXBYHU_;|`4{Oj*dM(-AvA>(0dWj`Y z@pjTO^D>eG+dPQq@X^TK$|!Qx#U6uY&97!r-6O5Eg?5KJrrmACB!~1GRhI;t14iU+ zR`4DW25(&|lCiL|(*Ih^;`SwAHbLPRSo-Ac&D_%59P&NB@Qk}1Sek6sGB_BStLXM!KcD!|bi>P;S+d`-84KtpK=_d=$yrAW zxgLAjWYrupniZ(N*dymR{n*_2?Z1)Qsw5^QXD?g4d>Zs^$Lq4fC0e;AcQz)3VtL0DWr}Rd zH~nLfX!gnWVynE#s$f`Nr}NC>+`jOHpkh}`o>`q}K1%^Ri4Uk*_w~&5Zt{s*EG%wJ z;3H4uSDh+%0H@IrE`(n+yxg?FvQoJh;;YJ|9qqkW9ay6H1xw6DdhBaoXUJuHey`3=P?gY=)tZB} zgz)prTb|_PN(w05ATF{mL>wK?SOnOwY{Y08l@iwlQw4{{Omov|B&JMdT_pItntbsh;2y;w;%Ka|ze++v zVq#*lwY#eWv9!>1OyS++)KnaJ4{lG#peNn&oUG}soDz#tYt(D6hPR+qV+9`qC$E7H-^GqbWL>37G%&b?cv>*MUHJHoKia|53mR|WLhG>kFr04#p?yO`F%7D^o3BT2ZQ>_#Q>Xqvt(*70XgIR)zEKtbm@bQz zJROxc0P|2@mHzkwAEqvkqO2DgE4P%5j0iJpjxl+8iL$cjxNsf#S{9!_PX$5G!Kt5m z?-(}2wzjvoThcv!8o{dH&1!Y@tg>ochQ=S|Had@tA1VLroRWtRPPS5s-O~)eVY~}J zLgqB3^}(I(nF49yU-m<|p>(mG(T?K%s=D)og3ivW@WlwO?sX~hdh^KmuF*fG0d13)|ajgZ_iYF4&`dNPY=H(;jxJan^T!v zsD_5PcE`?&h|JhisTx4c82*mG;)zKjBrfg?KE_3ruC})HH$%KNN0Tp~PbaAPR&-Vd zG(c2!knCmY<0f(p`Lp%r6GeUL1Co!`Iiy82WlXMJIXl3^dqh2rEknrr@$F~XUrtGL47>Lj!JJcEN zmyTf5#k5`RPn29O;U>D@KG(h2TVpPrA(t+?B3%O)OVCqGD&SzfsEE42Ncs#<^z7uepf{X8m&SfmcQ~r?lg~20k zN9HUU&a+I%q||o;`ttT?tueVNH}cHKaLV@%`)#;{ycFk_L(xW6bag_|WtdD&O)m}+ z_E}R$6dJiBwOcY*;M&QMxBcl_p|GMnoFt6?h?dqDa_Ct|eAAVBd`KrMnCuVc9>th% z)nakGJiaX**NjZ4+kaYiM6i342%5g1Tk__T%k)S{Am6N<_yQX)EYDs}hx&FSYA>qN zHFcX385MVAF7GX^uFk&g3(iXtWRT4#uI)>@xjt4r*Y{yLn%Dgb1;qllb|jP529(gL<&}C7w|I zEZa@{bdR#U>J-@bbbH$enPag(#XcOxVde;S-_!{E`2)R6qsB8YfnhSAF_W6=U~ME= zN4P3Bmh}1a=MZa+m}ok=T{wJx6iP()=X^=H2rck?&p6gJHcDP*68A@tTb})zq2vF0{71f9 zxNjO7O5cgOt-jnq!)3BpKq+$JV}td)y6r3G)jDUXal1?xUppQ#CLW_H-m}`c5B>4( z5;X~z`%{}}uneh9#I#ENt<5Wnzw|?|%hrx|O6I3AbNv!Dszi&*Qa98p_8)e4n4LB_Z%gdVwR1N%p!nm4cll@;8&C z80?8nRPz;oJuTLFEl8I21C_S`tB$Kgh*O#zxBlW;n9!9ROxz}cTsV~ZGB>+opS02z zxnhpcTcNt)Ik6lY)q|y{D#0PikIr^n8DhPk?7d`@9F-q{y%4u$7|xtF9!88P+oX?3 z^d)!+!3>%;_X1_zJuTvLyGU!9p*PfpX@;8;n@-u=);skRgLiK1u*z93EhE;PAxI_i z^$iZ{^?qT7{iDM0>t%)i3S#ZqzSD5b^iBLO%pvc)AM@ zdx#VB7pA@(MSx6FeOZBiUds6Srpr<9`VEQbw(Kz(lT%C6kGkUJjJfrmuAbJL>U{2N z6FJLo+LOOoYxU=heG=DdvB;cGT(+>_?2#7djHp;!v1NzF zC_U7MHJUj#z0E@imkbe? z){zgK7nwC&Nnf+ui$@mc3bS*HOL_vE!TiVb7tNOE!d$m5FZMmUt1qXi?%yZv-Ap2V zVwH0z&c8XmoblsxD#Je-xi3fQuy!hhlhY;1*`EJ%aO^PalR%(?dV`&q{te-Z74w$7@ zt+l8r)Mq!am1X>yPMESW>UpsmH8<3Qa*6BoDwRIZuL-^sNqsLX3u1uH>S%0}xI*6U z;^q#$YOm`(A?!H;xBbWB5C2~BY$^Fg%mNHJZ%~HJskZ*eM5esBpZp~+LSR2qK4tTW z2dT~ET&w!c`TMzuw3|np@4t0Sd&6_`_9}X~^?i~tFfxw~cz#$>s3y<+Qz5awU*!Ya zM#pbcG%g}XkHL+Ltaa`Pcj4TBD78B#7fK`ed9Iz@3Q}luCVxos z3*;j)rEkl8NL6LvlWO~IL!FF#^7_tV7$m~kd{@|4bPQxYzDkbXvtpnViNK|=U~ybg zTO+CL87z6SRE8{If6fJRz0hn7p(~mAE|A! zWtLP}ZAV>D0O1Pz^b{Q~s{)>k)@*FrY7cFkkJns2oU(egF0KwLl6Y8dUDG=Ko*#`& zTOMVc3r9?UzkWCK4x#jiIJXP`62ijf*8VG=Mnw@j$9%oLv8MNeVq;}AB?QmUh-{~f zygyp%(hLu39<`zXYJX{SQ!W)xTZR_~77zhLyQn_NzlIc&WtnvbAFX9LB;;mE@Yg6@zZuhUIEu$s>`^69 zwr@W(H?l+^|A>J>Ktz>%vHs1HiuKX!Z%$d4&SZl88g7UEh;hdG?|i0zkzx_7R&%ex z1p@P_(3>>iiI&BaHq_BE{NnrhMu1&PIMJ#x53vj?0siuzS$&c2djd*h2)9GWo1>f4GKIGK%e4$UR>gicszX%JfzFECUZ~_SF09Q8gOxpl8MH zK>e%Lc6U`=z4~8;_MpSNu|;jVWmB=h_EbvPFLFz0tUGWqvM6flyobrj<#)uEaq1nO ze5~YNuWPpgad}eqiQMNttAAb|EaI2pe8KIaiPv&fn#tuo3W~C_a^Ysb?`Towof0&{E-E-_#dI=>7TU#W2;UkLGaL8)XB|P$ z(EBqye@h$YAt(iI%e<$gY|hfCK!<8}ZQWG)uv37}SuTV=P}`u2q> zTjR2752L`@-N44yK~_(d+;4|V4QWAROGoev#zLP;A}a4g;4SyYQsx(VdFii{#q1c)#K%-0VTj(ni=cSay{~Qu>gLH#kX?I^6d8O1-n8CC8XnPyW0QlJ;V}NNt zRdwi~BbGbwjgIVZ*ZBWS!hJBTZrPH_wBwr`Z_IlMTp2Rn9|0sPI@;P26~**-j%?mn zFVY&Apjo@_tmGynT;VUb*P69g9nght=}5jQ)wPpDU`Ku0NFTDS`SB{>^Y7`>^Rq@J zk;@D#5l*WQ$j%m+KnwE@8X5&OI!Ji!EY6NBVU(y_4_>Z2Bg!KVgLw-x%SxXv%00Xz z1U8DYx6^ba;npfFn)gR1e>sx!C@P+}W%`-8%Z*9K*oWZV}g}7o(dri_4ZkVQ-WtnT(!TO9A1VpskJ2n(-Yl$b}su*}S zwrJ|f?M=yqDk|LznkvdFD0j)?E`yftV3BDXofxS9m;>$2HF%DohaD5*`g94#z=EISxR+aPm~1e5bte?$XuAcGy(X zG0Ex^qh}{$n|vY88<0 zW7L3-($SFK{kOsROHuxBb#=W*yHk_PWl zSN-_}?MBmgxO?!oS6ZN3XwS@Q5758Xo}uXI^cA?5T|`llu-LL| z7_qpD`y^#;jVe+wOFTBWJe&yGYO(-r1jvE^}XJ-h8jEVvM=B!sY z?CHWvukhHfird)Go!Xf{nFfmsK_0vpbW_Qzmzi9%Az}5+K~r1{b{F;S<|b&)t`83{ z26GpE!%~TeILxkSHrT5goSXUb=}+LkRq%17tClbb3ky3qIprJn zQQ9hlBM=7W;xA5N;HTeq%5jkd1(#7hT4&h_1P?koI)mX~xQtgEKG+#!g>Ggv zxDxU8_4U!w(U~|nnumtMpvwLh8L6`7hAX0V$`8JC+la;PGVoFC>o<5}DU!KItVRq~ zQ8h+2!fo2g%fqVuRWOduM55qeAWbv1-$n@zbEda|W!Q1!;k)W{znC!lX?=}2nuWng z%vJyU5qA-cwz06`875Z-C3W9FYFNZx44vytvM`i!$ zo=(%HQBi2q_c+DRT(C$VZ$a=hV^9dPSLj8MXxKg%){-Q}=C zFAek_cTD1^2gh;6d1mXf`vQ}f=d4DJ4{b+O1k_y)+HN0gjMfJ=6-2O_-$Ay4SK3zz z=PCbx0_*oAH8a!d4u>iUx*K^VB~9(^pZ?6IS`lNR^C+jCIQpB)z|DGcxY|}6=Qe-h zZ*g(9diUDtVON34^Fuk&3C4N0qjaVAlzI3(P$> ztAqYvXPsoSfWu;5o>yg{;OCv{_PCqK zCMqU9-xBQp+iag}VPon;SPW}0p_`E6P*)^ocE7lk^!)E4){YZblFb}K$*}<)WlIa9 z6CNiCgOM;nk(~EazjP%Gj_<)0vSb*hrY-+Nk}M!yGK!SF)PmzS1zBIsYm%pQ%=6IyRhy5_vQ zu`^wNp=e?KY}_2ZV4)1gW>H=k!3-RW5S6hRoIB5nb5&{*cmLAPiKT zj4-@zfB1<7`ciXI<~90F?dFhdQqin1?BdetTwan;_m>qF7KRJj(_m9|>$P<8uY}#e zB)gM{aCY_fDHk^we&XcL<`@6xEr1cRv9VivbXqKJ()?m4gOxiS-zAMxhX`}=DCqSq z0E(pvo5(p3q~?c5N6WQ051Jc4Fw-d)f1PkX*j?(;IoLQ2ex;GqULy5e zM>Tx)?Kgp{iePB}-{QZeqpSP;)Xp&%U;a10yzHU4<3AfkbqCKIsmu=)3#myEkM;EQ ztp3zqK^o-tncdwl)esjatGOnlh2D*gFQLu>-xMhdjDu1zGhejkEBxmLxCaP3u(nBp z(-+KJdn*HI9|SMYg5gmj{V+VN1apj6pTQ(rsr#jXx3@Pqg?!qAsdV)9Qw20+MqMW% z&>cZk6@C3WUZcisWmZ~W2zqV>&WC3E+Yn6C!^AIFb8AqL^QHqW@F~*M)1&#F6{>1Z z!7tY@PKT3HRk^o0r4e(N4Cbb{x3|?Q?EJyfhhhPibQq`6$od-w*H>1=VBqBgmFOiH z0j^5tY{Q4V_8~AZu$+w1`tRdQja40q+d5S6>3Fe(sBt31b6m))_o=`#d1l~R{6NO{ zEWR`B&3@2!;W`}C!nbSYh{Vj5Ygb4w#V6_d`ey=tgeOo~{1&TT%Y8_1`K?mY7KnTc9g&Scx)-%{-+qoIbL+UZX|D%k zqYso0XX{*#-Dbr&Tx()z_r+7RF#y;gpKc`VBOnh;w z5Y6oz3r{d!X|6LemO^RjFSO6%dw|@E@M>%QQs}iqbn_pEA|`hX*6MDxfF}&(9x6FF zU?6mwUp&p#MT9a#5OqPo2(-ma7 zKU0wrX$XWzjDv#%UOAa6CtKkFVs9A2&B@8(;^oDwm{`<-PE?xH;4YZaULiuICqI~s zmoj@|k=@knj^$s`?XR$1J*a1yg2%8UPB0F9YBAt5g#22A>k_gF=!a0tIbZslL;OUJ zWg)^CXE!e1w=pNnin_S)ot#K-G{Cf2Z?&s43OL%zz;l#$5)n2&^VFyY9LCZrMMgLH zDg+gB)Y_mUjiQvNeIrTO-)VU@Vyr6lPS3U|pIBGC6OQ%H)V#P?P=aiFsS8Jduf8@= zE&_i1L^q+00t_4>NQNLB8}Kw%p$#+JPy{uXbF zScmGRsBF5N8n=C*yWK=>^JKW$=Snt`AOKj~!(bb7(sNw-O8?F_wOkC=Ddys2mBYnw z#W{|M;xEZ5bN#{SCbcsBF9BVMOFyg2i)+L&uE(z39E9vHnb#jZ5PHyZ=8<`u+unP9 zZ1>Whl2X8X^Uy;}CwVTZIC*z5ap!z27+J~Ev(L5#^(8NBB|uk4K2sqX#y4Pt#`73D z_t=t>L$sE}Bs6t~I}e$++t6=9pBthPKDX6lguq5Ax{^93mILOdcZpkjkw7w=S8B+-JSB-vI*x%3s4OSB1X7IA`&wxd>?r$ zN<&Tqt&56W_K^{qdd8{6nFu8*QlGiFDYG{Nok1V3fC{Qtou4oNn6hfLsu@gLUVeU$ z!FD_p=JMsP<6D{#xh$o7&d$z-g{YVAW1Xk=%4dgI#MPui^0dyBB`ea-xx!0EHAk!) zeR0ma!~3Zch<`M8WPj)PKC7Bfz%yv9MjmwS2fEXM&vZq@&`bwU504ETTGZGmfuoP(qI_4<5M^TNpeWH9gmie55? ztp*GNIrA5pOy_hyGkeZ^OFifWEMNX#w^wh%6Gwh_Q4v*ND96Cx$9}<2*G@)Hf1`_3 zfy-H`r@Dmb`z2-2-qHYNqIdzAf-p&V8X-lyjfUpDj2HfedJ~fIt?lgu$oav|Tw2S2 zb>DXHwUo3@lXKkfQYX{Gon!Z^Sdn9uAbuM1fOxMr&|SKNjx}$o@^8~X`-8g(pcDo>Tj^@%c@yfanBrd503$|L~}+#t0In$jz)6oLDu>sEX=D< z#MhpWgfQckuAzl@NbmaOmdH0wEo*s}W4*ajM|uyNiZJrGdF;Fin{P=MMXd2rG_GhMH2JZIsY6bt5s3 zNFlx5EPc<2h%C3~Uesy==4a~tq~LlT2Q4qXUg&B9jwCT?LCmU`NaPu~TJ z-Ktei?Ekv?tiA63==xhN7kiAyaM>G$l#Pa!JhVVC?L@zYUpZU7397iiK%n_#W7BVI zf{-x~qWkao4dzLGEUdrHKT5+ODZpY1bNVHFPd@_@?ZP)sVR?x=%em7H(CcikVX@&$ zo`m1MxR9oHSE`f=EmlfjwES9}m4nQ~zPCHpCJTcrc)E?<-N9gT!wT)Hrluwkd0QGA zZ^M2*JKF33W{2riWm)+J5MYpAMnDRxht;U11y+mcLU4rxeJIK)^|RbK{Y#*~a#&6U zAP@JBptY5iKR_@k|GepkKu|z|djMl^P+kAbj(64rBZ)nb(8|7fa}Rt?n%mm$^YQTk z*$XzV?%z0W!dIWOYNOXE5L}mcaGCW)f(-l5kGHSu0}54K`MjSnu1ySk%DD2OjGWIK zR^z&Vvs_MmzS6Dng8xnbCtJ~dAd)fLttk@oy3{&f3Os*5`|QK{aEL+K6N)$DDqo$X zFQO_9c&dDkNI4iHVCTR*K4rCsje(mlTp0Mn>&JGL6(86vRDung>MT~)SD2Fwb*>1@ zez-P7FefdF16|^6zUDJEqpFwws0FOOl;t(2vN^B5e+%*Ni&hUyl{I&%|%1JrYkc z{weJl5p+^B|4%~o#JSKtF6)JRNF$T2ER+wY4kkQ=q+TfRuj-Y&;wl=!*=(O42Q#tp z_`Lp4p8TGiM(*%4t=`st@qb6LoL>JSv8)*$0S`fkGIUX>a`oXuyPe&FhL-N2fCF=~ z29oMM1IM=~V1F|L?q_!RLn+;@S1W7bS5e;7w4L-_c$`%KNOueu*Z|k3uyAF%rY5z) z$#9E`3i&f3i3GNn53PbR|H#Mm{`??~ZHw=l7>bj6M*^tejb?^MrmrisKeBDFNbO9Q za9wpq6C#(QyLlrB1UQ(-G4d(qom$`@D z*V`+Yt|(t-ZK$cGWo2jQs_UE=d1%{#3NIn~_kT@ScJK5K4mNdl1%kK4-1>Uv>L=~! zjH6s*Mn_T!&j)GxBm47)0bK`Hxmr)Vc{mj93Pdz2Onk{L=uuN8s>l)rUcB&vV{~R? zt6n^%y_=;ch9l>idaGsHxIc#km`K#3Fy6Wpdu-4);0^gQcK}3#|I7m|0d_vjaI}X9 zh3r>VgwanjwT<|gc*9yu+hCmB+}-AXVz3`X%WJV z_*&9mL70wuG4;8&$VfEDtMTdQmoW-o$$B{llFHMt*j--E&6)*P*JYj#(zCLLg8z%Y zxn5@rrcX)=Z4FrjJLXqciN?Tt#h25Jh;r%Ofa;_h!c>6a%gIOp!=js1*I}QxCr2Tk6j$b2gGt4s*tK^c+e1De**;>Oa@YCs@;Y0KT z#|kU0wIS;kM9i%rtUVn=v2S~ntgQaAA@=txE*QU1{Y9REc}RiX=Z!be9x=19^x1xc zAG@8#5uXfRb7$YeEn(#QrlzCCpPTNxaUJpKMPr)vsuW#hchhbSIt~-ZU`tcST6mm2 zFhWJd#QMxkNWv(aa~BAOz9kp@e&1FxIkq?c_|+*u1R#e*qzbnW_Ny&G`ASL68*q^I zFpgF+`9B1Jx6qobeCX(&O*U_b`36WKH^xgIyYlg!RZjcJSj6a={LEeL@uDuWwJo&z zoDoo;`!G?Cx-ifOR zh$>D$kK*TOmIio-Q)oF?wXC$w_w>8x+q#K4@kkoc>m7I3Jhh?1R|&@N)a1cQEH4^h z-k8jbwXD8~0e6TL#h0eX+ILgwqJqB6h9a`M6Sx|16qc=KHE}`Ya>BlAJU2n3_&KXS zOb&D8YEl^`17G>(`vtFF7Z!b?1gq`yY3+z^t(&}~3gdY(n66*Ze-8hAd}GgI->^k; zVIbL6(&LK19pG`exVd?FV%)v}{@Ho}-dWnjuv(uv_3t{-cZ z@7F~OVFa?v)zoJbGc0$kd;p$+JlufP`5Z(%_B^*u^*}h@7o!n5ol$HGQp4n5I*Deg z9zCFiF2D$CO54>w(f*`x9%$PN!R4@pSxf1rlbZe8AuBHVhn@6_gB_EcrkTuLZcAyu3}u}%|AG!pF0Kjm5k#(Tc~Vi!#G z%BN4{ZI@FK8l8rAc2r3iKgn@Q!+AW)j@ewdaRKGion41!nakT1?MEnzg?UDQ_fFUN zW(o=l)L6`(R^4@Nt(Ef}+zh;j8=rF79;6gi?GSP1?fvvdis`wsB-ux=dCe3aoxiGY z)@3a4u12X*=;-L9&IfTSr;}MzsDc|BJHzfClk`;DJ?NxQ=rlL)FC((xDWO%oP6$4C z6q6vIGM)CzsVd(cKf3PC(fF#uqD^Lbh+osxO;BtF0K{+vQ%z!AYr00{zZb4$+@fx~ z{!8kv-Q2Y<4;gY~VKngiR9)|Ba^YJE2|d^=jV;sqYbQ5O219O#KcQ&q9EfrZ{aRqq zg?+R&=?^t9SVO_{L)1qnmRns9JuNIeWXk{TlWt*=rS(crvzG|d%o2z!01jDG(6V~( z{6vV{W)z(sAfDcO^?T(aC$#}XsPe_>rsBU(9s=p#4TT3p7gplUMv{Emm3j!p*j~3K z=btQ-Z$P-@zxrH8*$ox>eRTAuiiF2wJY)FZDi8bvgBI&9?+#eJV{F-VLSIV(pvr02 zkLSjQW&^kQ9_O0!i{tI4`ROd(>g7)v+UzYGZVzZ5pkQnXJcqxsV$LfxIhP6I;kPqO zLS#2J?c+F<=o3t>_s1vk-R&=ye6_~8mm=riJffUWt4NX z?P|N*9@uM8J!_>CkgGN{HZA~g4%y^UH8@3<8JuP@D(4x^D<|u3F`v!z(`hg*7PUOV zaPl1sd}XuRxLsPN?7VrX6PYxhiurlA}uWyTJ-r}< z5*lXorN_~Z%nEox+@dunXHIQ&bhsB!x;11?I#w?6uq*x@wSghEbi0(~i%(S~csxm*xyhs{7iEx{%CGx`s-+k4X1*b^!t%C^1+Ub<>k}T8$Oi+DE?>> zF30PS71P)L3F-S*hG}Gdk}Hf_e7A{!F+@Pzn2Nz$a|W6`Rdtw0&4k>~TQeNG)zH1q zMeqGzYIto8Iq=AS4zN;(*xZ*>hOOZV%HW;?Gujx=jFi<+5(CjTI8prWsNz>6LJcJ@ z9fK?*o?_CoXEh~UrS|&-gP+w>VG{I<`SQfzj72$1^$YahpfRFToQ#~bo>h7)={GwY zAIj@jRhu&Mj=0I^$KG?o@RtM@d=y*TbnTqFAN4jb@l&u)dwV$%>J2DanI)pKrl9rm zf`0B_TmZ_Lf%`R~ZzR+yierqAeS6<;nneWx={9w((>WL%Z>wGU021p`nMI;34M zY$cfmcdo|)G(04A(4sC zVs?S9Fq)Q_Dhv#Tn|q94UHy%vhymft_l8WjU*x|Ci*=Kdy z<3biq&h{IWtG)U%gy=@Sg{PI*y?56Zn`}}Lh$gE3TO;`&S*NqryDy!0MW%g?2J5j1 zulC<3{mn)oL~A$Rx-i=j*IbuJe!c(L@nGjERnVP%tK%>5$t8&q!{#G`w&?k^w=xn- z+UA65H)Y?oC9#w{$wv6t{$6`?QsnMiMxZdm9T+C2X#Kl0h{SE_s>)$*ev8?%&-`x! zS7?f+wgt9*rO4I8fQMgVg>b__1n-tpK6E6Nc4{$3W z=N)4Sq1-%M8Zb&n@|r2s7({MxfY&Fu#*w%WAUeCMx0v4(^4$%h0zQ+sBl@!?b}l=P zfX825B>W$dFgp%mY)s-OS**&8Aj~>dcf!l#k}RYvFUIz}nk~4;%qK0LU;0RQT5SxB zGQmGBz1o(ZX+wL#itXa973i~~;VhTy$ayn3k=}N~JNvr-+AZy4K@RSSgF@1n#=~oa zJ2dI(wV{aKVaqFT;TD9w!vz&-wN+$@n6?HgTg@J?Pu>A=NobqaHt~Ve#wlTv4 zi)d=QF^!c9TeSoVSY~Ec-OV3^orx@8{WykfS4YE3@XvA1E=Ug6eG%alW|28dp9x`re5g!)e+O!ispdqj*rj4VQx#aP9pu~nEq?aAQJRcrA?z3 z{89%07SH6{YU-IKt{n$?3MMo1k8j<&g@-bf@^m zyVThA011X1OxuW1HS2#g5porOOj*1p*}qnlZeqU6BZQqS}Ce);z!2$)tp8;CcW`6#czBMi>sgh>T&XE zCrvdfwzo=hRz*oI{{y+CifhYKk-y2*3H>9IS8uoL5+h754`bbRdw$b&#&RTFzT$j> z=^UJJs|8}@r8wO964@nU zi*rXE*Mex8f{1+GLEf>d@qm6(oE3Hx-usU+)oQF0$|vAcySDbTJJs|c_hz~X#j{;! z^>gpO49xjB>e6{FcU(;PhzDtT6~*UV+&uS2wpzS1eAL#yyY~jO zUfJj8r$fWcOwN2?{4heE-~~jxYCTgo+&`DN7xL;f=wF*4WSeFUWiu|<2c}!%=zj}3 z`vP?qPt~9W>z%!n@y;>UXbaj2`%T!Q{@H=E1!MUd0VhYxR{ZKnl}!S3R7`ZfTT7}? z%cfQZ<*Ub=e+#NE##}~sF+@W_5Nh2ieW<;V_EC#W15w}A98ye-Zn8G#FH50PxX^@4 zYW+r{ZRX<88JA#fPOEBW5bFv?d@z2DA7^t#?+s@t=D<2R>%eoQ)DIW)u{H1a;OFDX zTU?$Lm5A!r*+ZWfKSOp_N>IDI&16eXs{0O2F9~Q z%b72LYLSKVhu)x}GTrTWdU{*%qIwa{V-FIA&NkBFmP=w>0=#zxegt=e;z-HJgjS0> zYYM$ebSGZTef?NXwi3%7I$ef$w$#Mbse_LmzY!_S{j=Pclu`vuq=}u;rT7f3N_&JSuu#;ym{aKsi3tr z8cGg@KMfHl9k0jl$llUOd!-TWGQCm8q3hjHV=sZ=#*Ttip;)w!i0=}ErPi09BT#J& z&u)r{R5zxSSIBcO+cGFkV0BkzR1mO42gH_;v_4o8B{W}bvVKE5cT1$o?<$LMuN*(T zBma83AL(Ww7KrZ}64mBw*`I(NTv`{q%=soDxUpz-jBb2A7ANg!hUxV~q4TGR7cH39 zy3B%UsY!^)#J;YfZ+d0VFT6zRH2?R3Jh@H!ShDx~jJ-9M?2-6SM9$kXp{5lr1b6tR zq^|MHp|3bzxfBcxK`{H6Rm#oCP>{P%Pv7-$+|6{`GdlhLc`-x7RPl4Ca-N4rvF&y4 zvPJ~7?CeTc1pyn8QLVl#m;J%Zo2+P-iE?;H0_f=1S=9(hu3I`E9b<#%4T{me#*B5V zWzj1)V$(}YLvelBhH4W{=lwIZ|BCj94%OBm`D>r@2+})CP0X!27~eyb1U0i*ujc(q zYl6qGACfb3+#L6-9#J6@C!_I6Y!rW+V@JL#^G(Jboo`%g&Al z)nX451LKGOcvghw_cy;p_|9M{PxgwLp25#Ksl7XVT103#g-f}IuYA9`wE2Id_6drXnK`9J z+x(!E*_xC>b$Wb)H#awbpUX9YIdqAp5Rdr}RRK3{kyDs6H>*SJgu24xY{k1q%#MOF?KtoY#ZDtN5!b6I0xU1XBn*rRp7(EZq;;J zMIon>%9u}2fAsuWKIv8`b06a`7>Pc$hZ~}WQt12NkydMpq-OnM=LH5s^$*8sl`|Wgg*A591>&4JxAPZU|1y5={GxTf z&$340zNvoP<`@&T)R10^c`5err$ly!TJd?$fGDd;e-YpPZ46a@cN%xg2|<%qg@=KG z)C%x4&h1hhRxzG&GRBx*Ki=di3jSdzXu}rg zp$nOsBur|>WJ;bf^?`IXyzL-*F6Y^p%0T-T?1{%tQfb=4zW&bQ(eh2W@UC6&28Wj5 zE%b*+aayiUxwG}=>Qk@EhqdY7nPWAmly{`=uhB@0h>DJ3(!a9iImQo)h`0kgp7p0C zj(OJJt)BVeq1$oQuXp_dzqt{4PJic@!smOPUR_mQ+|=6h58l2ClljI&I{MC)kyIM? zjIeHwqn5MdH4%vqFWoSgd-9f_bV~2{7K@To2IEDoyC;%_N2K2Q+g!p`G3Lqnl=JP| z08z4#WHY_5Uqs(#`IU%eq)X;!U}9C%av*pzcu)nO>^=FdUYPp@ zz#tNni0D#u4z2$9jfO^SA3{Vf+nJC`u0zGU#rCt1R~qKj*5XqlhP z!gRj%cy>keMgQk$ydkqERKaoYm-5RLv(!VNiD6d?V$%(=%1TeyzzDl;@k**MjjZ zzT!0Q#LFNfGEY!ka$@hPS2sn6Iq53NyA}>Ce#YOI&orT%4W>$X*r@}0F(-qqf z?h8lr>;A4k;-4IAb5~YS(66V~p$ynlGU+dIc1Bn$7rXcS6k(&Ux2NaViaLPv7XkfxPoBOwFCSmcn8Gp~ z>q7XFW|Cskcnt~J757e-qlD^ssm|@U5a-uUavlA*P_$h6)*J>?f>f#)Bc|c0QtKed zwgzeQtQL8EWN^Q*#dg2-CNe*@SaOI_G;I}de$<<I)>VVP+3nEP}QzuR>krs0^#j^C=n2~A^xQvl{N76>m1v0W;k{YM7aD%=Mq$0;*Z=VQ1Tv-zHoy7Td}qsR+L2hxYZPlk;2liRcY z1WZm$u=E{qN!k%*tZ7H7MHTG!a3K5_yBiYb1+2+<%6ZsML{HQEZcs8twKtsC0g(_PJ&Ao19927t zX>5%}{;GmwQ-m_K_m87K+N%-y;$#916=MNA#@hKAaTHaY!c)C6_<;<>GAQfb$zt*m zRj!9mteqJ|!_(k;avj7Q;LmnPtPy|L?6hFm2E ztKfG43^4*bbZHkcnYy~NUl|G*Uc8dhFA?(6vYHZ+#-|&*blR0sKq;Hw+zb>UOPG3k zn{?aHKd?bYJnzl<^0ZmHT&{BQrR||@`n*!`(x%4FaVinphu$boW=n^|J**;LzJ>Kq zoF@pmRMAZvs#~jrGUNWlqElRXu@%WQA{ zk47xf3NDtsojUxL_?~SYFE@8IBuyZD_pRxgZm5~R1qM=3P>2qu-xr_(+ZE&;T2PQ; zKAIoKs6z~Ypp-^&uU_R7q=SI9lMH{sg)Ipi_Jhou$YQ4Yff!}#0SRAxk)%<+>-pz_ zfdOzh&&=q@xGmWFBPW4@lYExL z$(+0BvFyo0O79mmtqtrQe9zUdwweEY=ksQFxnbsMQErL+sln1zc7>sRHNTbDk4@jS6hsgNVINtdV&uZ=euOpr(%ycB{%WI^UWg%izL9@a&gQeOT8p; zopYNswCzs`{hO3C_EVkQzp6{!lNc}SOGqO$Pq_JbS^dv%gfcPNCkeF(M?K5RS_``^ zc=Bkm&BGCle}UTo6o0--r$aPf5f2FW3zBK-9p-GcTBv&NLZ6ps>FeP6wYX|UU04^7b;<5XnK5N@vGv#gFb zh9#sT8wuAb=1#YjV=SI~XOdaXR@?>WKd$5U^TY03u099)wJUXd930-})bC1B+%3lh z;q@{Xyt}IKQhbT*GLmwyUiDGlYc=B|1TO_yd)bL)N)pvQ<+l zO3h#7HIyyosiT&Se!_e4_|6K|?i7E;&0o@{Q;6+*y!hAcn>Ocd#(KXL1GX+N9i9-@ z=T(_i8)fKpeI0%dC9CIUYTs`=S%$kCzoJ5gQ#V;ElwrDCxN#vMlUUIi;M-uLT5_kn zd@PRJ-aAP@74u@{K7q<#xi)UJQAr&*FUwYzXod2eSZQX)P<=%*)@NxlCxC?aPNMF$urRAd zm8P&hId<-~XF_(BsZs^r`jJKZ;n-xjnQ{_rO1DG~MitckQA{JI70#w2!s(`(d1++GmAn+S)co{t)TZ2%>r3LScyGmZc5WDYe^7y)bo|UGM3YsOTbB7JllL7@Oj% zyCk!l;*yQMF?P+UHO8r8CovrtsN`lOMcACWdCujq@Jp!fLO5D0(@|2nV0lLFFUh2j zS)1Noagmwg+(8l;@RnR^#QQwIC(*h+ATme;wacwmR(lfY#-cz1tB@*13ZbA=*96U_ zH2c4m(!NkfJa7z3jlec+FPne0!P$J)jd)G^3c~q3b8{IX93n$T_KDZ^ib;A(3L+ts zzz5PbmQMGV3)lHkGChiQ=u1ety38V@1T5(GmCcuq{KG8e>5d6{;$@^ixE;>lOO%n+ z`19mIfyv>E){;LV1vuC3}R{6-lc`Z#N7@J#M$$VqTqD-;h47F$!L4gWA) zmf9YXS>;)YRDYYPU$q%|_VPJnM92Kx9=h)d_nHr|H_wcPzSik6parzKBY39UH46m8~BsbR~&dYP#=EoZhokzew;VAfC(h zh^6XokYQ6yHlcvsBi}qs41FpwS*PnN24-g8>5FB#fox(YQ;2+yDE1LEh&aKQwM+GE=@p{V=7tFnZT>QwdG%VHDJ41)Nf}|tm zr}G)`2YWp5u@V+krp5Vt9bItfvF5cA*o`A!h#d^ukMVd_w^R5mGaXmXOkvQM^dVxfy> zO#rY(K1Bl6pw~V`blR|Q<;GsIxNO{_lI_DA=R=s*`?$s4<|e)A0&m~8q0p(8->JE8 z6ScA|InZE}rNQryK7KR<&1yZfjbCUqw46KVBxY*-?ie9;#?egek-ee$i5Qn^Cl3Jo z2+9e1L?1JtaHcL=6+n5?i#Y+}@?u#ds9$$J^!u|Q{C0Q7o^Hhnyk0j#SIt|x+-~w? z*D&q3LxF!qb#Qe5=27;%;ta*vqjraQST5GHBcJ89r;Ikfxvi|N{4wgEM%z@*8uf!i zEJKCiT5VVI+4)7$7V50oTj{qim>GlyP2n{1RB)`jfP6J%(&Eg_d$-Lk;g+1L-o8F% zsK36`V)%M}U|(mk-cbDh8@v%);FC)65zp_H6@K1Smse0Q9mB~STEWbJRcfrbsFReHGxKWxPZFLP5(Q?_Jm0m>IT#9#`Z%>y+ zFBNsj^{L1DiFV;~7BX&6HPI50^d)>(WpX7{(#)dP%@+0NSuAteI^Zl#7D&KPsCl0- z4N}OV?{RU5flRQ6EGAR4!Cka8S`7Mm z6LoDN@HsYRQKhLGcNTmPze z77(c6707&N?t)>Q^VRB7(BN-ije_7~VO*|05&#a_jt*W63&$sDjqS7~#)4hxjpt`} z4+%;(JC00OACF?^>rN>O(Cx?S#&k!?c{=YOsBr;Eev3)>OvuH__NRrv81BltT)_v_ z3wMIX47+hh4E$8vxK63Wl7PC)%H6#z?=RKF=xfTxC;ueijWsnlbtDFrSmiW-ahLgI z+x>xX^8u}EcCtfiKtjzU|Bj2&5*I0YIao(HB zT%wGCX8z-^0F0bP6fWmIYQ_|@k*jUgk2Px_n^{TokpOx0i2Xr+s) zLO&!ZnQ07sH?;X^rmsvPfMiT%^#mmoyL+j-$tH@+nZ|fqw9oy7%Saag5ItulSX@iX zGX^~t6Tkytwpwrfi#OEEcJ%jSLNP8vCJ6d+d4xnBK;U%axVX6QfF>J6B9POw1eyvq zi?L@wsd0=m?N5D5LL#fokLWJho8?Zc0boQaq8s+RVUERD=iObL61+uf9Q^V91Sv+8v%U}V+i@GUc(;8ZLixFSN46(06 z?b(+ajdj@UMaLyS`sc-}{3!o3(onQw%l(s}qB?oE6HY z9F5OewM z7|S|d>F4}uG%04gEwDkWiAeO+K*anuGx@YOK%Jb!1{<0+~1l9R$iu?U!4Mx!Un$qJBt4EW`|}L3AAK8Y7V7L=*2ToVl2-F2`N* ze46>E-Fg%<%|Hk)2AW=7XK=h7sdlaK%Reha;P@N%+TX9S_nRb$eAkEg?NSqt;O?qi z%ZIBNB_eC$HGq#yJ;Mqwt7FvvN?N*B@~RkxqElm1?T`3v7sR%{ei<%bY-^=_vDFPz zfsuTLU3-cOGsBo}+=_bCd%W(4p?jJ|CTKU#hvhok66jw&o>5I@m``Ou=^N6!yK{s` z!8jrr5dV(Tjuce-X-V7u>+0$;czPJJnq_3frMtQIPv5X<=ekhe}`!F4jY{UbB7@^{$gEdYs z0Q*7p%&3$GXcq=}J^Q7mVh1rGtUuCGcU6C5T_c$g@pm8=JLsTpSI$cA`P)_X5@vLF z?LW4cjE>>RIqcPrG(P)hB~ng}czZOoMWp&(nh_tP_a>rU`L*=R#iKx4i6+_Z{-U@F zp}2gkm&*=+g_a5n?Vwm{14|G1Hpo|7|CpAIEexnLdL|~{zycYh46T1SKJ$R!1!z0p z-@MR-SJ?01H01Uay$sELCB?uMQ>e4fiTw1%?*$NOdv4;N#$E)Bo2n}ZFY9{jg5^cHq#&V z3dN{s&)zv;TtJ56n->_#Dg6n1=noz=baY5wZG$B+hqwNnJ9p;RVytMs<{Nfk;NnK< z|MmEAus#~%d7WTzx<)@yh9hNZ*|a*s&c?Q&P`vB|t1k64fE&xQ4R;y%bGNgUfmjSkBp(Xfjbit
    MZ+YuN-Q*EVQxYoSYCFeL>S0 zb@k|v*C%bBoy3p7|6aV(m&l;A5V`z)?btEQVb2me^Ft?o;n$1$#44Qv`ic`r*U6@B zCBX_Z$V1uRTkMD%U*fF&JNCpbU4lvk!)x0wA?QxHgt}aNuhd{V61W5l1rE3Xo=er`^+^b;vmkk*UsG+5Vw`0zR%d;=vWZM?$AGo*k3@+Fbk9iVhr9Y_zo zDA0%Bm4URsC5VXp#S2X*?vDTA0^A0oAQW(v?rR00j6-TSp+DK%nH7e^iOcEWx6lQI z@IWHwm!cweh`SS#l`TtiSIN=zt*H^{?d^r-5dliA=b>%j{H_P8i3@NEFdzBu8nP`6 zS`dgzB#oMf6bPw|Gz<^LQnHr!5%UH&wr+C`TI?3O|ow4i$*OY69Y`fc2yW6|0iaEm(^Jy$PMr&u;&bBMeUaZRu zm;^Z8`#2$S4=dlydJLI-`0z6|wK*)$6OZ{L<@jf!1!!1u^{1YEI^*r)IeCJA#M?5# zQ)DV(u1RQ7{-_bA#oC8vZO5*@C({)qc5U8?-aaz?bbbzU*~3a`Wrrfklo9BFvyaQQ z5w&iPm+W7h+Nj&_h)GInV9~hbKccVtt--g?Xj=5LVs0|eF!j9JyL$3fUuyK)P%Z|` zhw(guHGKRm)jT2O`uOe%u{V3siz;^b0=)Yd%DBs{i+yTMre%zY0-hpdb-iV!eR*sH z>~G#Gxt!Id{-i&Z>Wb#IFLSZjf2Q6MUSI$q`D8TjMMCkdg#fx0?g!U(tO%xHlzuj^SD_+|Y8<9aynnAq5m!GpI^ zQLUha0CaSu$P5RW&ILB(kVD~gwBZ{$d>$PFC7mdo1*RYfhY$;~m*GZET75cT3KX`z z4lXTl2mI|3hr2RW3h(TAyBjiy!pe~ry>VPt9*|Q4aMBk~&sz#HMcg3D)`(`R*lyw>=O`PNP>*Ik7Qw8DL2k=P}4H7=VC<7M6h z_elUg__SQb^ggKEsPQepV=v|WzCSRBzCQR9m=CF4N;$QUOlvq_4PE{f5zxWTRt>gj zQ;!<2VEyz}622B^ElO#DbF0K=V@-*hlubbKjYYt~oXJc8|OMZTKWqh&Q zGXWVvH(6n?V)s%2Vei}4YVb3grt3sxUmRS(dir`##iB1KTzoE?H+;!yX4Qk%?C?LW zpBX}7F1mlDm6uQR+cxfI@e$!zK|IcE_k!84%=C0-ht6k4G7w1-!W^FA_*_n5{%=?2 zMVZ5+8tL=+3+47n-p$ol9-^#NbMobMSeXM07ttun7yR>~f7RZkWM@D4by+5L(}kmB#8i;Rm9%V5XI&y?`vgmyG^ zbWt5064ruaR$YT5zEQA-f_4Mr=~F=vWaC1@aY8}@TGI^d24>4C-h+b!YA6?ADZX&O zy4d^T2q)?@y5AGV9S2m1@Kt?XUD9i{<(losU~U0M1~;N{Cc$yVD}on-+`0?{}o$#~Q`vy6s^!6BYKd{MpWsz{Rp2QwIw1-u`|CuHoQ) zNMwn4%y!ZP+u;w;D%QB_BU$Yxo}>m5BrA?DGbS^!vL_k#pWz)r-;nP-|Ks8K@D(#_ zL-Bt`&+XHaFEH*LZPwRTYvuD6vskgDz@fUpd@GvQ=%>s092P;((9jQ@F9j8MhGn)R zuCq3Zen=n=Hu`*^&0ZKeYTz9&I!M-%%rzfpw@{5uMK=RRMZx+(l7$(z%isxeGO)vX zwiLXf31RG!j5_j}9~%h$aXfa&e=AXKiuJejXQg{U5Flh>wf>oeaB8dh=H0EySKhsA zWv=O&nH|BQ)m^o<|FnnGf=I8$i&^p#JulX1q$Z3MhnE# zE3H%Do8hI#orm#*l~b~6j?nT)eySO3>TL`gZBJ>+P*TAQtut(%!Yb5Q9$SJlbvl*tWf2?0c}YZ zj1Peg_!JM%SBg@>fgNJuAOjSzwtHx3?JUg@J4U0H|H5v4Xc0t^Wjt43k4F*&+-0Da zIN2Pz-v5d7duwc&YU4mW>vd?d2jWDZ^4-Pu)`sth#TuG0@lJbI<|iVfy2 zomu4frb8F(bCu$eZCPqHz;gi9L&4h~VRJq7^=gSB;qgM`9d@ff9Q9570(@>34WZw~ z(JZc}s-w#XJLGDm2-50}9uka1+LO5Gu8voccM&);IZjYZVCOaWy=jlv;LPVW(i6Ig z+59j~h(;VOX1(XP4Aw`@iXZyC0M7lGUVfKn`_z2TR6gT3y1qC%9CxR1ZIdwY00oudE#F%w?2f>7Om+y?k|DNnL|rM}07E1!G#Jv>)WK9=#b4>D3E= z0{Dtoy4sENo?CgUpCT(2KV)0TV<&DD398w*D@( z`8A~PmI;|jVpcH z{_TFjvk23+tXzcMjV-bEPd2$||0IPlOAc)>x%AM&pWrfm_)%%Gc}IHm&+!YDghuPD zI?166*RV14m?da8WALlVDL^`To2H$7FjlvLb|K!=KB2E*!&6jO5Wt-{2rZ zFcZDXSs1l57;&6n_Q#Uh(YmPv0iWRfgp##g+g zVLjQLuk%vVT7Dc4lm~Fe(1)H3%mKkS9yG(0!kWG!_24GNy1-_W>X0nnYSPtL^Lugi zi)4CdB&F%4nZ}-bim9UP6~R9dsg#SEiYJsUm~IRZaG{u?=nI+`2rm?bD-4+KoyEa< zdCZVSqr11z293y)!$bJmu#%9&Ku9b>DrCTy2w7i%dB+I2px~_T=LvbEArnN)ZCC5z zH9)sGe{s6cW;PT6Hg9NHFTJ2Co0^(Jh8IDWMuGV#3qT;`!ATGwSZ=qDJn+Fxp?&X_ ztqerhLvmtMYwJBAD}q5BbdfU)`hrOWj81lD_go)6dIS&C+wJnq@N^s6 zKh_{Z?jN(~M=;v}vF9n(;$`;As>!?bUswpjF6)mMx2&?q!hVDk%*##20}iPaFO&0( z_Cs(^5Jv)idB}7g3ltA2I>C=RwzNtzbcv=aT+DtX} z+r$yd9Ju&+?(4$t3dmWj<*H~~pJC~zebO|>$t2VLp{(=u%Wo@onJPdn<8WZNs5^8{ z+Tg*hEvr?4{iQ-W)b>w`h^W{nu9=&&xeA{?(}XNq$;NYuwa$M=}W&;HUbO)|@Z=nrMgeTs!XgJeG*Plcnh`v9h{~`H5~5(#cd9%RVn+ zG*g~(IbPgD=@v?~zMdlb;4q*xdb+G68t=cq(1z*VJ1+15a1{s>Z-x;dOBE?&Xxj?Z zlkj?w3=(LUAhQnQ6C0t02dg62q6x4s3yzN`0&TxyrgETUB*Tp!Fac!Hu6zS>XlQ*) zE7H(+kbab8A<#dNdr0p3E)ntfO;Yw1Hh{W^!}39SC`Qs9ulGh?Yz$;A_)nJzXfsk1WHsG zb#@oz?&U0Y{N}+zd9*bqOO)$`HzbJd#c0@{Dh(|#`3O<;4Z#@!uC zz_2Sfcx!C{^p)AIV{dJ3ft5{fj@B!%{Vo{2L zHyzF)0+i+=%cZYD$2@cXJ%@v zUC$TBWfFLO%gf6z_9e9`knGa5w*69^7l;d9(O6olYfAnL{8;6#%dLd=2nj?0;edYy z&La#QxA5`hBUUGq>Fq7v+q1@DKc2OCVqV2*DkqGzj<s_=wPN(-Q!;wbS)r37UyA z^~3{bEX@5F6~>g`3b~||l&4@KT9;}1KrA5o-Y=~CX;Qe-DU@BiM7$WGE~#AH6-+i$ z7#x%TVLs5dF7&Hl^GA|KaN^!=eDYHc=EsK@<=K1QevZr7gOoJER-wE)|gOMv(4q zq@)|98)@n8+B5I_?OwZocK%Vrz&vrzUFUM5$nI&fRMOAnwQok!CuH?{a;Ii4R<0oH(saoV?CDCxfU^Zx-&n8g{!0d++voDktp zsHnduCSoB)B{VMXd<|CtYNn1Tm@opWKd1!?G#d)!l0XFp)lUnUb*P{O0q|=$Uo{%e zZ8rsaa7Jgqri6-T`Ss5YxSn7|X~A6+9*zYAj>2}jjgX>BzP7?5HiYP`Kzz0^DdZ_g zii1?xjwE1~uFq*0H0vWO89x5>xI}dkR<)2P34KgR_+$A^#paUD4?Si)cqKMFvxpK^ z8cK6<35m7oYLtWP$v>8&rPpn19cZ(9qDIR^0H}64`OF?~i53 zZ!BLdXfI+Pr#F1r=KE`%mX@~LKijO$WjCKotyJ^^ay}pf%^Uu?03^ZWLR9aYt4GgY ze}2)-H3OLfFyDLtvTJFlT=L-3+MF!+2aEy!oGQC5d{7F87jj=yxk6kdqDNZZ8oE~i zxdFGew6q!n@K~Va3zK>}SGIPc90Yq6fl|TagMdJ>ZYL&mF3f>|6`8_-+V`Hf?^^M! z24CDpM0>$v!NkOTZM}kKz0xCnnrLicA@$}BqVxj<^#@G;1$c>(UDCO+wXMw?;y0dP zVKqbI6eIw>B@d^{sNPMJ$!=m@7x3c( z3S|1bNTxPIuocoUHZ;^ek$$JrysqKpg=OvP{HIzcJOP0d>IcQt3ORltYTn+8M;hME z>fTGefxq{)+Y%!i&$>)=Dt-Il!xf=Nvgs-ktgNhZcGr%P+b~(z;ONy}C9B13E!t2y zK!eA*gbe=&8A)2}T|ZnX4D7wI^@2MnH$+qN%)RRRM+rQ^!0>R(z`$duBB2kDWzzAM zaZQ(@((8&yhC>wIZm~5T?{D9VOvdP;qVNJ(yREGa@|ds~7z$}G4!39G0WAc;yJL60 z8Q!2zunIU=4*vc|mi)oh`sMCJMh=el+1W%GEZlMN|HEF=`$3=iw&O=t)oU2O{vGN~ z=%z%Eis3L94kZa7=1**Fd^3AhsoS?YS?64NY4K4KxkjN-qZb+qR1Azv7%FMAJ}hz_ z`#STMQWqM|L--sRY?M@0OW~!%S~HxfVMDNv&@`x3efFe~N*u_RrAY6SnD8_u?pou@ z#Mtkq^?_utWavbpC|aHZk;&f4i2-Ei*1BFOWY>Bng(q98gzid+2Q(eKk-Gd>5{q|l z?BDv@J|iD%bNBDgy;3Vq`+Zu}omuH}yQ6+T()$w=Wky&q7Jd=hN3}YqNO2e#|C$ENL=acQo{%5Lga7qb>>X$XfDky~nGgql^0$6)}@eAM?kaZcg7z zChv7(#{e(f}_oBh`-&8xnLuB7eJ-s|hn6drNQu&}E`&WHL-h!)I%%y|Y8}#5BL^Ze3#s-T; zym#^_OQjAcS?ayRA>(%sog10D(F>!>{q?ilcjxEHj-TkZdNQ1^@MViLa4TROFsJh| z?SJ8pfDQV%_;_TaA)m~R@nTZuSn>G`Yr8Y|*G=lmkR@U|UgQaEiA;eSCk#TUhIHl% z*9y~#cW~|h85xEnR7XW-X9M@>9i?IV{F8tuOhW-7K zjRbCkRA=`FwWo3AWvt7NX$=sM%a7HZ8uD@ddN6WdPZaYBc88}~_e8A@|CNtKk34~1 z(9ub~yK;jbhHLGVbZ(r|qcsyzkY!4(UbvmEJA+5=Ynl5d*r zJ18IEl(Y`_B>Y*-97wjV1T(_S6o=!*ao{D@!T5OPbhYhQ6b*^e&G5Ow2T)}t@LE6U z7u=arf9;F{dH~tt1tciEwDioHle%%SjyI{~b4EUB&Uut9Q%+hK3vF3j>2T_$lM)|Y(tyX0&rOQw{brjkfze5`Vaq1KVG zH%`4hB8bE8v&HsI^>6PwiiXb_j?I;?8;Gy)-RLlqS|2Bc1f*xvudmOwmnBY!-28g> z|Du-2BBd8oFpZ`e0tz3c_0E>)_u@k}-U`KxSZ^Gg^^bmNma|pLsiDMzI84s=Tk0?^ zc0Xw-Gd(akI2yN*g@%OR>F_=&Y3lR;3%6XFO5}0;Qd7ePpya;UnmczAd>9!SX+;qy zHlew2wB;f;m2FhLzR7;}^EG%JB334(vukn_$*iWBFq z(3@2>*mwHABBuzY7F#xhl)YI&gAOYBW4EFeuaGGbOVb0XA(w2E?wDrnXCc zTCgC6>2n#z=oqlBK=({7=9bhvtlXfZ_hYY3M-rA_vq$|*%xl|6z*!cjqL9ifX6i%} zO&k+2FL00t68M@-SPd79d2A>$usB{)PTFqzNqC-^^eGm~`v6GrS7DYA`D9|9#GaBT8#{|B&{UtadA ztmJ^cxD}=!!*vev?2*f5o1nW+)~Iuepi_;4mSq2MO%_TbL71XjarT;vOD0=}48aKj zz5|dwIv_@J9qE9{L4WF{T&geq^UW>u*;)jBO2A?MVdGxR#I5>Z0&mT)0Fh+|5w2`@ zrh*quY-G)a$ql&%3-j`Y;@umDKPO_{V%7KO5`iFtJZi{&^hEfqfI?Mk2U4pACIbN3 zWxYO}Ekzx(1Bt9K8yPu?{U?pOByth(Tvh?R5Oev6h(~y)#ugERM@~+TO{)|F(i!+F zrCiqIkc#R1Y5A@Xjl3?v2j`d&oV0yCeI(0Y-v=Ff3V^%_9Ibhi2b#7Q;J^S_O8>iT zxv@ORnf4A2F7jNy5vx|-94%e0S=$(ndp`-kLf~u3p-=jon$fB`WcfZ||z)6)Kg;t5o)<(c9tA+$G)-DitL9 z(BLG(C(8n;m0oAp5okni8JYNw9UW?KX1$!Qb$be7MbuS_pPG+d_mv2_?3;&&1Ljuy z8Z!+H%2>-OHA-0PTu@xBKv-5DhfQrN6kfS2{N;aZ0nSHYz)-r}T8Zhb<6~0NMbiu0 zotY%?MqjpH{Hb&n67TIX8ZDtqb&Jm%C=krAF;zK1_Apv%_Igefdb?&P>4T;883)*E!aRSD=VBUr7+0_!S@1Bizw7! zxCgWpo%iw)@(ENdau9bTBX|m5OfrZXU&4^3^(iCp293?aQ7&Z}i!$b4p~(K>=c}0M zv22#F;xSfg%`NQO`c?M5O1k3fThFfkjeYFkg;o+#9D?$3Z7|*c@}^rEGPV#&*J#fe zJwQv70=)<9R7Zg2DCly-fI@`~bA*3k*9f_0aG4cJEOvZ-eH{_W1}Bbmt^yroSbwXl z;|2H&F%5+O`t&Po;p>xzn?LY^&;Bz04-yP;F}OF`5QAho=rTP`tLysUsDzO-10{N5 zJv}}DM)L^(u-u&a6%AML;_9jp6qYQ3yk?WEq7jsyiHRCPj`#T~z>_*CK{HSk6^--Tqj6eWfZlsz8-0k!79w zR;}F7y;!5aS#QyjSgD(hMeU3j66lapz`3~VlMF} zO=wJl_>=H=Wz>ofD+l|ge_6)II$ONB4mNA*lSpiEK=z2#dy8{Z9$GAWsZ?siWsa^^ zjd$oFS?B!(?`iKk*-*N5pIVjZCsi8rgc;J_h3#dwD1+aHoX!rZ@9e&(v1IygHUWV4iVuI=Q*#ACR3 z^tAWAxXO=lvd$WhMq4JaPKj1WQ456<>2mX9{ZKx>E2 z&CLzz)LBqio}ce?Jh?(eBjEUY3-Ax_`ABltL!P;+6(pzPmay)a^5It$ZxUS62sadjY;Ff+qvvuHP*b5x#X z@pNZ^VFri+7XaCdirIEgVXpwF4%c{<L@Fkf_^FEfhAeRwNe&?mz^<&lM z{aCPW*V5x!5Cziv42u93AP|N$ldDJmetC%MI z1Q@%_kH7FeZ|LE79upB7?Yz9fGai;B_Ug{SrPug|TfBoE#_RT%a^~KljIhGrla=<8 z(KRtO1LuU8H!fC>f^UdJB$JL3_Fntcr*U6n;(om|VUJPW8tgf-8C1%ZnUNNDo|;FE z^|9|zY1!vYlv~C`#lZKK?nt1># zjaE;IWB5nuvuee$8m>v&1kR`^iH+p!?c?Rxgh~06?g7jvxbFhJG$y{7FrbiG!7QaGQOWrbQeC4_RXW(!^5MIxC_7pX$6H0 zxa#iQyT@WYObO%2Lgvhu3BV9o3kD{j+oel%MG%`nt$Lq0e#=0!;5K?(MVrO>YhyOU zI3uHl5wVpbqY=!NQ;QTUHe7R!>WvMW?z}PW;kezO)C7CX$q5Bq)RNoNhol{Ae%>dA5Q&ZDY*X8&4cmg1Rt@Pd$OX%=N=}5hw^)C4N^F^!J88 z@*Qr-3X(ScutUJ;Cl*|3J`=ZW3u;=+>r2PjV)*BZ-0+tMny&c<1hl}6B&WO%*8(Vl z0ZNRZ37>E}KRfHZb9wiU8^`T0ZtC2xJl*?nt>saEXoz;j?&z$E>8z6DvAF7etvAvu z919}>+>2rNz{TG;GD?C$ z%UGPOojHN4DDm634}1b2WM(?01!pI+Z^Pw(4P-iM(B+AF2o2 zS3=}{N-e{d?(UNRgf}n79^YO8gX?W~N?3GyknRf@F#!;9h(5n3mgQgyT(S8=SI~wz9R7Pk zqmcI#?Bqa{N}PvnNojxYmjiLi%D^@qtH~5+^I4?7!BFbWXX6nmI)AlNbJBA({MKoz z)aCW^q7sB?W)L?r@x80z_CjMWZa<}3-Yc@rZl+J8nB)%5m{d$yPG|E|{k1j!H%wZ~cV5k1N%K2fSN|~&KIUoi8`r<#| zSJpcmUT9dhS{sfkyHqb%f`}5f%sPjWz}!n6qyY|A?R_RT}o$H6gG@GL3%qwSGZNxs1lA<|xPw&~yf3l?Pix zSVVUNUh$k_ucUK-%sZAFF1zN=$3UDX1ov-Q-}2@nwyiY5E+{;S+!yVfGIH zO7m1SHmz>O4!zkmNZ zpJ9rjCQnDNMO2ihmiX13p1W3ou$7l8AD$sv3WTl*(LMl=akdo%$LyPmEJ3nA0~MH$ z9&ui{k;41PR{iIm`Wwp!aj{yuS^eoWl@PplaZ=d0QTfF8!+10;iG97F-vhW=`OR*Q zXMBLv$a|1(9+tJC63BZ`5+QKfVsqQ?A#2fQDmPY;RuX)odfQP*rTeZK`qM7#C+swr zvSFC@`-ht!W~Vg*3u2KaXCzY%G^iS8Frl3~CHwE&)$;rIEo)G1l{C6Xn_$dh;U zq_@1gc*CU4XCWsR)=NW49(uP3F~h^86)?}uA1v_!w%=oc>xQa=Pc26$`#7CvGJ+hj zZQv1tMIP|HF{bk%TdAHkaD8Oyc&1@5mM3X@J3noCytJt=lq>ofR(dne z&O^zzItkQVKhx4$;I8e7xxt;8QT?Rwg6%t3S;IXOj;%K5XtPwi7->d!ktk4E>nfru9*4Q)e3p~&ZxjziqLz(O$JvMyE% z0C$>*h%ZR>fVuqwEiRy~@5NM4f@ylqH9Gu)h!SbzIU=>m#KhVqdVBtiaQxlQH+emF zK5Mm ztxaOT=0@(=J24-S^&ao;L6}yxL6GT9_ITXJO65bxCd2?47JW3Z8Qglo#1smxZ?Qo? zQC@yNco^L3&sP5a^#EY*2e*Og+KUigK*2LWWKd>A+R!9up7IJOIhRb1C9FNqa%eB9 zQqIX#rZJ-AX*ZKq*E7FJYI;H`s4VJkRP4;AfBR?trlg{g1&b=r`AK8Gtt{S;JrY2v9#R7t|-^O zKKygIeqN^+9T<5RY*^C0&Tf zqTuej9wW`DS+@m;^^as^r#+HWyo;d3UJFGluisbBpSQ)4{IABON2*ZCbjSx6ZDTE&SC}tx{9(A>PoR_JvOdo950U(~9#^t6 z(|G^)Ojge3yj~=`|EF}G66?bYD-hnPgsUnt6kF|Wx|>=I<&fQZKHFZQ6J5-(@x5^F zf#C7R7oBBeqx?AV zZcpIx=@_Ga&$G4wJW}3S$dm$A-zunxK-ILIYH<4|f=Q>mF9GJ^4(Zi%UK;C zUt@bH_G>Q8IN$$)UqSk}kMj`y5Mqr}3`{Vt>km9SA7(7b6yl(wqCTH4PMWVnv0fE? z%*WCub`-eD9pInIz-YgGJLk0`3{{JoQnX;Q*niIce7ebh++Jp4H8@Fn?6;Sms=`24 zdVCWf@tk`EN-x3kti&lUQ%w>FLsEn8l&|{PR#(Fp6AV5~f}{_<`#6SKSI31eFpBJ& zQD1Ujf92HWQ7J>}*4snk7fao~EU%6>~^1|<;u^KIiT5#E9E#XJ8d)X<~)mH*vL z=|?{VT!?p!$Yts6=R4_}VvEq|0s#1o%@`XrdRP5-x^D#pNbK!CCmH%h_;a=KiOu?K zFcb1G+3Kt(2bqDb)>#-xIXV0%76)}xW3il{Uh@s%YH5x>@gUFV8mw2<^gdbpFy4+4 z+eahQL^OsX5ySQ;K8NhVz;HF1rDbv6wA%;jp#m`rC~eyEYj|4&1!!XuO1KrDnx;El9z3M@JAjI$z@X7tjb)%zY>f_3TXJ@~Z@nj< z`njD>Qvy|<+TZ?G+0}Heplxn(=is1WVzA4Ggck)|F?sMOR{Z}`A zSy&B^=g?x{fBxUcCD*I4h)pk8D0i-rFOP2>u+c=xWNMI#M)xYUsYs|325VfUwfmx? z_QY}BqVUmH!W~FSHhF43Q%lZbaFa+loU0uAu5BH=l3jmk;JT%?O+UowpUPbtS&6Rb zGHp;nziMeWSl4XH{2VuIodG}D)cg)~m8T4QsiM^>O3!j+Dq`+pxq4#}<5K(>s@Hp8 z%fqXCcxR@@Gdt)z?(hC*Oyl9b1Paz?3!LJ%A7a+O$1ssyFjq9N=-;KQ!gmPG%IbLD z-`nXF@N82g+^G+5rCasjx{hn8E8i|E=?A|hFZ^9%F{)4Xh7F0~>V?6>3U0p+y7v+p z!^JS(GY4Q0Ep{({MkT;Vhz#;=)%Fhh(Y7o^Tz$ir$eU>n(mCI_xEj_s+>VL-su=J; zWF2$I%C+yPAY)=-Xn~)5nvg&xTaS4%*FN;8SyoVgVKZIL>Dd<%&z_4xc)GZ<^^OYC z==)Ark&W%iv@=DxQzUcUi|gxaiw(;!#**AuEUUf&j&!StLD6%o{dV;Qebh!jiBgr* zy&>F!m4Viai9QK;S0r;f?xNL0iS4@J#h$Hbw7pnA5Wq=Y{FoI~s7D$$8cxXfVZc0G zAs9;%D6+i4TW2*+%r=8d%lUXS8H(bptSsSn5bk}v=%v}YPbmouH84(6ZogiJppSZ) z-Ez1gXTx$C@7WbX8SU7kusjRc)+hSz|1q`K|1GzcA=pGWy zx`ybF;7hB`;RyK;LTr(D)p!*e^Zw>#$EwtmvizAZIBe6W*f`fWISe$Gbm~7+YDQmA za59?>>n<$T^LN{ttOVIxOjy;Nn9uxHX#N7{E%T@e_SBf!^19a>Q#RV7ks__xG-ZBQ z256A(AeFJoWvOJm(DT24m5%6`roQp=Hl;reazS+tnJYl7{w3}P+r~@l77C3zw2XIp z6kbaViTv+%mkb^~=a(929#c}88fdO#1HcQ(bhapNc4l%Y&Cz6=*RkDmaTOs)CiOXv zURP?EOmgF$G5V8%%(2t(wNFMonR4Vy7_G3E-Xpg6NvJ{lH6t$Z}zQ z-4BpiP(hJ`LmC#(Cu3t#ko+N72XO4ksDn5ijetD>pUwDZe!fJuOnQ~m5jD(5O#PVr z4(`Z5pe#mInnRAy-6+{OOgr$wm66AX_vs*2Uf$EY<4h<(;x(6cG%T|3KBFvnZU+^A40UiV$EnU&9_Y5 z2jmlAeudt>;~$6$L!Gbo`;dXk6}=dGcv)Zjzzx--I{j`EDc`}$vPoA2Lwt5#i61lM zAinz7X0|q7*2?0z#~Rbc{-~^QEN(3|^YhU=t%5WA%SP#S_Uz!WFifO)MRVPv_BU}A zCcnLe6vwT%U*N0Y-$}fF8?roa>pya9ff$Qv|*G zH-n6mr`qL7coDt=+6LAfc%weEl?5}W@BBOD0n#{imf$z$70$UU3atW?%7rxv{YY!oC7yK z2``_`zsFz~`w7k`&;@5mUe&*~fG!Hg_x67m>qV_AxYL}1BSfhCJj%)Q|&TE65dgDLJ)=@NM1r41tQTRw<{++0zWl(yH&tz9B;1crk!a+ z8eoZg?R*z`hEE{fo=@~AG+TLrT%TOIkBN;XvXi_}dd%x#a6ysF1~s)}VGkmY@nZuz zud01GN|rk!eB<_`qzFJ);f=kH;H)}X7ndrWeHI_5rElKm5&HFWwx$?YVMsX{)~e+1 zG!H5iZxpm=oZmz2i5<=^?*cIC7_L7A@t^0xyG@M)9w}u0OlEtu$ELHb@zIdwjr@6Q z;i*bBYq~}_3hm53?mB+9yfTi&NvfD8MqGtiP*f1RBwH@CcGojI^YK2P%BCGJ0B;bF zHyYvCxUJzVSohUr16yLGt+sfo?0qVF z*O@;56DF5Rq9{LOT0(|{Lb^4(Ccq^!rw^765&tDyx&)wg;_N-a@bL!{>7c`W%`FBx zUh^E%6ocrBZ(eQ@cr;ms2|E02tZdm+KbdsM#g_*<{b^OpeX*cQb}M(!*!L?CQlKnZ zG3vP3L|i~r={hQno0^jGeoWdga^O`zFt_Xp2HrF zpf|boQCj+~y!=xL#uy$MfeE_Wz}F#x$TkWz^(CPEH#IYZ5t4e~dE#-uP5?W&?s_~A zymm^>dLH1g5aM*9v7{MC&$;jz3#P&SSuQi6^Nj>E{qcLHZ!*vI3p|H&?WDyTux2>n?elH+ipCc79=CR~+na&)L{*yqTN7 z!s9kc8P+!xri8a!wpfJ>?@V-r8p7cn_D^(L%@OObu^|Lv?nnk1=&ukYZ9cjBYg>Ad zLHu4AF&a%K#vtO23>AY-GvfV2&SK!!%)6j}N)efANBXDYu~HrFyJ|4U z_j3ag-t7jcacbR96!2~O=o{5K2IH8QpHI~3Jp7bQ%#t9*&!4MR5#Ww_w#>K;t%pEM zW>3Ql_Xim7#l*OU&TEbiBcg~fu1G#XHdRavP6*z`)&Wp}rr0ImeFx!V9&?OirARdZcF5I z+E}QyC!WszRwv76N!}Sz8D+I6M~ZLrX6q6c(#4Qqt-pC`BDQ^RrrX9`MjJDDhd)it zt4DJ;^H0iq8tBzfMaMXJGN1E*#*RKO|0t`7jO{KcvoztLJNIcY5V-;$2m4P22^EuX zNF_0Lh0Z6vw?;n9n{)Niy8{6?iN2@428$7!dWbg8&AyD4E#w2AAm&-jPyY)lV1`tG z@j+-9k686Xp~}Zk@I$#PLKVB#3X&YXlq}rCOXkx--Yw+HMdX%kPTTew&_A zx2H_)=vVt2VjNn{{sbygR7a=S_|@Zqa-D~5QtHAkF7FB^&pE=!Ho^!lPnE?kK-Hk* z_A7GZJ{>Nz&@0Tn`QMV_{%q+^N%7cL|AK@MRaa#Y2lo=E<8@&QXaqu&NK0@p`FYRL z{tkDh!eLC&Tzy*gdNg09AcRPah5(xWUDX;<^4<93q3$6uYU}xFqqDJ}h5ZFzDVUhq zlZ~$MYhA-t6iPgl-=d)fR^d}l4Q;iJy!k)^Rm&udu+kFN{kF;LaC{otQ(W>NZ2y6} zs`5P{zFKz*EF=_%tM$9EzNt3;W`!hbcIIVcF$c7^wqX(ydR0_NW?28vnte(}u@QI@T7vR_9wlz{86u37n6Gq z#(eV2vlT@r@<%6Wsqxlszd~0{67uqXd;VlRM&Vn%yt2Q%BL7m9r4%-FS6HJqc%^fr z{}uU7^(|`{a{lED_QQ;;XJsX2Wv{5HL{ui9BVsfLt*b>;K63>^`VH7s4~PD-Pi~e^8Y#jRyIX6l3MvZbP7R#p2s!r zW7$vGFLN^R2^#Q=yg>eOVY9rDeK1`YyLVze*0%M{``g0og|px&^pDIP@NVppap9X0 z=A*4zHTfd98xGWl9#%m);@u{w#ur~4T$Pfx7E|p~V6)g5%s+E}@^L(>gjalRlJ3YMh+CnV;RZy9$K5wAng|FlUYdhUGt^L(-)zN4cb?(!!NYdkYt z6-&(*_V8uF;1j*(xCW(a7hu<#Pj{~gAFfEQiZ@!~xvHqZ*fQIg1+pz8mAP00DOs^D z$Kb@qxJa>y27`N=4Q?-Y2OTpcj+1p(x0SF(U8R-k^y&H%_zJ5^KEr!ZBy;8Mm}}N!xL9+9qrSLYju>vZBghL05!}R;60C|)nJSd z$|tbCSg~DLTyQu(WV&*)6$}e|*WNe7@*_O_sKeOJ&ZK~nQ#rWD;b8qPsOe&2QLnV_ z+}RJ~U-OzDe_8i$ut)yogsjlX;?_j00wHhJCF3`Vdwu)Nr&O+7tmJ1mzkk{nY7W-f z@^(cOwnS2V%A0fyNWHNC%RNamcCW&xKwpE$Wg<(DMgtA14wx83cEfn;ZJvBQK2sL!7~|G0Rl3(i z8iZ1(d*Ma#yzVS4zW<%$9#fo!rEj(4!~IAo>#e}hf%bNCvj~FO0Z2y(3BeJGXg@%8 z`2iSAN~I#Lg7benJN*zNa=O3#5Oj^pfIXL)PV8r1S=rek^wH39h?aJ4jOKSk6Oq=A z1Vku8Lkt~6XiSW-l@&91yMRE{($ftnY)7snagajnW73xzrWyJOtDCtp>7N=MC?`d&ZTo!~Y@+LcK(sO<9-+bt} zcpTNI(Q+1U@N|#{#~*Kv+go`t!`f!WmMpp9Gj;p zbuN@;&wTkykv$Rq)y2DHuh8~6z)lvL^;Xb{tcb_FohsO(Qlc#1e>%&aO(vg#!`=`*rxO0zY?CWRSjW3CM5Y3h^c(MK7;q3gvxzJzH z9O}uwWF3R0$x2Ek4}VV%-+(Tsp_#N;|8%lCww^AT$!t{j{26lI2G{;4auS;?5&jL9 z7kngF3u^UFss$Lz#X39E)HN0F>fYc5Y~>8NpT5#wT)<9b2o>H{tq4j9I+A89Zcq7u z?j8I?L@hykj@bRfAKP2P!os>uhhEsZABpq7K(N42lnKYq%IT!tCa-8Dm)Rdy8lT8I z8XecGl1m;h_wxYj<{-p34KSWJqLLxXZTBj=g?$hU9MLWSLOsiC#-yy0>rVyvvJT{r?Ac}tt{KL(xI;HGz z!o(0cvti6;w0|E_on_v~Sp3LMrjaL1I$c$?h24z<-MwdN7wdO~Og0oT0e{JP0Lvm(+H6Y_-g zp%?TE2|p5+0_}?X+`|+g0r=H5G@!OvJuR$faGb1E7%#NZsE$E)&&8PeJi$Egzh+IZ+ezc4q1`=q1B2-RUW7 z<_o3HFkY?P=Wy)+? zFDYiC=Dw%(q_vsGz1uFT#kL zb0Wl;)c&L6Df@cB)O$D}_f#ySiU}A>cN@*vlU!STrwXeHpSMj(X}VuDYJt6tQmu*+ zn%U01X*l5^#z_Lqe%T+BZ-b41=H*biLgXPW{=M#>U34 zxw%sd3M6Bh^^g!a4btH-J>)v70Jocy4VLXwIHNT2e}09<8;yqgUd}$;B=i zd*}hiZ1JwvWAzo{_D*N+nad#Se{QcV<5pV8?k^V(rKaWeOx6*sv9*NXm0gvdZAvs2 zpf0i4FxNlp*O}fFVc!4MPxRBH8t*GFicEo)$TUGE1t(dodgh5Nd;H+m{_FaiXCrDq z{`?8g?-fk5iuv9dzyOZEuY{Q z#1lFP-yX;@a)qi&sr-{qPwjGU<;T(b*CQ~cs#r6ZV>`w5 ze;VC48ZW{q@{`8^o(o;}K%GsI;|043!>+So&94C<*_4x4I z#M}C7P364&F3WL}7~L{;sIbybg9n{S)ZX?M4O*c1XOj`nWSvYfPd|Dy5C|6^b6msD zjG%vVOM0eXPU@Y8=L`i zljACN%652$N7!@^U(4BO_mA4TJh>IDY5as2pKbZZgX^dBf3u)!b4ua?!X}V z(Qp+66I6_&D&I)R`+jGz{NNHu!OHCYz0mTTKr1E@p_qfs^Z7r;F6Wp+N^)pSdvm4b z0wj#X3hf_-2(ft418i6s|H!v1DI$}qql??HF%C<&%V{Fhm0zw|+CxAvq_%bh{cJUUF31o}uKfUZoGrNx_A?d4?0;F=H@qXp;3Y z<9o5%1i&9GMuV@&9ZpjGQtRHG3rWRw=iQ~zYXigY>tcI!ApRpmRZFz7H0Lt;IQZQ6 z;L+_T51_wCf+#Xj!EUFr~59-oalV$teg4-v90!xV-6Q zqE>GgUy563wbn%!$C}rg7%28py3>W_9BHSs*7+vXRW<5Xw(>s{_U#qeUx0yvO(Iah zH?Ak1{Q$G_E6AO0vB$_#>Hxh+8pa(p>^{pFB1#frmG*Ze^C6ay``lv=*F=6OvK$ zXRu^oTZxO5xF2heg*^hyVcy*v;$VMXEp>~Ou7_Wg%*zwEvg+$1q3LRi z^xunTk6QB|`>WC)2O~C5f&7%mz)-;nwiCm93g0-76x%Q#^4cOHB@pQG)mM z_IcdH!onR0F9QPa_34}R*R3Meuv+UiYd%D%9-stKW_D&SCtyuC^c{}ovCu$)>_n4T z#|-@y%G|8o6}`^|>(UWTt)k2ptvK5Cu4I2Z_#SP~esN~OrG210RF==(J7w24Szs7- zKuXHDw<6DI#&yy&s$gdl);8jAK&jG{Naxd+nwQs<7J9X}Z`cbho7GsW$q;7icE%#+a9g87FiA{mma*H|3r*8nn@ZibZF4F zP|oa>XZo;obr>Bya;0U_VhE`T68Z!1S@!S6~&OYOb;Pw8E%Hfpfn zvTF+mNU!7~BR@Xz7wJjs`VyuT;1~9cf&e}7j_FUMF}HU`mmFj-go}@LpgXyJo0F7N z;N2m|S^A%$fg!Q{$!B|<)I#il2cDkueFwH@d)oa20Dj`JbrD~uF7--A8(LeSDS7nf zmaGmnXVm<#=g~7fx0^nKr&j_6bnlUQdAQ0`Y;hSTSuKQn*PmvC8ur}^{}|3k+4o=D zj?@j+_{+J*RtWE{O}7((5ovFI%8O%%kFn4GR%XBE%U7%cX=%DhvHMG|yj+a1{C%Vn zQk7FD^X6(W$jF$sx8n4ik_Lw|HeMKs>?m5yV(l{}kfW>X=~63F`iF(Z{B*`BE~kHW z636%L+a483*t%p8^J1UmQ;6s0aX1y9_oxQ_Qpi61Fih@dsfHX*pbT=p<9ZHvUcXy}=(uj16lt_1%bPh;&GxX36^S?*WIlu4o z;@L0M4>Gg&zGK~MU2CoD0v!6&T&b(mwN7yC6D?=65Qx46Mo&Uk+*sp$g^>s-q&ImG z$51vn73I2+`l847^ru<}TPUF>&H*3MyLWRWkgE~t5V+|6nr1^?6FO7o@(*)@)oNM9 z5W}?&vn2`veJ!B+*_kU1W)`|!`;2zC`9XEvn8(>E*MN4V(N5Opl;U|?t&7l?X4pao zu;=C$7Jtt0yN-Y?lXgAd5UatWz0*6h#$lc2QfAVR1ybd_hdb+q*-~hNiu6L=&1AA> z^BjHa18BMcG{)G32M^=x!iPCn?|lP>@SIZkSt>E?YYc1#r%K?Yjyc_7-qs-0YsPzN z>FVO`ox&M?DG7FdaCmr+su166W!Yp?8-bAm8#7Ab00-v?VQP^wHj`s33$Bn^!zR@Y0>O*BkMhA^j_!y?#wxXd||p?Cj31^A(}?`s9U zTwlvB^7xRPBv*WgePKh|ASPn-C=yAXO(9$q`XD-9g#_}6Y!;{NF{-O(^3W3^zbiN_ zA!4w2hBYteJ2i%_QDUV|h`4Bbd=1CG$z7jFFV0OtsmJL*d z-)r`aZ?vZcN7RzWVA_~$j&iOhkL#W+Q+%3)+ABFJjDl$`{N>&gfvIWDdTSK_E0?v^ zMA@BLH*`n_4xvy5RXdNs~ka!VX7Lnq^hE&^XFr2SF-j@W!Xmwy~Sz!RF;-M2|IB$ zq5V?zv`EsfYZ18AfYP{{T-3p}D2n0o7M;4GxjL54tTXy6iDIdia_P@W^$lfEFY;_3 zt2_R^N3G);q8-l+IRa3#Qa#d~TK!CXxA}c$F7nY#z(QdAr+ZblIsq9O6d;U){5iJD zT7Uie7ztZ2W;sVDO44$aEkpj~bN$k|osJ~GJFYW-aTOFfFHL2lj{X?=0#LjCLv#5y zQ=14f4T_lE$~x9N~70VGNs^m585#q(HDe(Pd(dt_$hC)X*%Wh<_C zuwIW-GwItJsjJWuzg&1$3D*WsR?98i;MMkVbEU>{iPIp1f|@QHrA{U;PESHLt?>+G zlgWraTHVlsoFc!PL_t=2+tsVz?`z);Kbb=5Tbd|zQ1kXaBLdaiiBk}h`c+yH(?a~} zFU@Eo+!XX;Z*G*GNiyEDiF*gH^wdKTj$``-uaZ)epTw9-4D0W=k|}77EcXw|LgS*R zE=j5s=HbdvJPLZ60L5Vk_QXZcq&!uOM?~g^23g@o2Fj0sd~`wXt5?YK;A9yd=|5M( zd3LFN_rsa+J{NP;DsJ#`$Isv*{>H8NoB(BmQI${e=6_dr$ala67e)EH#sfMm@>{t+ z2Zt+P)o^-tjs4Ei4x%j%Dmfpaz>!VLuJy9awbT}C(e_V25dmzBv&F+;ogqk zXVr{?{O}&JVHa-BA;=q_rQs2nYIpyD92@&6wD;s0(Wt|15p=7NolcRNZPMuUE97;#`Fs)D$*k7hybm{dz6^OP?O*)stA{Ef z&lqiX+~Mo7{SBZTnwo~z(84G_-}ATfKsIw^TREn8SEsiuQ%{o>8a^bgW%hK(B>CT3 zfL|dwHgk!U#;LPkcP;}ZK#|hgAVx>8wpleT66FCTcAjh~`##uB1+%XAWQhehIV%%D z2`#=Nc|0td#Ebsx==&tdH!^S%MJ;)EUseBxtE(z2cSH0lJ2elk4}3sX)?4Kkk-oe9 z)U-5E%ZsD**J7%K3^=3(a;s*Y@PtY-BD~~jw;Nd<<~3E&!u&k=HNoZ&qnvcD(}n(* z+vN8vD*u-0B~AAh@Lln!(efYLXaq{&S-<%jJx2{p&{byUQ%UbhRYR(iw-e>K?A-wU z|MEihX9*~zArtbE4*t$9QfM~%Wxl3zyKOicYN1k`BkB}*yxj?uD)JV}P@Q}vhijV9 zl9ILnikghQVy0XWgfXbfz&c`gAK9y3xytz=sF&#MVBjgO;*3Y>W6`aZ4i8M_trPIJt8477XV-%nrr0JN&>WHN#(wM!k}~IS(H>lxjC15 z5vqZn=&5F*#8^lSy=vH<`RKg8RVmG@Wf7MJkU`_1#WXw4s@>S$d{b|#+HQ#LwkPt- zZj(Q*KlT0V*1J<%iL8!DMzoMz!;9aXKh>jW+~A#Nhu(Gd)nDU`)Q^Sf%a}v%_2V^i zFO>Qp5N*o76|my3vJe$Dq};CF$^Iy2k@&KrDvtKETfsN6$P8YdjrJZND|+2gwGZ`- zq2IT4@+I&ZBrYAdbe^d)2-vTj`haxq^GvJbA>s2u-M~ySdY#mK``h0x>x^2d^I1BB zx^)2>j0oP)sowRhwO`G#>O65&PxF;3yG)$r{2;DLDCw>)tA6;3PM2dH*Zy6zt#*` zjWG_e4&iuMCv!`Vu5hKT4Ymax<}L*vpC-TS=8iNNbJY~9wV!0KJ#JSi`k?ZNs6%CJ z%wo2IH2E}r;LAg1tg|%a1OXGyNV3@|`cSMq!eOtff^4HR-$%vLuEtf&rEClC_GeG~ zc*wtLKneuKNfs@yKb82E94TM0S2gT=X1;7+L?Hp23Hl2W z;{}skIyQ=^_f&L~wZ6nq#k5Sr%3f=MQU*xznIvHKM*LKO#~M29F9}k(1=fW>=1Q2! zkUjO4S~&G?WQ__Yo~H@A_YaOFs>k)9A$n|rgvB_*6w^dvi*D*OT?a(C)vI28HkZMW19_D=hG0p({7>zH(L?x`|6E*ddH|narRo=w__%a;LSC0gw zk7Z}wnw{vU=N3XB9r_0BB~vDiJo^?M(CyTi9ADzO!W>sPFA86Z1`ht@l*aEY1Ez*c z=ZyDx*z!a*%R;_S-S&h;m6iRsR7v>CoJ=Bo8oUjOfm|U1vzu=~Yn>c={|NI`N3beZ zCL#~Q4K&sb7K7+DZXz26B13;(!g+6#2v2?R-rmF4aa?d4kaL3ca_&NPEt2Buv$ONS zUkerN+GBBcYNq+IOmRt9arGbTA3UIZmfX^ReY?+K@(?`)z5D9I{oK8M)@jfO()fiv z-D@y!4pSZ9jK9LFXXv(Zvd#$B+(~?~(IzPD%1;$2th31Ich>zL_2K@R$?bPH0$y+S z!NG{p{CECrRH!p5jHm=c-bkjQ0eR(5fBQ||yL)r9F_TYRo_@6r`!qVrFf$hO@EGF6 zsJ$m^&{Vx7cDcN})^q(D#%^)x-5dAt8(d7NwtTD^t$T1#!Mn*2TP!_6G?^LeXa)a1 zgMyw(pAXq|Gd^~g0-OH1`0wBB=5Nk0)5rwEqE^x&794xQ6x4b$OT)vEuL&`7sYzoA zH?taP39M~7BnJ7r@@vM@mLU0n+xmc0;o*M{X zZcxp8+T4kZAPCJIIf=)|rKk73ytQi5xVVGpDh7QGrx8?2Kk-(P+d!i&P$25g(VG~m z6FU>Vd4CZ6aKznYoMW_TA)$)2&fb2(R@cMe5tg9fr^Q1&UQc^+yGH(axR*07m*p4@ z!0S`Bxs{ly8KJO_FCnp-SG)Krk$jSgWgaZ~<2N6Cy|3qqD{L1wlT51Sgam8L#nh2d zM1YkV{fr9ff}Qy+!jiDt$S1fuO>eqN$I=C=9WNYF(BgdCQ%URQhngRv zV34<+iujtD7kecLI^A(|-3SRyCa$lxS^^-{EUX}ntAT34ujWZ-?# zVDJuYmZ2tn>v*DfE57$d1er<&iiVQ$4-`f$3}Y7MO3Mn1c%Kn zIvLSyHQY_Kz1=1r3h-o1C)UmyDb7Rq)u- zlf8-b9}X`h2a+F7It73J^gCd=PsghLqV_uY_4SiNa^SV2uq|d-c}ZaGZg? zhTI`Xba!jcr{#&D41?W3-0%7tdGqw8UZ+AT#!Gk58NWT6>M=rR?+#C|sjC|<(PWs{ zt)(>!6GkR8>dHBBwo(mU%@PN~y1fbX6Yyt7v*k|vhX-r1_&KZw=kTSyCF#P;aE;Rr z2%ncuJXT?M8H=y!V7ZZn8W)lxi{0MtNV)@Lkp9@g`s8JEtKg8G{fDcY)E?vIO1%7w z>rA(c^F|(b-({%+H3qyFYU@Q_bsUSO!!K{o+SO|3;wRz?4cAxSR^$_hl;<-r!FFC2 z!-;$6Q?>23ZiFV`*xonD?$F8$IWoS-l4fR>qd&VAySi{|G%#PgMXne@N-gHjI4wtm z9^uLOVk?S=q;usrXKs;>5wLH)MS7uH*q>n5#c_OlN1P<&n?$OByW7ExySpc=$(|FK zOv~}Yj`Pjv1Ws=fMR`)mH}+rTSDbym3A`h(QHs&HUBBW{Wrbh+D_*z^KxX0gEPGqt zJeU#p;WtY+6ANqcFu(O}k?}X>dwN)=m38@C`My_45Vc9Q(dUoK@$n0!tXaUJhpU$S zk(JkTQMF1~*M#_bB?wRUbWQMz?84;*+h?_I>hrJ5`CwgDCrb=VtpiJQF_ zU*kdTZ<#?GN*pu%<7=l*J-2MyTD8>!Wk%m^BduGtDY_GN=3+oeD>+IEzz9zdZ zb~}vQ&C7#bl?OO~Z%F|KwC%?^KPq+t^e_Upk3ePYU{CI?mHt$L!;GXAJaSRHJ<7EULQy{U3v4lBvn61Nzk{54cEQJDZ<^Z7a5A&!&+_XN{p|@vh%%`FY1C1whl*ibtxs#IeMXsrrg;>RXt)_9a4cQ_qzTIak(ehZu_Bp3k($XkYoA=r!71pQ= z`!Qm6fVH112}_epIN%&)9z@!iuL|{m3@){#UpctC=6K9J6%_J%L1BQG!0*Y<5PyUJ zUZzN$>;Vzig>k>7RQhqzs>cjb5*+x0Vsr5cmhGZK5-6MZ=j zkCbm{F(&tqV1iud4^vW8H|MW(!T*Fx3=q!et{+_6*;Kdg$FA{pxGrzlu#c3RErX{)R2)aXx$t ziX>~Py?mkdTlK07CC6(#ypH>Ks~Qt2ELn*xt#a5&!zc?aO4FUR2^!)c3=y;;Ce?H>VM( zkLV+%4%YX!q(b>y6T+&BU#tVdML+|jS?5d-^FGIGLz(wO!-?}g(LP(1T79^;`#bF* zGjLiUVqm<;7c!5%>h~m?`QVV4d}V#T6X*}@kmhzLa47RtZPMs8h(546PiR~T3W|Br zvF^|f+8z(Dk03@Zz1wJd#`FDVyuLd>8jXTi>6b(W`xZP>Hx)&CdRDLzvdf^)Cr^V} zNtBP5cYh-@(vhA%YK)w@(v2i)25@g}^PWRpLFMi%4GqNiOJQr?c6u=;2*)3c(3R3{ zA;C3I`StuRX+}DAJYIGI%i4W|oTpmMO^j)`6yLbLd8!|oVz{%@apZ1pFOpPg(HeaG zTp>$xO$+#A=jZx^Y5Z+h#f@TGGB4;TBaXIq)NAUsC4Y_;V76@Hx}SzOOy|b=8{SSsVx<}4cQg3CXYsIfGG$JI)mLPL8Q*bDAzbOX@GuERd$ufWp-D$*g zN}fCfedo?(RJ!kFaM_xe#lr3@FUcqLr3w-xhHIc53k@w3q}VUq9!D9nFmW-NnI4GD zy{$GK^kYdX|8l1dbtLmF7LP5d+NQNV5yEP5&f6&{K&I}-!%VBQS~R$OaL{;2VIF@~ zH@w!g4I4;IuM6MeI1+I}*cc8v1=Mf4E0+!Dm$EIfQ)BTQ%+UU*_g`}4IM4=;^tZ`j1MfCR*E&G z2M_aGj_-3g#S!}&{*HFG8IOwYQh2PJ`O^m0H5ZOK2V45^+3R4ecg%TvjB29FBehuO z#9r6;mWKaqr?)#;y-M}q{5WbUcZ~B7ZwXv4oiY` zZrVG02Pc!=2@zJG9Tm(yp_W;m^_k;FB@LUMfz~NB*rJ3xDo<~i<%nV$p#xmC zzSz%%DMB9|&Wb~l?RZtSNSrV5? zHB@CqwRdnZ0OGp{c2-cLTmmFm)qKi+f&ahl05V~K9)7q`o9CxG%k>m3rJ)EYucQQY zmUbfV0wevNVI(hDz@#W?6n1Jk8mc$$&(6&z2>bAZ2AR_cYRZ6?fw^$x18lgb5Acvv z1bt1ok1rCAbmVHJR^ld}VkcEthX7xHqm^pb1M*Ig_ZwE^nO9iRnOmmC_Bx0_0?XF? z?Ve8n8`1_iRW&P?F{WpI=6kw2>h*`_xKGVwEa?mZhq66A9%&14UXGRex@--< zxx9`pFR$)?Nw8;wIEkkVb$x_FOE;@Ufg;$-yY1S` zr-QS{h?5X>e7#g?ZIC&6!M%HDq1J4|s%eko2TLi`P=nX%4o+IS_{+o=?`Cy$6tRp7 zdTC4N6h(}6qe|nbIQNhWG)Zaspl4i51Qrjm^}D_9w5=x0@z$a)Eo&A9Jqev98XO!% zi04Anydu=&yyaA!Nsv9=dM^B`f@W)K(C`!4;G_yE!d+rmLhX%b^V*ASg0n@|wQrsn z=XBs4T%muz)rnBNFy$mKyHotl^E85nQppmp{^Nxz5E=n;FF~*TQ5=BsBqb#!uiG98 zpwOORjHOkI5fl`BfR3I8;3`1EO^W*2@QR9>@_mo$9$7J07Lvrf8&vV@S07F?)(X{>3+b|DT?+=iZ z^}b>!cw6I!vmO4Ti~+oZ<|vO`C%RZzb6}Bnhmt7=MscGP`3SUg&@8jsSv~3L%SO3wk|e{^BOP@L22hct70VsJ>~Q>lu{# zwYmy^pgMc~%i%2lvRP!wJ+AO`>$kCc*~Xxn*a$E-BRI}u^=`%64>-`t-9;$YDz?6L zhnHv-Ue`IG!hEhV=<_y#>_{N!GngOJP_Nc`{%~;5BmLY+L@BpR;NHX*#>Sa0Hn!Ib z?6D?5rM~L{X8Qj6s{Y-wniPx9kwSeRz}~M}qJIZ)NG{bw;&5g;*9ZX zNj3US$BS>0KuVn)Q1ydu1qmRG_((XSqc=+VA=0OqgXcR5l+{e-9WsuVIgLhda;yo# zP)?P42#|U?_j>qDjHeQy2U?xpE!M1l%C$XiI5~;!UE<8g(J9h}6P?yR+=GoOiJD9v z*Q7gqV#zjn-ptgfRcY6TrB~Ey%?-6asyv84Aqba9S^%&q@Ecc`9_S`ty|8M&xC3;k z>Vzi!6~}!QR-RU|90)8PwDxd|D+wLRsqfX z0}!7hTKm%6FKPC6N8@zoV86Pwn>j)zw_2iB8qN-QNFsQp0ZTnZ&La*m+s_E7Mg5p` z<}{F(Fm!aTcEiQSzGJ&H?0}6O^`xb309%o#i}X)rY7KGL^tcKax_<+D4q_78v$JFo@3`bcMzO9V_o0gfyk4{S8P3B4e5ra9cN`bWUaqQf5QU?Y z@P3)l^Gm%vA>QwB+a}=jL}6(-Boir``E{WDHY+E4Mj%J3G%D3*-Ef)aR`7A_=X5_v zw=@fzMW>-cS}@{}X4EXp8pQ=vE||;z-pg(t}H}w6T*%r60wEom#0FGMLJ(=2=_Ew>psXAY#u1Jy=u%bX0-weeVzD}`bKEC_j+RDO zcTCUR+)l%|Er-00*^K6|5=m>}+V!_x$4O zVEh+NnBOWrXkD+#w3QEx@Z1Ru8EoqcU#;{gK0NLY)@e$4rI8&XC;;=g#JsRVssC#r zDmVy8?N0{1UcX{c>mA!v)Doo*emlck4KpfZe{zSFuWku{Ixx^j4ezy1Eyg>UzF%!) zOF5^{upT}%031m>Gu4p@?+a+nbHgUpgXmw|Dgxx{7`BiO$Rn1ZP%{`WC8bg?VZp>q zg`Bwf>Qi^G6S61D%twS-%McWPC1x`okOX(Mq+_#MkTWCngJ>voj?aaiTh7eDG(HGJ~^_hqG;%Oi0b$1l z4_!4<%Iw{J42vkcnd7XHl&Kr$O=TP*vioKL+Z3SxeHJK+A^P@Dk2;g(y2i|)Sv5vAhR=#LNN-A0&gAX#o&kq#Svv#IMv|;OB~?M| z7u)YAlz$vaim$61?+D}-Jf8NKvp;FIY#mnZyT4%DdtUcsaYe@4kY7@P=1s25`$nqZ zZjA13P8c|XGbM$@uoKKbUDxjJ%C+NDvnj4`w1$x*0larGXKm{&gv_C_diRFrBj=t&vChNG&W7-s@GXX0de;4?+UE6MtdSJB{HJ`NrVx5BI^dKyg0Sq z+LmCLSt37JwqKL%WB#1zey=2c6Bej(EZL-I%H;P{R0}-&w-(?uf}W>)ZY+v_>CO%x zQP;3CzJrtNx3F}w=Q#8a3)Ciw2^AeJX>FM?M~ims;z=B$ta$ftp8rY`e6^+)?O=ac zB8!QQUCZa$FAXBFSdP;8pr$XDFapZ2Xl9Z)X|@67Dk^y%)SnpS)(TS3gXX zro>IPbg;AWweMZjzODR-rKYbaP9jYoze_4Deh3aU11PBfqRU#^(9bdmXXoXg%pHB3 zW67h1wIE+vZwe49N~Mn$fwchPB6`?ENFo^Ze%Ks|;U22_rZ^ZXAs{Au(8tnjzca~( zK&t?sf|HGQ3`gIb;4EJhS?tkCDOHs74?j9P@9U@U_F!tpd@><7UZe1fdWb%vjH~+$o%nah_^e)(c*+_b62{>Xzw|8 zn)qthN4TjoK`Q0n>P1e4Lh*V$vYhGFp$5HU9=y=)u+?(Jf{uGEZ7F&W^z(Y0iufH9 zqtnuX$6wwe4Qc%md1xD(U*3OW9wBvLQmQfK${N?t$dP6n0h+8Ouz~;$c*fAz9q_zA zRj`iMB0_u#(Cryt1lyn*)yBkGz%(ecWlZA|!{o{vje=qb)beAGpb+R(=fXld&>05u zro`SpN6byQW}1nK4eUZTA1$ zz2$1n^F2zf*{QFE#MSJk-8d7*6>Bg z5w`elZtdb3W*R26(38t9B!Gkz?*ylzov6cq&x+8~M1ze;zo|)0ZT96$%7+d|6TwhC za1!P;2wUJ{U*FtV0KH&9>)6r9aQ0E^m4~}K(wDR(Epwx%fdKxN(__WSJm@tvSCsSo zS~S4#csTj+mzB!>XRJTaSsE@46-s|wRvsQ6u?*LmsY@u3V7xC*XDP>rWV&5lxjXi1 zyih@JTo<(6vvuC^#ljMt4J}qH8lpsxFUo_2;BgF9YYnrD_YVYHGuJ};KA-ylb} z2-u(GeM(k*J&SCq?`YphlV~T2AI%{p(?d;91IOzH?&=}qxru2ap9AyR$BfJT=F1rv zOfY{;^!Qeh_i$SU9O`2;5Q>Ted-T99r#1U(>DZSwX&G92v0y`1F9G<45S`9=M{7#_Q}hJ(Yr-k_H48Ea*FD)E1K z;(*2p(L|RyyCdQtS3#$_+~IpFC=w~(eDg}jT4%6eF3H8}j=k`@AP9?pf7`6QpC>Vp zjJu&T$jp2Zq5Sw_t?5Ov)mc|D@y0@--C*fo`RQ}^`)lBva+8#nhU2(7;Z65I+N5A*M2?-rZKq$~Um4HE@iq?j)w!Z@IBE zry{qs_W0L#NoEFg+pNHLMMsieWtkX9n^b-2a?0ivSZ_qjDUS}e%&4`?_@e&~bUk)2i_YE;xEI7i~(+jFyslu>*{Kqp5gur3|I# zV72$B3HqOiz%g|^f+$3)Qu?N;;SP@|n`hP6Ty_zVRPgZ+ok-%_w~XbxTC?X@*9Y6M z+M~33dsm0S<^vJjj*}9Xl~qq!4y(?l^1R+$a&3|@$0wM zG9W)tY+oWJlU z9J89}_V9tmk@CSwaY;S_6H#Bi``xC?)z7ND@g?-`A!(Od)oK%=NpD>11SFm`xq75j zGB;(y&1NpRRsWmT00TMh)%oQYceaD1rmhHa-J?qXOk~sYshp#tn@d#MX4dQ$54)3- z=Mmo_DOBs$oFEo0&AS_F|!y?R)uVQI-y*TPO*1}lIWfd*_KBHMk%(o6+df@G!RC#xLL+O7me z`)dgR01T3rzkXtgP*L38#a~`d1f@`}b~7#m8dS6q%ZT%>&rKxClRiMN86HrrtEu$O zX^K6f(0ag!BMl_ru;Q3kKpy%F_yw^2SpB9$kIBy83_9*G?OFE(ru#u7U5KR>WzA+f znE+UpZPod(5VtcpXr*1EYu%%%QQ%BRbG*svv*64-#kV?<%cgD93>;9vpJ%6h^&eK1 zL{7m2854*>X}p!)HW1ikWsBgBgk`=>XZbPfc$e%0*U@fzqGuW!-9%KBH_G$bF&o#=f7dM6E=QFr1`0xO7iz>G)!Lb6(Xh1SqzBiL@F_QN# zIL+kt{|_<=D_bktxoTLAu1*v-eZiE^-P+L+ptd_RWcEhy)OsP6V~|P_b4BTphBh+c zrK8dlF3Y*rPlSpbHvKVoQ~Tl^tW9M1lG)5x-}FC4JIaNa^nZDKj(R?l>842A@6Mkq z4}0aqhwbsvK}<3_Elr`&vyza_02eqRatg^!V(>`dTi7f5yCOUU%fsj}Ki|XV!Lt>0 zk)g78R)UQ}cF$pjO=KO?I>#Q|Meh_rO6$j)jE{{gQuJo53HC>YpOWop?*5!LQcg$9 z9WOu2Z$(NV(Q!CJ>l%RdmhJ8NF1g2B70^WfjWby_Y^G8GB9ryFq0tj7m_FWP=l1g4 z-5g5-*ti>Dsjx{%tmAzmqm?e6 zgV_LT^X9PrpOMGsgp2B9ke9q=_4Qpe}L3IRg?fpfZ-`7bKg=%PVJI8`oX&_ECb8S9r z*4zX&I;w~o9k*tCodz*Bf{Kx>pjC)v<>SgTWJ+HzvXUnR^LP;Uj&cnuO3KsBk02HV zmlRn9xyT^E#sNR-BTY@s(+K4aGEla13R{3&p)DBBqD9l|v9$vySEh1BGU8zPE8eGo zfZ=98Kd&zAS{if|Dq6`i!I^VaYt)yPqSQU5eovh2@e()jkyw*bYBNU|K8+GP=VyjU zJ&Yr$j1?1+Usc-C6(tsyh~-vxaPGNTQU^dx;~zW*SdtzwAPWp=64KtqX|g;5YF!8> zOdyoEzaAP&$YDi!YjSsoR5nW`gpYVY`)IypZXk7YBp1*WTY?m+>k8@Y1CZ^VXo!pT zjT-&*0N^$ImtCcPA=6iP;+4NUTrH$++BT^Ipm46Yn^H+mMQkzdq0c9~2o0&C5;etxWY7>p8FNnd^vp>hjUS@wvN! z2qR-yZn|$0NE)!gH}MH?k8xCfs$D$I!i}0Gneqy%#%~B7vo^RyV|k2=YYd*WBO(*j zcCIBaD>qp**(kG!KX!8GB9zW8)f#wz2Z&dK_;GO^Um@Gn2zX)%R&b8`1(3E@De7Y2 zB>0Iq@;geJJG>JIVAUXW&bdVL?ARSe9;fp^Hj!kcDHki_CSUOao$-($k zHaPv2Ej!`9qg&n5EillA088pm6G~RkW%+hC)KsxEgyP@&27scg>7Y2Fx$q*kSXd*V zxR?b*nVEnZ>poeRWu+rM6B_UX!X$2HLsaUmXvg-Fg2BNt-LaBIOG3khNU%m_frx6` z@rg&_(if7(vGNQManUBm!v0;tF*5Upd{ z70-(q_L`8@Hwz1s4T6{)c#dqL9lwBZV`a(#Mo@6D!Fqop5FZICd4aciV=gkljbHmZ zVn?7K3iJ6@JMtDt{vgu0m?9$0#6%1L(Hgk=eqisEr8?&rkfH)MAb@o{+jXT(I+AYg zUDR`wSlM`W)iVDMj+-cK7sTH*9|Z*jhjK7}qU1 ztl*5?IoALAchB9)L-=Ljs>T3)^?-I2!Osd90L$G{p7a5BV^nhQFQCr095mni-khjM z>w^{wdGa4#-y`s_-bp0!sjOELa3V@on_4MSu>5QmPPyiqdvjXj1?_<`` zzpwY|kJ}0E+c6Iaoi0FCl$5OO%Iy^_*I}je6o@X=)YJ@pe~qS9VQv6>PI-v+G(Wa2 z_X*MujX`4n*1->d=1E+J;4jW7o;8UE^5g)hLCh{t*7EA2KP^z>#qoNGib}P1*D*bu zg_33=Z^QUEd)a}#3pMA)db}&Zcfmz(^RH#82g3xo#t>9;;I+0G&EBJGI{F&OSBj$i#c$i0g{0cVo+(hT5Iqw z|6Cn`amIf}IZBkrxcf0!cw9~m1gweZqs6*`K_kH+vR>R>vIgc(2}yJ6>0p{7#n1#E zmby(o0$B$>(B!}Go}87Ni-5O5@*qz&|0$pi8>}phk5|X0jlL#$Zpl`&;ynMHj`c_r$I@-#FSO?s2FiR$^p z?cWD>H2Uv;;QM^9c6nZ?7li?;r^Xe4(%W^ygEx#7UeiH6XZz-YfoXRVN&__%>psK4 z-frmy-C(?YAIX5{fAS0j1>ou~{PxeLLel3>W+Mu(eX0s*F@UaWNStPKI3GX3fumdR ziMwy#EAYGs^Vi&SE0K8zH5%IuAGJX+<>9_-YtL|uncm-}Q0st_4=kbE1F&jI=XD!D zx5xAey88KeSNQ!5k}7prZhy#aJKGI%H-P(xptZFY@*bqG|6Zl4I{_+c1yhP&ngx*S z$E(~@w%Mlz-v4>dNp)b;02~h@XY)Lle?LL5T0vd^=QV8l4NHOW^8LHW$WAs0tz{5d zYkkW5y9dFaA%8>nZeCs;%}Hv38AB*F2r1hTc`#t2zfH(c}QA`sb1w?&;@7 zUR>2LbNV{_$I!j*9O%H7qN8DoMj}^7EdAQu`}glVK!na7laUn}{&hsaXK`ysj~S74 zQbX-D>&|I$fIU`tA4!~R^fkCKL8ilTno3=KRYH>yEG3~(5dQ$!wzkS@vcv#8=gGhC zRXcw1iSxaoP_P1GpVG5amwqelqM!72ft-mqT=Et05S0)W5Qpn z1@1pN61*Xf)Ej?L@!yl@7?xnsMn{1Z0?5_1_s#VHM@U?T1jzi>xxo}b!!an~sc<9t z_x4fSA+mBS-l9Z<3bX)ZoDxtds)))!WNXamaK^-LKvl^OF9Gllfo(+8Wr3s~LUtF} zL&2Hxe09Y?iLgIkMn7H!%Dhp3sL~@v59>oYYn5Adz+|Y5Ot%tXptzEUuZb$v{YeB7 z16d*y5V0euV$txKCj}z@&I4GY7@-l0ODzZPByti&6X;-B2!e!5{@6SP@THwEJZ7X_ z$%C^EOL!UuMoYj;L>W>rFoXhakB=We?vNsU??2aSpk|?(@a598;RE`LAIfRtCt^FK z^omIa;0$|Rp_qeMh}&lRQ)nm} zAtA6^z$f@;Klkl~h8aOK2tbwRoR(_qaK?FjW+h@(PYx8dv0azl;DVWBW@2!XeC-EH zDY5CDfqx6kt3E5i|Gr(^#nFa`@4V}i&@q{6HPs)Qd@b`)*547EdvXj`uTZ}Gk`sw( zJ1$qXak()*IH+6I@A?2040^}#a1_urp83B44|)eI%lNL~z@Qi#Qia8v59>7g^r!hG zI|vgkQ!PLoxV*jjOL0b_%)$=W%#dZcNt(dKdv1pmq{tNM6jBiS(?CvOQXu5tDGYiC zyp^^1-Q1v%`cFN^S;|K;qc12p`ig&}Bi8p*u(AyY$h)Klx_<#HUv2qJe(yo&L|@1J zuy_`3@8DpkHTTNSjx{)C|GhdCbPS1Ru*97f-35`-Y!!~paTG4t(_9tQ>|cPb04xn( zUq6`29exu34#&0P+_Kv}6-KBSp&rldJ5mQu!rc@laa*(auhAN3eOM_%PD!)_TEDY2 z84yGK&sACvP;&as#CF;&M|S?;qlXqqya{2Pd4zK1(`)w5&lA%Z@(~_ThXSzFGguN% z=k52|%LfpXn;kqRw^cjGTZ6j1p^K84ncs->uVD$o6&Q@5wN%CY<}je&AS848BK+?Y z$J$ic)6yoELs_uXV-z~qtjqeJ0xv%5Nv~z)mo$>k`DV{hjC#DMRsUVl?PMgRVKsaL zh;%J@ca`TW!JFKg*lK^z_S)TDpluI=vZVfgInX7lU~c{cq;mhA5-GNS4FBw@p^*`v z%N8zRngtwgQ~$FLaB&b%2BNEAkoDp9x#YxW*7*jqi~qBHDQUv|8Ff`%Qyybu zztY!#hC$5TuFpD&K%V}&VxIlkX_S;SYAPzY%*@Q^S76Qk-wKI&`Om#d4J)hs;K*pqNLX|7m?7*@%?@kdhgn?ahfxNc_$aLj^t0#xVE) zKBm;mH~Hr*5^6YK4n2){req0NLch{0=X{BYX|J-Gd4Pr0)zQ&`D3|^B;%o!PP9_&9 zTn>MGD^}+WGX*QSx=;)iv+VLn70G#H=tFLr=cCMFbEwLHH=^_#{G66ysM4? zIkVX+&`pYDK2(D(0>9=+OiYwZ;eGr1Z)As9tMuG|eS#?MpH36ZU=k1drqhvBIRAQ& z;1{9{#r|0o*&s(O_F3rBx9|U*S3$T8e}^8Zpa1VqCI6X_s5islHU7Cmh<*4$WuT$i z)6f7s7)$2JJ2xDq9L6O zKL{4xV-L=cJi1<~WU2LztcMTQdgH6zRFD8BPDMtn5HR!xb58`+qSEM^YY@1N{_NR; zsIN_%f?x|#o8x)1g=fJ2$AV;TchDdqk&sJ^zfwW`Ad2D7G?ixZ==u4RH^b9h{0edv z=IZ#07d+Gj#&1v2$P3ShA~tR3i9_{Pa>;$KC$92UbGeII-ie0BF+HjvIs4U7JRg$4 zH}8FeG1%3~?t4fbh~cpsmzK}@T)l}TGTc4oyryQGOvHKrS-yyNz?dZys7(3qQa6`& z1&4L|j&(kg5yJ?A1CFOvNh&%*UW-DGRkth>7#Nq@~x zgkq>K9*m1sXHA{)Sx&g$YD1Oh7=DG-RNymmzZdaXCfCYF-BOAWK4I%_s^g8>*u;^v zT#0P^>)A$L=f!6;)i#+kLaBL=aplH_WY9N7zKrB5tXwKNFtYkk6;8Q8q2wIp=^?G&VN#o)uPIKFweY zEPDSa$F#f+zI*qVlb4IQe$whM%6HpRGCx26)NRQBjF`) z-z<%k4Cm;E!$eVWFP{zzGjl@xE}<-V%vy7KrC2v!%4bsy46nVfuTOU8qvfw$SA#Q2 zD7H(rfg_aYc=t##1>D5-x(7FEZ=Cx?-~HI!T(q%WR?shcw;R;<4|rid5gj2v+1(T^<>UYCnfnGg zK&rSVIyxGhkLw=3@fieM3RE=u(?&&YE_DXnbEt` znlCfe=#Id{@KcVObqXilAhNHAPPjj;W>WF^scpMv`-FPsiIXQR=bvpHI(!%$iECVk zo|fWZt6#KibdaveKez0KD;q>lUN(LepK@agYw9@r>?e8-9<=(tOHVt2v;X7c;~BC8 z^>uZxUcI`$97n==5ItJr06b#hmOXTRSCM&ddE?#Y4}HcvRbP;jN(kE8w^?^zsa9J36jR!NB=w9-dFB2N*n)Az%2`*Zo;R=io{-r_rux zsJiBt-af)>*YTL{i%O6 zK4(6j{pRw^zeDxg9%jBw5WH!e=d2*=Ut0?AD2*Oubc-0gf)dE9bNw16JS z@&2Bf7rQQJ;hxPuJ+JZ9bICW*XlSh2%EMu;JV!))T!>6i-tX(@uQu__O!rkR8 zN+R!e+hDv2x`sh42KX{?1PfOCLtU63XoR-=;6$QdJwu%UHiD$IX)| z?l7SLmOj4lKuAzf`=L!mm@@jvp7h#uLTWZXLA_m&It*M^neh*u_iCcvCK~wsCW>yBw;jF>zG}u2ELTay1n>(WN#I zirlv6_}G#mos9zsn=ddN1=sQUowWCTSZCiPxIj4oP9Fj zJC|W?;K|Z?h60S#tmiqEUoPe`%AcUFvo+8!d`a@2_O34lv9ew$1gXjBI~Ece%5?$3 z%6ihnJKO5wg}X-WV1gBy{Z}chWr9{W>@oCLYC65azf|J!urJRjY;tyCC~@0#jiZ30BS>%D(acWa*^eHf7{?l@$xi=^t~I%Zhugcy}~ zhx3i8Z7c}8ja)X)m9O`qIyN^eULp~9JNQ)c#&3ii#6QslqTuY2_J%*5Y)j6=n&SIqxb~Z{1gjJ=?|NxRP=@%pBcclHJDP9&UfH0u}tvDO7!diqlTcP+^ z?nmY7_leS~Vdt-TK>KaBA=35ee%;*&8StEJ741m$nNEe<5cPyyyv*U|7LpR{+6=nY zm!WokWF&1Kfr!q^!y_pZB0dBkm3qRT{ygN0D;=>tn^UjNo5_7?mMRf?eF7qG={;{v zFhnn%i5dG>dBuvZr>yn1-6kxX(0{2+e>w18HC|p9arjBx>%^cVvI}V}R#rx&M=hD8 znJ)BeS<@l7*LmJMDUwhQh)IF;)Vm$V@oC-+V>u%E{gn8vz(8u<;+U9`gpf-N&oRzy z!^My#;+)1gQ%CL5F!xeI{hi9n%Ge(1_yj=%YyVCiI*PF8=s9! zoDu))2Aw{Wvwh-LbB$KUxsXNn#;o~a^w@|mZcb2URj)^})OJ8Wg2nqL*!WhBIKxWS z$Twd&S97dr=Mx(bdDFbT zS)k+QlWQeHDzTtob(Qb?_Ih0MW0i}MC1Wm%I9AWJ&*3J58 zBic-9H#iYB^2v+ZLtUqjBo3hq3bZ^tJZ5-BuY;J;E^s(GP30)e^Fu2qjygS^V=!{> z4{%dxr&Ch0*!W^&soN?l^a=)p>0ci{5}^D%F+7VvFj=-DX^(%6DDRbgvemUptRbZB!ft!te_o$oG2Ka`mDv{$eNlO z7G7zSN3%_*1y*WV6)xG_e~h8grhD!H?Y?CF5<)^k;w0+{z21T^p&>k3tFRuSt(9q` z{DC)PPUH&bG1ajfd>@{SKaQ*3ZdBlh`fgE!g#P}!d*M?SslgQ>>WRt8TtuooyDv^8 zWyU2}5C)fYoeTquvYV(vS9PtTfBiGMDTB{YMX3530-#3t_Wpc6F5Z)N*X8gAGO(-X1KEXyg}bJ8``D*M%*yT#=(^>iom3o#2)7W2583S` zd5D>ngV!c{J=Iyp5*vrtVRO0W^Hq#6cYLVrrsaq!-25z``D2A@jN-6z%Aqpr@{ zg&;P`IPYikM{hOpZc7UbT1!#4*l4xYOAycPdwE`PmS4M{S3vs<9z{=9i<;wFeW742 zPzYH5fWRQzuE20NSYle7b(DI66j>&upL!nYO*{ z&BlI4=rSCag0zTtDI$$LNcj(7AleA$g%Jp1)df+g{!*%aB+4oFhh*<)FUZJZBYPl z%~bCh4~hP}E#&OEoRV&06&>p+bm`Kkn5opRusZi*ORu$g3;?vk6(dZbOL=M(Z2$I` zqjnwX0ey=P`2HH7pT`EAFf&-mzGBy(5h8uqt!LfZVtlBov`?yf2g&)*KliSg$7h0c zmN^N<#q&g(yu)DC2C9b`6%-Wo?8p%+j@Mqd^?9$|$QZYjXN;q5$Bh%_UZ%&otg~aY zvQoI#V&-Y&u1t4XA|aQQQ&Dj*s=G+9#cm9TW$74e%q46`Sqbn90f93pCo4L>_y&~_ zqczt*i4C${yf!2HLpW6QNhP!re>u(MXI=@?ZLa!crz%H;7a=ZDOz)0gVytv&_Ln&1 z5tTOdPzs0?H^<$3shKF>m90k-cHH*iH6j^Vb6p<{IBbAl&kQ(bL8SeV&IV(VvtVDs zFIk#bS&UummkBe%_l@n>`k_95ueE6j8kpt&AXfQ5s`jLnpTA+*D7RM-llG7MRs6ni z+iicNgO7d8afU-i8_2spnJ`YN@4~ZsgD$aJgfvYFA-3&_+rVEvyJzAP3)lABY8mV? z{m0C~+#KjvJBn*8%#>j1gWoIPkpd8)5^%%AO-{Wshh8|2_wF}Kt9A|oFh)E$lnWDd z^pNm_P4ywRPljfWJe&*f{DI&$v~A^6WEK;a*RC!f5=6ILmUfZc6S4nZOvkLW@D)=2 zH{O8il)6F&ifoH{X2~jM4ROc6e$MMV2Ho74N`;j)#Inqe;6RtF$X&+V-j2EeiED5v zn2ot6Dp;G{(g0nVLAKLMEH-5H+SVe|o}wgRFXyKRZw=4quiVGUZ#Ht6-wgw9ZYTxo z6zQ@WrF^5atM3@Z*g^=ppK+NyzLc;!-JzC(ov$K>M_klQ%6P#c(k;yagA+Cjr^>qP z;#>EDp0@fPX+1>Q3b!l^-R7_sqG_vqt zNhdvBr%!o!c(t?)7y~siZ&u-vQK`kP`m>svn#9e9gP_X5o4!O)uv32$6DTWxH*oUE z>guk%rM6^|aWWRUY)jEt-dza=FT3~n zF8~$I@;*k5FL|Q*iEB%#XRiIJ53c|CX)x`;dqvdx+lk4wSS&WA>0xJ5ouTK$g+JtE z;GsY}1P3UiqYkSYt}KZUuVtBRT&uNc}4JCO;DGPg4c~A z%Ub0;MxVR5x{~plsYW)fU;dK1LgJQmIvWb?}_UmJs)3dA!UxC*MwJxD+4}ZrWD-_z& zoM?P%-uEd#{fPCWHnN!D4?BrFrQ%=U4JGD9zI{#z*)`Gup6eLipPfhjp}@_C?`dS> z(k}dJx29eQ$#Zb92@4CW?Ht%)pK|x`XtEI%@-g?s&{!Wn{Mgh4MeZP@G8)3qQ#&zg zVky)vY%+-LP_ngbN-3$4N@ivj`_nN>+`ccP5<;Q~FPrPw+UC?R8VCbzzovRqRofCh zF(P;!n2PwJTV2=e&6J`D1R_{o@cX+vsw37JS;p}Xh9<75nH6P^nvNOl+=}2erkb>p z3svPQTBlvHr5cmHF8P$k>ir$`nAq{pQKomwpzABGz0S=|Z9NG|TzAg$f+XkX1uY(C zkUjn*1h@d5bAsW}Bgm|4Qc_~5fiw`mSu;L|t!0GVc}pz@@TjC&d*bGj@a8y8P@LwA zd8-s8W>t54uKlAH)VUD6ozGh-5gnQtLxZS{x1&0{)|!A4$<6Cf9*?-j!Le7d3Zn=D zb!N|+VO1?jR2>^&ch(hvb$oa@w?3dEByFA%QBM2xfntVRln%%8=jjX51N~>obE!?ohZzDeKIxeup~a_9v9U?1&hr2-`gJXmx@z{>(^szQ z*yeb_X{JMWwY1U%+ZD2amM2aM=aN$`FR^(OfFoj`Phab9?$Xs{@lplKpzZRG!O|+d z%kSxX)W!!Kg zJ(I$1knB>vTNB-=+$SmzzyNY{TEL;dtTcrWFV&hVqbXDF`&Ltzq;Q-^0!Q`8&-%|` zCavM>8b(;$F`ZcZgPVUweM2Q$md5gfRZqd#OJEdM*A@4^KEzj zJ;T#}dH3huTK-ui`SkAMF3>k)rWaeEuuKl@inUJ{6IdRT<`*<0|xi4);ppS35@ zf0qBdx#vgI|Jwlnzdlrl4>~o=edq_#z$ay3_rNr}wz*y%bZYN!=q-(#*cmH_va6_gYJ z7#IKm#_bF63ym?YBqwM3T2oy?>6QGy8hQY?1?vd_;OOM;s;T&#QQyFj5qIfdXZ+P> zX5r@Y*XQ4q+v}cB{&jT#V2tN)`uwgM&(g}x;Xrra%Uc}ug~{0G|nKhPE~u7CB9y6q$B=-~0!SbvqjW=vq^ zq@#8FdH42B3vdHy0u%wy|N8y6-*1a^9snSA1^{5j{d=8x8UTR!2mnyc{(BwECjfx> z0{{RY`uDnj=ZTA%tJ%L@4(s-K$J!bII4%MJ2n_%LvS9$=9`N7VZV!JmH^$p0+S_?K z-@a@B4gf0vBR~n@1h4?`-cka9Cjfqc$geqo902pqU*)eO=54{k!TPJ*#lgYG!Ml6! z9^PF%yn6)11o!ZX@bT~nNeGFE@82i6e~*BajPyR)Eq(v5i(ve9CMMS1+Y|5OB3&6&~!o7=m2Ltc6+4?>J z0}~5?iHVIvOo)StjY;@dcPwli5?suPzHL%Id-alXx&;-ZlR*sMh zifEa;1tqi}G7CL-?ZBxTUH*DR!6Gb|h|c>p3n0L}ojnOA2|xz$vm}oR5EgcaiScgO zJEs37vKVX=Ay$Na>EnwwvD3VS>M94$YEa=QWxoV5MUi6oEf@~tVcL{nmL|b=-#g2< z!4J4{P#9>!q$9umksaApnEmI7$|KS`!bRy>hm zSEDg@i|9djg802skCkbXfi!fDs=Rxz2G{7q3B9tHP!x3}W7fw7>}BzO8C&E`PGX0; znTA;!NP@?Y0^cNzo5pF1w$?PpJtgct9r=dui8|Xt*D^r(k_z2cuS`2QjW{RFpw@PA ze#s_IE~<}W=xsxVj5Eu}T2g%*Q@!1FqG+?i>ql*CUfsMwM+NGR&(m6o;2qEdIbi;< z#<9o?@7>J3dukVRRQ2yp8Y`)1)=wjUDALw(C^?ig>wrV=hAhe(I!*|?T0V#lx%Wln zjo-ISXcAG=hm*h;U|ULeeb2eB!ZU}U3!4T_Jz!}bS-YO9gjI-s&$OS(sNs727OpZr z1-b(NBr;hi;~rXpC(eldy|56xu}jKm7~rllL(`uzRiuH=`$mx@e{<65U(xE08|k&% zkP|ddx{Q>w@2b$DNV7>EEcC;tUus(xMoOC-Vz5Fdf0*1G4$kfrwYa8=OlpEimS5&} z#&BBBW2{^d+Va1;pv|Ym#W(smUL{v|K1z`AZcl|KID}-(fb(BWWm`yQev9IUBio@=wZ#us1?E}VoKg31Ve{)Ft18% z>meQ>oM7YdjNA2BK%{0(7TFw|#?t-1=KE5oj zJohxOZP+I)`SwiEj}d~!kOd8oI@M6pNT-H_cDeML>JVtu~YaO{jCB^I&+GFGDuYlhkTRQl-<@0RQ;?z#r!{5M}N{Dz5 zGuKRxaLJCC6V9_@>lXI3y!OX_nsnsUtlwld8UmxUwZw}EZb~kJ?Gjdv44&fYV-0=< zRt^H`#h9k-kJ;e5$dTgqlhgy{WL&m4?p2)Y%$+fGM}UbaVySKcwWX!L3Rm7Qx}HSR zchq_Z*QAK$DzI~(FmM`GMbBHdf*bNfJ$Ic$Cn_Qacj#s1g>_z-D~D8Z7Dn{?afY62 z)Rz01gZ(lwJ?~=Uw2$@F(p~m6_A5bFO0%G+%9FfSpXDJwE4(I$@;~m62HEV>2rl zve?eDIHCcvp|;66KkQ=`;S@M9D%HmSNun%_o|DvpGRY9Pcd_pxG}C&SHdDP%z+asV z0|kdp zFCXP)z7)^r&e|vNda{k8&o9oHx_n;^qt z{n3qHGWsPGWDTj&@H0R{Q;8V#>rM!pw4ZEelwr}s0T>8G#7Po35Vjkj&Km(>o(PHnL1 zWF58!?(7fE6J(>&yHzA$_qkyy38uG~%Baf<2(}`nWD?(v`?6JOw``IDm0eM-r5<~3 z?q?`yW!<5cR0UH)pcga!LRxAUGyhAC`{%Iwv9Q_-g3IFVg}W&yLqh}iSgBX^UFIwSIi zwY+P`EkcbQEw}=iX{@zEOP~acOA6)`ZCI9nWY8hLxF!&1F6yg10mkc;&ucG-D0-*R zch|*6qIkcI!r{u)TwRi91Vucc4};OMyaYUMFTvDf?-yTJa!Z1iCba^nTKKz~cn^Hs zGDn;~wQ7#*kS>z%qm^F6(I?-d zwY6Kn-%)(p!2DQwqeC7@5gYXS&6&OvGmHA=+?hprPa#a`x)FG%S12Q(7$eYGF!r z5bMs}?WgS3*p&}FWfLswk_-KQ>I5PTph@}ZzH=Y7IF`bEQOrG{fC6*00g^liXzbnY zhW}XcSucTFY0W%S`3Fhwnie+k#+!`UNHEqcZ@`ibz2JN`8@kEJsC0Rth_-#7Hv$E; zZPk2rSv<$GJZdD)^>}}fApAo(4NP8WagG}qEBsgr5wiH)L^@iFiLt^~fC^y6lt@t9)j;mRv0`joyxD>a$5dZPPn z;3O5;r_csyEE4qBJ?yTM^y8@>vlJ`1M)}t2-r4ru3DDxSTL;9cutY)X?%dtZ zhAC6&VRaZpn#bjU0j(f{cD%wS@Te5-q3E;&G z43hg;|L2fKiQkJMeqD3D0((4#cvdkeFT|v7*u`k3WKG?BH0HwB0K>&xkE+gwS!l|f zl`-VZxd`?N_VM&gyOt!JDV;5dw!7xk?Ip{7om}oJO=2yRLtuoYQ12yO%m&qhl3gTi ze$^P>n(!-2-J=IQ#1ZCddG5Yr!L?AnjU3$4Pv6)jYwJ=Xo-c=?pn4P|-zHtVBo?;Q zs12q3Lo+6^f+riKe*wNHnW*TM2dSWFud*Q7N)qa>dl&kR`M$d~p2NNDxTjfC)WGA& z-u{oCYWT+9_~Fy2So5ifdlC2IoxKlu$pjOWqN;FH`V{AyB-)4pcV^V?x0tXPR9_v~ zFQwdD{0^yYQY-%I=<(1!_bljwo6y6-$*R8+^pO8#hAKL@IrUMIWt`G%;&DX%l!<@N zP4vDVXXucYuraP3^Snx?9EU=$l+rLhoZ}`@%uRK5{uh9C`oQ2(mnm7Ea7ZDsyU=#_ zVPM+yx!=aq9|zlS`E+O?SClRYfvB{Aj5tkpjCdWF6Y+baCCDHPx^JzEIA4D8WHD>mWDNIjH-Ce^KX~FHvc; zeZN&NzWrO_N{0i%s=&3M8cvl{6N;*jeI3SnY32#ENxNvP6kFv?AFVOk(htYNIX4o_ z{1v)+{YiVi&H0B_(zy>L$IIY%z?A1YF^@mY2Yz5hll+Da(rveBdX!AQD) z*WjaxM%FqhI+~}O-8FIvnpbt*Sk5GZ1Co1|0x%qdy}8?JOy|Ju?)vk!53i5tkyiB7 zKDTk*+ck;g!-n*^)QPcijhPHh1N{rBebUydvUd)|qQ&5;qT$xEorr^9fO_%Oo%ltV z0imZ|r>8cGa~+yiX=E!$eMN7wG;G_8>GX7l%_KRQqqr~wQ3HX2Dp(t64iaM_F=p)q zLEoLx@)wUQ8#W#rUcB1Lu#DiHs5zrO?(HGwShr)H<0nN6ja;m9eO05ldVaVQk*Tth zE$+}Ku(K`)RB5kRN28i#DaKnBzc_)xr z1vLH5l+;jKi~r}KEdW@uDN6_^#br*6_$yEmFBY@-OFG3eUx5cL1mPxL#owJa=+?U- zK{w`%K`wJC za9W()cHukd?aRtuX=-Dh%g{s|E;L~2OoH_)n! zTTD8zdI`Z0_w!Ns#!toi7PgMQ(x|uLdXRa?hdQHhMd|t!#agsDOJH!P@7r`--R!Zh zo6gfg+10fVmj3?Tl6gB$DLu!p%;&!w)3FGCWj7`J(3VDJE z)t>&*M%s?lWI|53yINi^|IFjCSuRoNz|7rFXo~;fI;p#Wn%$C9=Vg8C~l zGj08%h#z4FrPDZW5wAG*Rq1k9A-<({SP71U7|{4b=K(AS?5ZZ%-lYtcf02Jm z@gmv2ceaKiR&Mf~vwP}dfI3Bvg z-WBn*r;Y9#)gzM7kl^wAKON837j>!xOb{bGY6<~0B`H)1dmeXzyU%{y-HU4hr?!FKnH%h*G>g-GKhD_=lvt(FKR`?*;u z%elr@u=U;W;SnC(?$VQEx#e@bH4`t!60bJWN}XQ zEb56Q0625XY!hJYe{A3G{PQ(LhT~~}?NyqHjFU*8{mY^cuceu0G7w)nEqTWewplRq zeZBW9J1dw1$-(%d6=>lymVra=kQ0)iBZE+rfjSg3X4?_q$bwaa+q> z#KjXvw1e10{-TlRrBigz3kUKar@D6==@+-J0N47F{>>Pe0eaft$ahpj1Wi$bgCvJ4 z_sYG~heXZfDuf@;OV}APUX)A~x$LEgAcY&o);>FJTx@hYOTH%Xt zJQv|xJjud)ED!D3KRXM##`ocwMV+1{s9txinasvp6iB8V(*|=NdoE8ZCaZE!fkMRP zg|$bsT5PC7K7*xM+i)5BbjyZrpqEo&U`Sbrr#iF$B2%T-d`WRy(r8OXgUW*-fvs*H z{F%N%FXycfKB2{@?t9pTNC@>3V7(uBH`-}as4zj3gW%Ub#?$W#puWpUt{fzO!o_UZuuxnxl-{xX0h*7k-(Q!`#1A z4K^4Im<`hz^VC)okox|aZgI=f4RUq!)7JLX(J_$ju!3^A6@O2X8Sa&l95UuCg2YdY zF<^TabT=hA+5%21JN9fU_f@I9kK{f#I27|He~Pr_YE+UN7kA!{|DlxpN-xnrwlcfYB)KL;f>YZ2?b|-5hre zv41?E7c28qT@6m1*rqLX?HAy9bxF}_$EDXh?8%0nU!KeR1WEl@a|_>z)W*8UW}T0r z@hLvMhZ|4OgLp{l^u~dfjgAKEQG-o()}W_Ey#@2o1ko28ahPEk70%Dd8#)X8PAJ3! z=OWNI96!eU=|wyIUFV_=`NTkr2C|?cM`6Kv^V9^U*jLW_%u+aT)MbJWo9U(oJdH;+jpqwOOM4w^aPJoj- z2u`Cd6qQZ#Y*WS3Supi4J*($IiZSedwshFvs}kbR+W=--1C_TJ({yo$v$c%QZ!`Hb zzTApj`JhNfJDGiWv?1PYxYJVjbi!%vfI>`*9uwn7pYOLhoGJ~V0BrY*N|h;QI%$ zDutd)Q?7#Pw_Mp5LT-YFEWF#xZ{ua|c^gNqrqW5vrLB4gs;aWGTuxl?&qg|OF|bKw zsgg09g@6CT7_cAcIsl0*C%J?NACGG^C|2Q0{fIp8 z&KlsC!1a%ou^0BNJ7n(d2=y0TSzze6frVUxpvj0b2)UL^F0Am2PB%eJjJ_T9;Am6%UIP(Z?t|LtNisNu9`ii_vtbV@A0mxgW@aFbU43vhLxA<`4Bt4egh8XnHM7`Sp zJY!)9B)#HxNeRB3RE(Knhv|j8QS{75?D#=FjKA*EPYI)H8k}K%2iM(AIFP%EK|&lz;hCNIVx zsQPE+25^Is9S<^RCBO4-;;}@RIy&!LA3om+6~t{m!j14!air;hazlG&1iAOt*Znvi zJ@MoZJtcZ9Dq(`Zrs&A3IBX%Ja-T+vbP^9ztHlW_hhVWbK+0Y)gy_SxagNUFM- zY|cyZc=2)2$0>h3p6p)fzRA60YtJML7ReZ7XowsTA(7Wmw?-K6KUO>u-sibr^A1V& zZS2d1=-!l>Rm-b7dYjG#2C(6lXKR<|a+B1@Qfi-NN1=g9goH7PTkH=a^KUB^i8(}) zxjeeF+zVXEbYHtQ7WOF1ctzT7RhFL3(m;Odnq9~`ES zfS@Mxi0-dQvQ{pFN9PtOodz6*4aD9UlDY-s!KhtTBE@!@jf<#77LLMXdpQTrp`(?>_C zeQZEk{jFM7VSX}9TlY-o!;fu>ier3S7nsK@V~E@Z<~M^`7>*PA6dDze@4w~TY(ylQ z;&XYE0av%I;Mx{-0gim`?w<9AvmrzyRpMwRAe&H(o2Sts+S9XU=q|#>uZnw+FGq0M zm@n~dqA2lk%)Q<&qrS73z2MA1&^O363H#eeko*b@MwXC}zHXry7Zq8FqvL?8H8J)5 zsL5GpDqgw|I+JE&>LOC1brE+B%`NgR+CYX@bA}tPLZZIRor&WHrg2lauKRRLO){m8 zR_>&S>-_XGaX=XV7^-CM8*N}Z(<)0nRDvW3U)CvRYKo{S%-f)Yo@>r~BzDbbLRUjc zMOfHFXY_b>%uAqlz`fQ9B5)xi|H;!Sx>dzSovxwB;Cx)Z-Xj!95$eW@1@R9)^HHnKWwJMeQ*%hN%R7K~ozTA1=5FAk1JNv|H1gG_< z+3c__G&0r9T}1lHMv6mhFIgYau(OuZ0#Bw)L$52cDwI<4Jh?fEUp&64P7KN7a)N@3 z7E6G7y?Q2F-ZE!&W7nau$LIqcy#mm%ghff7vfLyMM7hWk?czW@dgL6$Lw5h46dyK97=tB+r!Q9Cwo z#~KkBVt#Rl$~o`E>F9LED2i&v3~3fv?|kmYG-OK2inb-Ksf4W)Vp?< zoo`W^a0{R27|4n3MH!uT4LLZRW5ZqhSUgC7D1%up*tl;=mWSpZLvLg6NpL-G3NBqG zaD#XkLGYG#$Dw+BR6v>2M(qe8a7P@&mY3kfzwJO&rScbGCLdS&wR?-Et4S4}Ksr#b z{!Z3d`+N?iwZ(=TmeI|sSXSu!R>fSuC3>|~bt3QPjHAMC_ZD@(AwL=SlI@L;wCv5k7pv zP7Ol@Fv|m5JBNTwjdue=?u3YRfsEj|rD%CztdDrSQ6fsVLxwV$?P~T`hhxjF9knHgE+kIUh z9`m#ZqSqr3%lLN2WXN1sADgBdX63hOv*-iNpL&u(Pwy*?n~*B0yqkItlX2%oMICX) zNUKuxu7_;JuBM18Y_#*PlzvVxjJ2w;+n(ZIV7#P{oC>2-qig>KK(Q`O5aWYYO!$<% ztJLWnSqsx@;==~r>$eDT7h`M9? z-IDBO7Inh3ityDMDt)1D!NTiH(3rrnLwj|MINIbjB?x-X9ZFE>!GkRyZVMyg zHC!OF@alz+(rlf&GYm4N)vo~=*YK(80ENcYHy7Sa)-?_SD8->(YOl|J+-;NmASVC8 z3QqfVE7@kI{4y1jF2An=bsjSSb-l37puv=LM`3=$(357af?2TCJvc;&)IAOV16Ew^G!|v zgJ;3)IW5oG_U5HaorcAvx;tZalWF=Gf^KNP%DDXk+(E-gEZ}oh*CP5b?KC=OAl+A3 zsmqkIso2Tt2%n1`TO3l{qYsWq@=CCh!Mlm|4()*59b2y#^7=kOJ_%{>;U&uVDGTeF zeYy1EB{QKdi&(2{#==#2mCIZY<%g+Etx!bfY>|YAa3M+#97tyBlQM4zivI{pTaQsV zL7qGU`(e(GDq%OUaB(0xFVNifgIq&8$RVz`Y);hRY@#X3@=m*Bygj!Bjxtz9QQ85i z=qIl%GMc^s+JQqK9H%1s%0rn8YGZg71)loBr?_qX$SWbBeI$z6gd! z6Xmh?as{>kS2ozW-m+fT)GPK&S+8qZTAfi2uIdYxHIU(=a0}u3qfeyI09xfSCOi5W zv{JV>A~c7$bqM?mFy*3g4#RVS-aZNh0Xw&&9#3hcC^P8uxwcK|BG-)?DJl~gq~gP7 zX=Z|`J(aMsVDreEs5;Zg;Un`!=T<8LMI`U^;;RYmwQ*EQYGcDTM(@p|>y150nw2S^ z)1OM2;)TafZxeRC%Hz-#;Um-?TDT3=9_rpQuXg>UPO<_o&SX+btII2jg!h@cNqk<< zK@BJq)DwQ1`qsSGHQBM}KHtvxF#avz)p+wvXWP|XUU$oA2w8tigXx&RA8mIfmz0Q9 zgude>P=|`AC>JqWm7aZHiA6xH4J6Bw9o(5*3j1(h`(=i>$A$An2G5S;< zBN0XX@ndvi4avDx7mRUV%3~VvPT|`!{N5mqSq+tgkF`<)JUB;BGNQF*o^9R7Ys`Ah zS){(^sXKu9d7qnRgi1}48y}=787BW>zN9Hj3>#G3a-_!*^-Q8nLe4(owfhfm&#p!{ zpPEV0t=Ojk8iquhM5<%oW7D25CW zV)3opVrWVrhiP4x^NJ|t(QeM8l62qFMhaQ=7waO=@jk{{iHb!5hN+Ewg&WyaM8j^x z`o!>7$2$F-$RJ0c2*hqs_^Ib*MRL_5C>P@CiCIa~N!yc7Z|SA#(>PnZlB~d71O2)7 z(ZjkVi2&a5W)Ti<8i=@fI*MMi!kq;z&2 znLbDq>%R@b5kBC-9Uq4G4d!cR*zyE1agzS;bQbA++X?f2O9*|kP^~hqyKNXzT%5}b z%D%FWp9Se7gEkmQ-HG0@wXqLAKAd!VtOeYw+7PD;LaB2t0DK?AYFh7AZi z6*!~0Uhci{pU;?)rrp0(uF3zPyPbQ-lr-q;m$R$H7EsaAwVfniWY5dDXRm2IxOQOVUo^)QF#N|} zQXzNWiL4BJF#74xa4k{doJ8&^s6r0UC)^u!Lhwq9EFu@E+K>l|!;|lKb<0!8yWME8 z)p!zT-_*^5sF6cTPgoOh#Kb@CjIY;xC)N1+mYBQXyvERRb*F-EuT37^k*^p-%;611BSyzS9o84V8zH_u zD}J6LB2)Jj+Hq&|p8TFYk$sog?M>+K8i`g-Qnkjs$!0srK0PkBMqwpNryPDEpEBR%kV$pi%2bR4gM)#7V!ih&Z>T08L=~gc=ullvp-pEFk*S5cwUBp8gCeYKeiM4=%x+EuJw2)QM)A+6y=!Lg8fv zqvcOjbtGs_4v{#bzS1Ez#OAe@bufw4X-=?1_82yDOn*|uSLr=>9Dn-&u1S$_(KM8O zStb;s`Q4C5=6p$)k9=iafLJc+6n21jfB|}PUa~|!AV7$xynj-U69CK=?RkGb4J@luZ@>*gK{=1 zgJ*fKbj!VrfJ{fNo3e@jee^r=0X&5V3LS@Y)-Xq*W0eX9BLS zJA8Rn{f?qu%@<3T)Jju32@wWjjg5z4OuGLCwfJQ=w5D{zgW^x>LHB^b-pIDDh?gAeDBCcsG(&DYGMX zh(uVE7vfH(cLjq$?99YYYjRASqUF{f%d)XU(p zoC5)(22jV#V=fMhr%5YR8)(>`Q#a(Mjxzo4m6@(o)h~c~a~gK~nXHM~lpV_6fba1I z^f(c^d3hai*b6rD;{T$Jo{=iVk1nh%cQ{SFW59SD072BO22!tlvDC!AJVk&qyul~= zR7DdOr9-V7KO1&cFIwdrFvn)SausIR5Av zfMfTk?PuquBL5`vP=nu#C6}_8`tn**%VeZ#Qi- zx(xYSP4nW-?>R%6e25?H({-NzxZm5)zQLHY18YL7vg?50jfGeKCg|BY7gCJAxkNd@ zxW-2pwAC;Ga_){dPMhsEDF>?}?G4aI}TZ=Ty>OOj_Duk8L-@fCx71WksSo>+Pu} z{b=mC`4{HJqct((iUYmPiOYirAIkH-{CJg_+0egPUNxtRItaLw#!2wFUd@B3@$O&j z>LPeqnmr97X18K+Y#yOElmkMy zHbM`klN7EdZmmYuKZ(5kKS-tF|NQq_Zo<|5E&trZ_h8Mgh_Isb;x}f9iSX|{5kgxWk2(V*LXLL)K>TzjS44=0JCQUPgoFQ(d34hz!p0d{VUVdM-sp=#M z7$_Ohr$1OAh7`XnYXnxVwen)G|3;m~IBzt&6jnDsJ*sPs1P_GdCRa%Q z?3pL+q`I3!7RzY)C$vx;M>Xmk(&M`%GZr`HTj<`7}x@_t#m#Ua!AY1eAblK5wHH`+< z?^z?i=6`2t0US`=TeUzx3rkV{(k`sM3-;@vqC#z6Qx8i;9L3;P*lXtKInwK-UprMB z&K<4lY9c*2ko-ek#m?d9^US)(E)qdP#w$a+4$zU*?{r!8Z#7NW?>WQC_fNLGUX+WJpJvi^qwR6| z)R;&5`8Z*wGcoW+eKtb!08#8OWPmr-_Suq>LC?!7~~cz90G%vh1Ao%5!HLqYkE_PDV<; zzqKl}eq-?ETm6&7PyB;a-a@gT^ad(>wl#CTQzjB~@AaGXV$&3Nx6c;)#?)v`!w-I^ z%Rs-=G};Qk=ZuEWKN<5)7VmdvlZqzh6qBu6GRi06H$LP^m08N*D7Pa$PlQE(pb`GS z->b7S`rp}n{{R&7Tz(JI`nZV4dwcuKTO#^-YETOoi@{5FY^E?}4omd@L0}ii75!g{ zUdan+*$$8UOhNL63u+BV3-kB7{=nBi@b$Nf<_~=R17H8Z*FW&}4}7KX`U7A8z}G+U z^$&dg*Q2;U@bwRT{ZGLD4}AUq&-wZXzW(vP{_(#4@xIJPx119f`|VuVeC)v@ZTpw{0ScZ6FmH%QiK05&wU-A7#(S| zFWoi|qS$=M;tf*hx!s;({5CTu@*gvE6tu!teBcr~;cL37P9hP*xB1Mx9rRS^&5a|t z`RQXToBuI|Lt5m&=ST`!{Cmz-3MhR&RJiRnnN{kpX=!YS6Ty-ZOZukE(|e5p!sDG= z?2AC|@k5B4M|hR^e8z^v$WHF|JqG#}e{x#wdY4EcWNBqaPl7?YfcDE7jstDgL&!s1 z*~jmBV@m+uyJGl~48|NybQ@G<+XMK)1YR^4KJj@ z$r0;?k)oMWt4ju5Z(WKx+VQ-w3SLC>8G`TFVk126e$w2QURa$H01xHuWzK&Is3G?> z9P35U_41qCMNdk8eL8*cTC4ARQn$xcki~ygCb=`g7Z-qYeY?mo2tmJbZ-{6kTWBuI z6#w!0uHQvwD(gc{opFIa&qO1q(ZyQFc@h#Zo!Nica?!5P7Oo#dq4UP*2R`dS)j0Ds z)~)ljjJM?hLL{T{EH3Gy%62&n&qt>e{HFqCVWoztSK`yg*w@218{A?))!nmqJge~7 zGgNh0hKLdbne9HxS$MHyLsRM87qwbJdHn7!_180o`+Oi2n-A|<`{re~y0V!)n%1}0 zj{{trp{rhhWC+7jl}QplZa@Kn=IxIf6r)y7DVq|MU|9o4)DeO6T`H7TjLGOH8+Hfb zt(xKu2`qNrd!GHyoB3@$sms=FIa{PZ2nLtoOPao-$y0 z16;4x`&nM^*wAVtqc<8pGq`;{$FsKrduZU-V8SOv z2%p<=QlJJolG!#Yrx~K@hh=wW6htn3LoUPO z+q<9{w4PHDkSO&WcAv*c(ZCTI1=(AxdmoGrre1xXuW+C8OI`>%qjMl93Af$mu-KiM zt{+M4Tc;G>dn@wKS_G+MW`Q-d=7Nys)yYwFIfy4QF%Y7sm`cBCy0Z4Xh@H!f;>G z5ebu&>k)K-Pq>yg>&iM?<(&z0i}Y}m8qTeJ?FfCbv*6GY=#cn)w5_N2vxY+qvI%Gib z;6%-cj(O%$_{4+t=eROv+Pkvob`PdFr0EzigdSRHy%(fCR+U?%YUU?&)GUr8H!m%yDIkOKf$Qg?2TOWJ= z$j#c%oVHWyuqH={LAey=U(Ur?*i!+Qrmb2t>*f{d<0#GY*|jN-sgHlk!4%eJXe41O z9uwcN=@ZjKxtsD{e?if`ah|!Sic9+;tI&g#YwCN&A4K)@oS9$1AfkQUR71=;WGikN zXs-z~Js;5}VR&XjPhWpC552m}r@C=PtPu>gYZ2shekeFw*Zy%3-8X;t=UW|O(Pb3}d zbdWJWs8YZS?@mnAsHxlQ+;u@g}Z)Y z8DuXkGV=4iL?!C4;5F*GkqWOyNVlqgmy^%~(R;Q|eAz)_r8k?Ujr|d^ANAism_jJGcg^e2`GD>Bv_!Hnj?V&bJ`iH5mmRVU>ri#{ke$^4n(xe$gYkD z^!amrFOeNY;D|r}I*`_}kY704+0u+ZPRUKBX^l*FbtZTgI({_k zgh;0u;MoS<=*>Fq&{=?3ompTcVG;=Dv_c9`$p)G!(ah?PCLj8emJR}m3@=-HHYTt8 zX7xw4uPuaEgh8$`*`ja*cX)gYLhvY%L*qy$SWj{rN%&wOzfP9VsbXN{Ad_mWODo2J0O+$1wt~7{oz}#~)~l2ZRxPWi$=~Oj5}ZV9 z=MHUR;EhZZA)0e8Q_G2qe0nbn3)8cGzbBg&muJ*qN$tmX08e7x*@Aol!+?9qY>B2s zYlX|F>X5Cu!Fv84tNbj%Sc zrfK`(r61_^#x?Hte=3UKbpx_Vpw3vnxMAbI_jzsgV7j)937sut9};q*5x5J}_Sh%* zcq(~d#UDN#?It2lJ>?n0Rz0)rqcDNIa?hr_-Lv zT_3+1ciIQ#in69`y5lCs&TOn}g;VxTKogZ}qR>}!GWwaxrz3n@RRJ-hN>|Ej*4=C2 z*7Yk%2IPU{S_+dhPI{38f*|oV_=VjZmdn-%qQ<{U3}c`NcTICfG|6KJ3vPtUbU99s z*~1ZY1qHB@pEPyoV46%S5hPMknI{+QZZiAbWm9{qt1QE&_E6JUFe78{Oqk&XRT|+W zo;6(tGw9`7XcDfVFc-)-lWP2V=jkNos>rR2jnMhy__6xN)Pxu9?@_+G(k5oJ2H|SwOj}9nu#|f2TbGP zRV5G=9jk0k^K(x@S*`mKa*V6%>x8x=c22P-rDI_$@o1_61ZBvMOTGNPO&dU!Of zFQS|kPU@kyU`#n*dQ3kFuHeu7V$_$){JAvR(c_5P%+C|G%K^@w`?Vukb<|XSV(C>- zuCKZv&?2{1OQvH%d2)7o!e&^L*3|#Kv>m;mZcB2@sBUkjL4CO{FL;^wple4r-w&UB<|)b z&xKdN4Ai-_obS!)z>#8lhT7&wcpOLYsolkZULxv^2bs$4-1R6zf*Kc-l8>)kl$SP; zc+4+iLfZ|J9+QX8d?C=C30Wz>T{`jAo$R!#-oe8*rR@Xthz#w{2DKU#wyp8(_t|Sd zypGa{C>`NN zTjhiki2zbrvx?-Hn?VQG&w;PCHY>y4vcg=A$gtMONcgiI8$Q+`&e_&m6n=|yTGF&>X z{0xeIamY#6QhP{Gu2((&lO<)u;{H!{#$SLAFNRaH|6!J7ZN;9~Q*GXV=J@~c5j)Wd zZ9?Y;jX=@$*&C*Zy2_CUJUDeqUOcWwScu22(r}V^>kVtnZ?S%=(^Aw>7xXcuLAEpe z0xTqXb4v2aR~(=shhOAF?hJa!i*)^indjgwm5xnH})gG*=xXr{#@n5Ei{=>k+>bTBnAEzYI$rQJ%KJ#EB z7-R6|?5PZUw!SgOlQ23qEo3)q7*#qkTGW5sV5&5*%$m@z1vy?|u*gR{bY->){rEC+ zBtH|IRAh1-qG-EysoVVhBD0RYSfj=!#{hJhoi*om-w04^2~i_J>t?pxt8*5DpI22q zMNqz)5ziXwOsrns0%GfWgEBS2Tv$~b?m_}1?Y@QN{Ow%9IaDmsq)!>Jt#!JUKUeCo z>WW*g?$=8+!q(M@p2Se6e?Fis9ZHACHih+aZWMgBc;jQ1{NLDn>!`T4Y+bw(AOZnG za8Gb2xI=Pq4}^kHSQ1bmL z`scdlU1z9nM(-0+_x1duX&7;sCecetUe3I9mB@PEpyAEQcoLMhDx{*Nom}aEVFXWL z334~6n}8vN?Qeh(I$gtDq~lcCs$? zsC(o+#bGD9jW_3Lx2rJQlrXvQ^!5Si+CUDXn%u_g-kUbo4!W6*5$*=x&#L!NIOqOs z!ANZ`nf*59K5gYp{%%AC;s=PG;8=M2z0S*^lE?D2{ja+by$5gBEcwdEq)rV;{mCne zFlR#Mw$pMOMs-UnTkM@1d$&c0)nuQ$uNo0@*RGa9L~C|S%w7WCvKCNc1*QNAO4*w^ zz`Nku$^<=5e?OC%D(BNRUN9U})K^E6 z7CIv)$?}RX#FrU#HeM+kbJLZMb7Eh3>lw9rf%~k4lx+;TClKsTOTU{LiD{0NV=3Xr zUAud%cay#)a7pAS%uy`VnFu#mWQQ%N^x5e*%V!BIB(1OnU1~rKD)r~PwoZsk4%KcN zQb(ERStMekuB6p3(i60n1dXcQ`3Qj33Hcw>Vd~vajZULPx%uy<@|fsqHp2O`)fiV` zw}_yX_+Pzk*vesW(mc+0!!5L1dDc7hp&>YiC)EmLL;)xq$WvjKDq-#>hJ<`&Rsmz= zuDkOA>SPw?6_M$mn5$Nw?L1X#d2Y7~o0|Le?^XtZ!+7``!n53vxY8VsKztBGln}%f zcOHF;8|Z$8H#$f$3CntTZbgh%Z1o^G_OOzni_>{V@o1KNfj*&6);2r47PKB#urFHP zI2c5dtbyoMj2kBHp-s?#Fiq(k{u?4uEi`%JqLC9kS~+oWQcu#Cz0o4TT4>(pZeY2Y zv9cj^{nv&0tJ7*?7nHc&tJydL38EnyV_m$_N1hrSkhg3S(fFB{Q1zoE?s}e4^}0GS zkq+6TBX7CV8q30$5fxo(FCi+oc|Wv#nR-(nLU^s7q#~ty_)1KCn-DPq2WHJ1Hm97x z#l|6RD?Gfnul|`E`QxPnP(}1Udw=--jqvJrFH=Y9f?Z4gbUNOv#{ar5)cM3amQ!{s zOvyBgT6Lp`PD($p{r7iq5`Tvz^|JVvca&JEzWkbj6STs~u#?M_e&p^<%Hgt(zkNh* z!tPYh^Q!yUe0#^4ba~g6d{%7S-LLHp#d!I4M7vO~)ad)(QMOD+2n-KG9~_$G(v4c$ zdnK>q^WLa-k4G(D7_LKj|Nfj4yS7Jp?P8~;dGVv;*!^@NT%o@<+kNvPmqvEZ3>8@O zcpuw~Lo%0=t?hLCvgwsgwwbo+auX7GD5VS(ezY%l{m`BNKHzjI?KYJ$ryDjT-9Akn z^O&G)r!r7iI&w+Yu1u$_L9?kZOn62J%mwCt#(TpkMeSb>|8o+ORGi? zQ84YCXsCbYWviWBKPyrXJ9KQfJ_Ct_Vq4J<<_87AWmLLa&uYUSY4P2w;xw$N)X^41 zOfuDqP#+~O@98*oUKoP?u`~LtNoCXwYv#7tPgScE+yxgz2{N)2rj1`n@ovtN_8zZ0 zU?Nr#?Z`f@NR0X*U4?Fz!fe>Eo;5Mc)>hXL?F_cEtR$O%Jn|W~^)CQ!ceaMQlR@)b zw*yjEl4fzAUV_O33XKim6{02rPY>*^2Z#jn*BH2gJD~Y}^n64aq_4R6UohWBV>wXm z0*b1sS;P203-StytAblux9r7<#@EkehIO_UjR}E*-+GkQsnw4~tqGs8yFZg6s(J1v z1k?=HV%tT(gplYJYuf0f0c3|s63=(IonZhaNV}xE1s{bIr!{K zdk(iwFEN<<{kt>sweP#OMsNI}U)CWIz+4V2_p$In_*3WXi__XY@oM4tZ>fXw^nfcI z+mK&cSmgq#im4_!knlCSN~2r#)VG6pUMvT9fGLOORW0;4?{B{*(cA!+?f_+*-rfIA zzk-efRD`>D_qNu#K)E%6&|4r`^L-5JLzQYkv`;} zFd0#FEKaXE|N54E`65P@yudJRc|wKJt!i@Zx!NkBa#&b~?C^EEybifZU>F;ABq-zD z$;%<^<%`FkJRb{6Ulg|80X~i10V2fzXjiR7uX=9qlr!#yq5MyzIL)V8>7fASonj%A zPwbQKvBr|3840e52AYWXN}nv=!+Rm4b<$;U#)lPU@XAZaVymRT(7g zJ_km>cax13up=5ZK}h7IvsTRp84#9_?FqqSmtX&TD`3t~S@uZ`qDx#`K%&q)fJ=wD zU7?DiH09B*nnQjyP?haK5zC^$fNn2v`H>tc?OgAkmmXUBFcYTtFyxy-P=mx7CFdpn zhEOYKhd%W+PV7z4V!;2=Z~9?GpV5v#Y&Lh7?Ky!4N#E?`qB}~0qXB)8GEzNy(ebvT z@LbF4ss2u16fD8;v{$EsVrKc$os^Z5A{584#nR{C<;6Gj4tk)NBO6~;6#oYnTB~xo zv9dEkE4<4muiOf39#LHiiuv}?1}EKEOs(H5*<^M++gg~+Zn@xFz9+XWc#Ja~p8rt+ghbp33UqN>4yib<2xvzbtqX2GoxIEm}kXoqg9SOy+!pgv36ZPF+ysMOlw-u(Q$ zoR~scu2(xD>acdZ*$WjX&-`h#m#a4cWItxT8}RpdSC{kbvTzf`{F5_uwx#|MvwDwj zY>!zS1MxkdP?@2h>v>f2=<5*^_i%^nAXOi+t~$>S#-DX=-4Gd4`tI!2IBfQ-W97(q z278lO9bzII1CHgxkUo`_{?8GW9dy=awm6St<8$3;P3>Yt8GR6}K-6^78vjga0rFbM zVp>d01NOXeX^*?EgtRtEci4%q6ad(OOVpgJ)?{7a&${Q%s(WDNP`(nRC*(8EeVohg z3~D4DVtd9gW9RI42s++zV-MA{Ukr_02kO~-se1dZ7}!o1`rVp7BB_uA*BR>LF)_%rBuu>LhjxZjyuq%i9p z;7>MS?q_XPMJ0@>5GzbpxMzbgOTx2~#|03fiH&=cLwd@3ri+@CpO;RO_^N?(q-LHS z)i0Gs;^PiQOrGJaB3?oOTTct~j9(3kbI;dmWq5=#)R|b-O_TRut!NRH#>=fwZYRRDU+5K zx6gIBNsDuZdP+%+*-O$VUJuWurSq1$hLkx)v?-HBMGHm@G~&b7PO#f(y7uCV(PPGm z5|ii25f0g0vV?3SbprNpMyTRR9hB)CJyZfViNOthS83%VI z7HXh;wB}Hw`eQ_G6gaMM5l^e9-2XX6e30KyfnpErllEp9En{kaWlnrKYEjj0FV#-| zfTb(T;w?QX5^38Mwyk%r#mdiS{9q=Um|e!(l;FFw^J>Jyg`! zbF`1mvurRJF{?B!>+W$KS%3~-XNmw+EDI0b_%rz0n?n#PUEf0b!PTH*PE*@T*3bN5-Sg+H*@YllBweqm53sE*-wA|4gju^ znhD;6z>8NF94UDr5=KZY9HtrwSAheQH8m|UYpP3WPrqj- z6=;wmf>X^6G97gLR{EV)kB=$xx?`-knhnn}PpVx-ZT+Std5zF03$fKidVKUXEJ6DE8;ell9q?89SS1 zt1xxt4?P1)OGjf?@ds=VP23)AIXmwl;pa!wCUD$Y~?@2@dduE`1E%O*?yT@sKq)d$ z`M?TTW9BjhD~Z-nPb3(0QP}>$reeZQfHsjCX4~4MW9EsCT$7Y$A2rl>AKR46*`;Li zd%nbrcKah;g?*Z4+PL0S%8uOk!T>MR58mWcbzTq7Su?BB&Gbkc?$S~D7`tiZuEXl; zs!DstKP(dtEKWP(_{n}lR(CKzrbkDLy@F#x&9A67&r;8X#=C++S6Lf9Qm5R#@d-k< zP8lhOoTw$nuPmxZuR8P-_05^^TPJm=EdY-(m3HPcv1^QT`Z37sq1!83Tfs(z2HnZ# zhvQZtC%5o0%swc}HT)S(??tFf-SEgVqOj=Ev{x-Rbd{!dtV^2hNL*M8@#yER{^*Bb zhH38<3JUFX#-+E)W8chRr~;3!?)-uUyow2 z9gfVGl$X~mnluHth%NbQ&w_Na#hpEKOZRM|`!|g9!^$g_#$GLqQLT(*wxnTa1z>x6 zEDB)TD|!Y69ZGvbb4g#oC!9@`Awh>K(+^(o%<*erc5y9k4mfm_tXa3LS*N8PDJ@b` zQJS`Z!I>-qlTWUNxhd(smK0mRq4lJn-xzqHff&xGbfa%Y)*E`)ervv?d?B z4%jNN^4(<+awWdjMHJ<1J3yf|zimj)&;zBy*H+#AP2|I^8Dl*)_`D-iAFyujJIo)Z z%FG78@@K6Po^viqk3hKi>}+Lv;ByT(yigqtertz%nNkR*d6?~ETl!=vy;eC$rw~IA zrKxOJqgxW=EWB0CtF5 zM0m?^Ab}bvT`ez6_0=*(w6T;MZRwX*%Jb2S1W|O_|* ziYI63=X@eQaGoGT&`MRo- zvo&WM+C1%>wBj=r4l~oR%Ru_oNL}4!-DP>YNQJTEgKgeE zh!-=G)WJBYpTE~0!7zO80q%Obxt}~b(OgJM*OqFGz2wf#*)L*u3~Yn19sjb9bEBV8l_ zEuRihiQqJ9V2fO7+oRPjxNxTjA6YfYZyI^#^=YwFyZQ{iemHtA1EO*m4BK9=cEVQ< z>iDt1!qv~eZ5nCDBw|sJz-(^5weoz6sExdPZD$%)n;yb0_5``!zQ{D3nSyU`UcXWN zi?Z2#yLh)^BALX)5j0`1g2|i>*1H1~R_>H&HeJ&)qy`DnYrfkNCO;71?_(D<&y6o@ zGDA|*DJ4}`c!XN|K*)}k5FgD-u}q0;AkMyPK-nmnDR$nRx*SjdL$0Y44n*lNo@9hg zx8HV`7G*B>V2dcmgg!>g4#z@sSxeBwOVTCjeUBt7yT)r((? z9B69govN)+ax9n(_HaC{pUWJ))FO6XW+z`fg*6uJHu38)*aQf6wDl$`D=)Lo{)(a3 z;N>1`n$hTO5lth>B*@_s32vEjXe+Pae=reD)onGcmy&P?2uD;@_}(kBDGcj0zIeiJ zi(~17PE_;o$;&Ulp=PXkBDoK#^kmKn9S>d>& zxeLCfjk{bWW^emBLqFN)r_r=8$%nNUn;1GurX8nE_geMcdM8y|MidC4tnJ!3U9-FctoGai zHWzML$9_5}dqo7tFiYzUCK=wMN?fPrqcXolh5{MA`MG0jdGaFc_eZvS5qE%|s5`(| z^UeD|572xE2r(4B^1elS9|<;J65$Wz-vPE$Z&Wg*`(pY9=D4 z^bQckcn8S!zR0};kaW79+yQ=7-J2uv$1^{~S{7pjP3XASjXz;3J~;wv^n!G0gZN{` zkbtNhUxx>*3F6`Un$5RLskg~@faSE~LH96qkEtftd%7TmZZwz-P^MX8fUi|EWU zR^D}rZdCQ!KCTeFpWHjZR-GRIRs4Iqg!!JDs|)9=X_%U&*Xu(GhNV+$a>KTRYm>Lm z{rjqwW3u)QJytni;E`@$Y1hIM(z~)S(w^Mt3V$!+E@i!jQ<~(Qclj;B-De>32YtaM z(=D0hBYl*3h4|N5TQuu@{i~*|`BO`@!K!{kozx(QIu4g6S4~CJ4lb;5yMu}ntELrg zN$gP$TJOE~hsocLg!gAXw*2b{j2>b~A};4YsZV>8x~zwcekPRknQB)z2A;sR)Mt-l z+r%ha)tFzNTY5^_j>3wsrUVwo%j4HR;f;>1H6gF>r4O0Y#B@5r->C6@KoYm1LE+FTnCn!0S-k3{P#aWjgudyVpI`E$sE;z-$| zRWd@86H^tIU0JA5e6z@xB6@(|W7{WQn0Ekt7wTgZ#L@?ZAS~;*6hwaGuVE2mac^m8-psf*in(Rfex7~P1;HAW{#?-` zQc!L&IkWdrE|_dOSm=xS^{33`;7V#{mBzJaVIyM)#qQ_(Lbht}GR=m>D-efbBJjPJ z|2gMYbbt`dTGo7OJh)+IoauY2#A4{H^;rD1JQbS4Vr&s`$REgS?6-H;Ct0Dg|WQ;_C4Rcv^%PJM{abSs$iaxx zHP_Iql<>P`2yjFv&rE8hzD|#>n0~ZzwbOw13(A&$aIEuua{-tLY5G1I3ghw92~Tjl z%GB4UERAhEZll#B>io&hJ}vPBmnub!$dR%$YzgTsU$4>*D?OGQfk<`*+^c#2yap;P zeVyHAEnG8JX3D7%*XnOw2e)IlG^P0Or1q2&GMiN!5d}@hAr(EwV>rJLMNc$V@IV>3 zkfuhGb!8Fj*!#oj3R)8{R>;kIR@4B=Rh?$-gXZpr926aCU*xES(ZiZ!JchKD73uI) z8CkcCb~4Bq<7cyxn@c0PUE>+K9paJc z_~&&sF|oH+q0XFaAy<+@AkH#yqH?42c}b zQNZ) zY5VlQ`Cpoq$BXHN%5U?^x=f2Vb{Z>_M3Z_`qT8^x_Zm@~Y{F|H-ouaAkVvy&4^1At zu!8N+CTolx^>IsORj(Z;*hC3F*U%g49Qyl`43Sydj>Ns!;UEeDom@967pz%^)g>%r z(;YlX>A_`nFu}!Pch-DRQcw1)Nuj#oGqlBGN#8Ym1)e0plGkIG=xD-nRb!^DrmRMA z66pl|A#CCl!!1t^N4z!<;|4t0_@>+Sw$SE-1m(Vyf`vs2`;ttR@Vzn_ZyZ6Cc8P|1 zCm9Vi;v7vdB1=RS{LV1poNq)VodmAR*o%dO?X$$cy~;bhq>azb&Aj_7S+o?D=f1i4 zP5yTGF`wc$E0niMCts8I1ba;Y<{79qme)!;=`HkOjPjctmgfud`{+1xkov(B_bTpuv@QUUB4^j>gq|ppvPMAl20t^ z%k9ci66byX-)TY8GKBEgO3l&v?O=k7tB||ov`quV#sM=Q`ElA?emXsreQZwK8VKPpY~0_UqW+& zS#rNclZb0mpnq=m#yBxk*fBsR__{wx~W#^jUsy=%y@7L)C8o{Ef2fz?$IWedj{ZMG0T}~sMSc<0 znK@Q%^5jv$wn6qFqGpvpdd6h!Dsgn)q&IF}jQqn$of5UG7>=^}(eNF8k!o8V+dqDXr zCaV_L52f#GU-@@a=|dmbO0Q`k{l}Dg<;f}bLR%Rdz{Os5FT`jq(u0sS*XL=u#IlS_(Xi*I!3mDGsZjYr15is@70bKAS8nEF6M81H_|kq8j}s8edED3eEMf8n<$2W0Ojil!l) zD$Nh=3y~YJj1c(PDxTrtn4UF3RSm5d&xAkW0oh*r6d77;VYZX#!I5$tS< z{A+qvx`R6LuYAv;TG0=+5d^Gf)ogWKqRKR@xUe)VSuXL$keg{@3 zo^!{&!aG5BH@7G}r$AWPVkWWpWTZtIEhj~r(TeB!n`f3EXhjEIrp++gVvndBLH zCs)s{=?<~{CMDapHRYwPJMApoZ!}I}+>>0(CE1g`KRWlAKv=0*Pf;U>v;ioQP+~G; zQP2u!GfRW%%OSzwQ2jg?N8^Sr)7G*8n{x3>!2^wv2|!%vYU$Oj2To|-AbHC9$$ zhn@4x`bXrB-gt~ljn?=!FljYu>*{wvX&@voEvz6zAr~mildOPfkeYn6L7k(ZlxxL? zmab-bvl0vqqqdE?Q7N{XrsF~mSgCe_Pk{%TqHi|Q;>-<`I#g~A7LwsyB^ZOJi*uD8 z{sA61wI~lp0e7*>NyMZMZSAyaX3PW^uiUHGh&NrrLQi(CRo-Cf>vo~#0{HDY-uHNv z7AhZ&L@jD=CEkkYSi;rSXUsRYjP_%LqdAMr;!mnzKs$IsSo|Qn-%|=b^?8#oLWz(H zSDE!`Blvw8+RwUk{Vnhd{ZeTCK&y`EiN;PLZ(X)Akk#PbB}ET4Nqki?glu$OMz0sJ zz*K94c@(ySAr;&2mig$OcYoDu?>Do#+Dt|Uau+L+OF-3`jMR3%+srT|Gjta3OQq$@ zE_V^?s%zpkss_2^kP{LKSf#-y-LthIVtZOzYLdWbpJU3PVVd1%jh)uti`Q^bqF=x# zpvO}JX-S)!0XrYtC@A!u(vBlgsu7z?SBEtpVCc1QBxe$djoSp%U zlA@`Gd>%^^dTnjBKxwFInEJduMP!9#F#BL`Rf|($PGEF7_hWcYnzSNA+%cA12t={} z5fYSB98<-KS9`3p_BE!wPNtWZJ@T{r zYyHm07&RAS?ZWfSpRxNFjQ!@oMJX~m0w0X?Q=ZIzFv<_EG|P_eMtptDGy4LDJ zSun;|8&XF`YPQ(;9y4w+re!GPg-jqNu5Tri5-V=yN0Q=7>@ICqc6c0y7>{vO^3s>6 ziTPAQ{`^3PWb-=A;d_a=Zg!|qnjH+15NOM}cdtR#S_R9yvJeFw(?Hzz+j_hgr z7U=ZUV||->LNb!Kc^HfAy2a`u%X>{Vjfn5ucluE74$xaI;87Yc87r4>ga5Pgyw0YRvUIW*=5R$m(6|TC5f4(tc0KlzjANtp78hwxMAVdwP1~ z2?DshRNxqsj(JM)5m`SmZRaFg9uSzeydCx#@fNbV&oFD+ynNB z%{MA7m!)TOH~eg7{{q7F8=cT3=V! z-d7UlY#44XWvgZ_Rx%)+gX`Q5Y)rkj?fi4cu zTDUHuL#ZJ;vGtEf{oT{T3aBhj$qY6yLOmDZ9_kWDMc1krMQ1(VqM=(L&x^QEmI4PD zaEE_$q<356k(E)Wk@+nmSpoIzVF_bT!WbT8vjkLf{If%*KZIicIC}c~fe%&)2`LEi zCtBp6LB^r+YnA|y>`Y~pIO7|Dz=nQz0JdqDHQsDZ{b$&W)p?(CLLEZa@?(vrn$`d> zdizEACwgp8f^tE)EZ?ZZXy-mpi+9nGoCQS@g`wTk;N<|n*D$zYN zTv{+1Ryuw`Hd6NM#T&9|xi7g(qI5r9oV6AXMWmI*v}zsT@A;&A3yM)e@kez}TyrDV zm3ZVcS2D;O`O-e38nBj9pL1GwS19s%d#A)y@kQ$F11C|l%piFy!jS9)eoZN%6m&=+ z8G1OteDBdsz%zMvqIfVznWANuXZZ;#bX>(J2kn8)tAMT&eH8sEkIu4sBo3!Sb@ou+*cnI#)hh>wQ8aN0PZ#G(CsH21R!4+Z9<>3zj%i4cf@3 zQy1yc50GzKr&8W`4LG zdag%Ci(zl=8fNz{4A(ucF!NZMe@zq{(@MVwA>&`=ShbRw8$UCP{P3ZN8WG(2hs$i~nNjp@7eQ zWA)a{SYL>WB70hr6vkp(w6Iao`AJP# z&E?HRMxHvsNGA_5){g~4*pMI<`j1fEJwHE?Ag4xajv&1)WCcAULsoLJsJCWQ1IlvW zz{8f5j9_$job74p#Jguyl2>B1k2Ocmze(xl%KiP${2h7!9nSp$+5Ia%>gKN9KWCQ= zj!Pu&0BVk(*CaS`?*R9)Px5^a;Z^WP^QiZ)?Al2F1Yj*>_Fz8!LX{3{7o%V0RPC{xbOUQMt$9?G0g8^8#XQ!96e7EU(Z|j@$Y|y~=4dBXYeJu`76h8DhqfJ}&^h znN9vxUHwr?y4C5wXIhB<1Uju?#ezaNY9H?a1NEjohHrH73}?+g*UoGe!*JX`7k)nw zE&H;F;H`>iJRJcw|4Jz1Y!))z?P>0C7|!)i@N}5<#B1IP03XOqN#AGfrVAflA49!n z6K~%C@lZz+>XO*i`x$3-wdZeTSG^thK)jGtdA7Q+-0VGFiuSj2@_znuXui+w4L8)1 z>k;MM0ZbZj_XoAF?QITK&n$o5-mIloGu#0Pwq8r=Df>kOgpEkg8x$?yGM;U|@)S6jj~*md=5UY~+>A99^fnISr7Xm8|a;lF)-a6yH#TwfbC6<2II+pKWFegjsf;=B{E)CwO-;sXB zT+2J@Hz1uYnkDGt)@u3Mjpz_<{*jP-?ZjKxUUwGRb6Ht=y6ExQ3A>(Wo(;uoD9BUM zq|jGG=DLaUIPW_Z>Qg+u7QLQ!D(*DUlp%l;G|#dMg0$&tjuoZR%yK9*Vf}`dY0h!O zqq;IpR#UfO8H);3g1yhp@Z9rK%)W(7vOSV+a0 zAuQy^PYx707`17>*vXe@wTUh6RS?LC27S_03J?6+)=E z^Dsl@%IoPjPA@pLk6JvT>n$9lJyGPqu<7@4D|?0KcK~@xV|F?dAtw$yVJB`CgQpMT z9y+gIXF*cM@85P?@JxbLn z-BXQSTQ^X2RJa_Xnb0RA7OggatVl@Z%sx4Y6wFG9Ing4Lwc+V{CVdEfFWOj)JsV2V zQSnm5lbCqvR*mAf%4xfsD6SP1dY5+6)X^CZS3b&K%q^Lj9JtE)TQysvUQEe8jhDxZ zAwu7mg(&-R`9PVyHsQqbT-tf}&F3GMORC+|!Hy`+k!w_*fLk1;A|EdhWNzQeU9(p|D!Gcg`0&172BaAO&hlsXcrO0 zYD~Z%x4po7;n{mW#EOc1jSvnq;eT`)3g}tZb#P;E$SkniW;Zt0bJS}RS}VyQ+DaLY z!b2jUSE1qlF9ts55 z^u8)Sqq%rWy>$f z%g)Rx>fED9N-uq_FcT14H{Q5Z?3zoNc&Upze*8M>W8UH|v6vFjDvCZE%3@|Nlvh!h z&1p{XCZOGy#V_y)dYI{M*Qi2Y#!?uiO;h#D?Ccu2%etqM!(RQQc#u$XlR2r(rm_f? zVv=hjiRd7txi|Dvor#I%gB6~* zrdWboD+=LnfkriYYy!;8FC)X|_~=C=VC9wx0z<5|@N-98G|=X7lfR<1Nd_40nLqz>*BU&%$zr=bSvl`PuAkW{LT*j@Px9;eCo3<9R zTBdqsS`+Lz2sO0HEh#PV&`^`p?Fw=;r+3h(e=u;RsFRb7jh1cikKUfvh^&yaD(7D} zu5Ox;)S$X`$*1hA2{#aR4Mp&GZx>tbT?9=DTRY?`=d%_^gS@!q3mZ#HyA=1`C%?04 zj9av3^zNKnZ_CW>R8P+GRcC5z**WtD2k@*o@bPk&8LYt6XK-lkIr6{p3-m5I?AB-M zPxV(k*B+M5;Mb&F2={n0fDN&6S;Kj=V^mic0n=Zb!0!=x<#6afPPN&)UKXoy4(VHW z;X}^AK9~sT$iJ6Xvt|&kJJVfdPmvJ?@(nAKIWeUAIC=231~1_{f-W=8 z2v2&Z8tJxcPu+>CN%PCKXr=DenhAMqk#Ds|PM22Z@`Qu;KiY{^tQf2*=kQty(jkQ#g=(VCX-n~0XzmD6oK5ive0bXMqU z452rIh-)?FM<}xfCwN6)>MtQ=6*^XL?sc~-LX zDt@ees;@T5BuuQl1mgNd(;b0TwtZvcyQ1nqa5Tgxb#j6zK$@`_Fh5d3GG$vbSi(*@ zajXN$Mqb|Ve7lfxj3HFcs5x_dj~U3MjH()T@--RgNAhqwf7&IXa5W^SpSjAl7QPKh zvQfwH-|wnco`-|HXX;qQXrF>!gy{)7Syrr<{xaLt@KWc$=Rs9H2D!FlGIA7zSpw>{ zM#jbv3BD-*w{);q!nDH~L?|Zc|Rxr6f zk&ip8HTBkxv?At-y=O8)^Xt&gM_m$vZ20h7&XrhlPoRU-OqNPIL2-U=q+q>Vc0!4Q ze)raIqbOzkwg5Y8Kjb>tXyPE)|1^3z{>*#{JM76cJ$q+Cil!4MWZB&)Os27dxBGw& zT_R@B`cfXNCkHrc%@P zUta^DQ^i7rns2dr)kz=MToSeppGS_4B6_5GRU;A@`|a2l^sF9cb6sP^5|ry4CfUzm zHz6!?!Xm?LKK?(-Ul)AVV+2|IR&nfl?s|%STbhuTVX17`DcyuO4F?3{ zfBfe+Wc;TrJ=TkT8{_#g9g9d=nO>wT^XyqD)W51K;-Cmd9HXkG9HO^nMsYSF{r24j z)}vWEyqc=;zDCwDv(5LckY?ysM!&(D6s>Zo{hvQ1ah;E_4X|%LM zO~+D?RO?#yx%89sa}rLeBTkaKKUp$D81T#Ig1C(TB;tKi=xR7;x8&96yD?c~S~x zPWSMyphrCygl-+}9@Gq_MD;j|vO1cm+GVCB#bDXM15E+GQgcHj1p@oIObgbFgdwwx z*y>D}TFXmxNCz5d*UOfe&p6QWeP=8$Qj)WSuBjGB-X8suRki3+PAsrSg{9Sl`THUKhe< zIB+btcgJBIAIFr2VXL@3WsR9RIerfm2}P~G2}H|G0*1vv2crROS~`r36iI~1`^bIM z*&k8q6CKi`vyPg3n$577Yht_ukAw!$ju1N~SSMjUAj6J>orp_pokvEH+1@(Fri>RiiIYo68HtztI z{+pXbEP4v>cVp4Qefv;t@Wz1qd4kX<;qh_fsF;cUO<1FU2jSL)ZB@m!v>wIy3n>$@ zq-w$kfod=7?@}LXLcgv~EF(Vkm~C6kB1EO}1MSQX#W7nb`FLMre#l=jdexA@U-rmn z3*iS9V^0{7=CqNv$jtrmP-xkw$f;Qn^Eix?Pc$;Lxlp$B>;h>wzeaO#mSTM0fvi$E zlhZiK3wQ4-T)??VH273d-DXz_`ynbL9?PpcQdJ+bl1@&y`SaEmE`#5p#3~V((dJ}U zIJqFLmbXQ|eJp?c<+Li|5-7>KNJs@~XX&qeRo@7`)W)LC${~wXkwcCR9*<;xJOMCi~TnCa6P9Tx=h1(*&Q<{GML6|= zW6uSQE5u5E6pp-T84Z2XRL^ET8POQC@41lK7}=+5GXGAOYw1>2D{z%ho1$9Lvq_}b zN;64i*AdK4=l7<^I5I3jQ)cvY)K(I1oWkU-HlJW|3I!I!GTVn=3plX3t$`t`2EE;J zwi=}?W*uc7vEc*~1`phsYwp{Rl!HIlWrkt0k8996uTg}m9vu+HbDV*$@BHLI^a$Cv zqK=MMj)n^uiWX_moY#}8R9U7*`#G`Tj)u$~)nsAyJHQ%WOTT?}&4hlb;~_ z`VVX2f_!G0i0B`JkBRz(3@J#Co<6f9vwU&nHMn2rSMIKGR;#1lII;}!s7&zu(Olw-alTNm$2#O*2$Kyo-5eg0Pn$dEK2Mr zRRtM{Bnj#4dl+Avzl3FRdf*2NdcQC$WVK@rRmz!DH`~r<1)maKA(<~Fzd=UFyC@E7 zU<2-wQ}ZUZCFJJl+bR4j6tcCSLKo5SS<9vhc7R$6Gsc8`8lSZdi}s1~>GlcP zen`@V+nB0!LE!Hut8hA`Z?2b{(JF2Pz5|IV+WcbKpmSC~ufIbx)|}u#jU~rtMG5AL z3*_d=XFuf84fftOJWzQ_*O5{t~5CnPDA`(jVu*GE64Gd{D)t;?7YifZ<3Kjn;v zk65UB&Z=Ku2H&>kT`s%tEyphO8=Mi!4&k?C4|rYst6wI9iL%b;Iyjr-ns->jQ~orj zD48x|zsB*OIL;(r?~l=Zs`2S$aj_9{kWGIe#>CnTY~7m0#2c-DQ=pqxV@uw2BkMC6+ZG2 zPqtsiDqk2E3U3p>AD^gkm+la4p}6L&Jfp8U9)TFV_w5Jb5X1&^;ai>Hw*EEcr6FOW z{}V}nr2DpcX?yQ4p+iCz6%GqqnB=L2G|-!Nl$o3huLY?gj6w zKHU4$w^!AfXGerLpVW@pjCwXWg7;=ZJx+*{EUH~z*^L^U65qzC5nQP3_oo-DEx5l1 z->?r?h>o1kKb*tnrW=UKovv9Ay-oWSMdcd)%NA>7gp!ECjrDDF*Gfw>(K%VVlj~Nh zYJ5K4yzt^$T~ca@SS=ub5BnzEAOBD98nkyQN; zOZ4AWJ^!am<{u_txQkG{`bUNPFV26cZQ@q^yKwp7Un8ZcNcP`yh^mH#KLb?1!;qFRRdI~m&};6 zI0btQ){@9k7jRS6YIiMW!QL~B^C`cgpl96BH0G*DN2+(abA@+9U8V+V*6v_=wAVfI zukt{fQ7ic|39~aRk+Bs)H4;MQ#YM?kd4%$vOE|08HYBHneAVYiC@9Cfw&}5B(yQqW zfSWPi<8GJk#+t0#IKA%dfHR$pszHF3E|4?+vd7cYP@n7)((s_7srP}jrC_WRtH4~% zb`c<-uW_E(rgE@DoE+#;VbRd=h+Of~m9m=-CSt!1KH`-~F`~Cf8_BUUsM6~5pJ%+? zO1Lx=Ts5B6@K(%I>S1aysW@fC+!bcdT4d2+q!d>YLV`^!ILSJvFyoc47qhn!o2qIK ztiPh@Dp*AmZCdrUH#EfcpRs1;_QKHCtRD!;^~#L7*GtIeROQ{=?%?yB@TB}kfXKZl zZ)c?&jvRgp!!No(f|`ldbQ{Kb8tWo%NM7vM>b*eldBP9&j0-vDm;y!?r|YVqc~X! z_+O^hl=_mYez-X-{N)=V+vzNB;@)j-&w>eVb&ex;oXx&>+MbJ zDAGRYuDdOH$(a#jr5ArsqC&_{HhbpT@MU}tS6}-g>pek_0?z8DDc;Dp@$Djfr`pp+Pmo4*~XGb!a}KhbPS0>K7t~QU4}6;xz(QHQ62Z$^YdUK zd+%&&+03A5<@{BHeA~tOZ3^jhHia!&T!~Rb zkFoR9&Hu)l$`{&an)4$}hFYtWX84Dg_YFo>6{k%BQMlP9V+h#f0N7w~!gN}XOCV|2 z#Q5CbwR~m|kNV)=G&E7RwQ&hv{P8jvlf@+E5b62ISRrYh(?fTu6%?`dD4 zXJstRrO}0%>Je-+OIoKRJ9(Es(BxiF5UA6gU1PvtZo5A&Pd3s}OiS4>%w#40BL3kj zjnobfVjWYhuuSS22X~0`(s!+uw!lQ!7Me!A#`aS`)jxj#w}axY9@sL`JoC(*(2Sct zKI`_#A*`d7L%D?Wh(kmgw{yO`DzQV2?^Y_+{&j%?_(X~Fl*P~$^ipN$G6@f2dPvRs zQS}G+TVP?L11HwvJp5plSHKToxaLG~`IzK!&?je;sp0iguJWNSt`CKz;P_?|E(7W2 z?q+bWNRdij4sekH9BFeawCOAus-> zLRN=CG&Au<{s#6|1M3L37CWyEJ*Hsarl3swz2MEN0J| zygQgND+5vua$${SyrTwcr_(l?AScKJSn<|#m3Pozq(0cytIaYZ@tg(`r3$u7$tG&z z!a^X2w~(bfmN2(V6guxosIP*eKYHN)pYwy;+U#C;xcXc^H0)9E%y2eIztS3gv&O!M zHon$FBzlb7T_vRQOqxvig{43?!NGG1nmx~!D?O?D4du6aNh&Nmx+Yp~!K{!o#-96Ad=d37cEF8dLUD%^ln`4D;M zSGnE#a&UTz(FnP-ESsWN2cm4>^Ea&6i?CPR5&^0;auyX`&vlg-b{_~P?wLAo6pWL> zwjs?6UE3uBS7yz6yqYzAg7MZSU+ziLhVsU4;A*R8554(ibSANUH7g~TvvqOR|I@1> zdAV(-?!z6cx_mf&YF@QVJJudom##@CA7<2hCzziccKb3j>!%{ijb*(ar z*~Vp?U(EA>faO%o{N&@ipPC{N@10M3ekr%hb&hx88ttG$50Ik(u@6!drK-E$jbodu zi`b-bpOmqUfgCHG^iIqfVj2t${ld0qF<6<$BmlN!QMjuqxDsyHJkYUhE-(}4eN8v# z_3}zj%RBIjvM+^kj@Lo-qX^&vzr!DHXb2D2&`(jjYhNO#3N0|%{8DS{fKUd4Q5W89 z-9aNzh#p{s#9ljJgkm70Umo7;>6I#|0vuoFOmhA*C4%-veqP_-w^HjuU_bt#bqdL{ zqSLBf;9DRzF>60Ms2-4RLps?k^YVHSX@S8^MaLH$z3fK^W}eKvc;t1YV;0cC@S5pC(IdUI7>qKqDzXExddEakewlJMun`0GUAjV+qG zQ^Upb(fS<{3(ju=B?HkQKE689*kXh0o#=wY>b6%LGU@<5vKe4SNbo!#BolWqF=Ual@-B; zA(J+IoUEX_%rP=={hD*-Krgn-w6w`CkbxB#N=Xo*jObCFm@q}@#GE1Fhd)9j7u8`b zX0AV67<?DGyK$ZPCTaA08x z*JU|*GuQ1ma`u^_evtjKgdjBzG<6p$HeMYaLatgsp^|7=We>c@sB`efEof_J=i$UF zpO69wLv^++B*==Q5OOpQRxkSRQIV%%yga2|I;3x_zn(oLshKf?njzEjD)VF(om4u} zEAadty@&0+fTm}!firFNA4X;PeTMT4-}l>F^=^3yMV#egubnz7`USR2?8K1pjz|Hp z!rOGaSa<~&T!Rl~NhdqedWRXrE}CI&pCqBDtc*G`R{3T+QUq04O|#t?t0xgm6Ve;})tO z|7MQ;2}4M~t@;COjW98=%`Dsn@P4eYY6d-wfV+$Uj52cCBGX2IgWV!eMoQDmA)t_W zqHa#eGqVnpMwuX^!CJMVpvk&S+iYWeT=;FbdJ@#%eLEfdF3CdqWGvIAim4XLY;ONg zp&U}#{+V@@MK|o6wZZfTiI_u-Oem9UYQE1Gy!%Fi%`RiyuPFQSD#L=8Q4F?A^DoUT`sWi^T6pUF~-`TDr$YNB`XJxr4b$E zfEO10A@iCYk4V-(*vuBdgqu zy^N!+>QZBODz*txjHrs1=_y%%e!(S7f{9h}O_bF`O6W~`{P6PW5tbtFTa4&*S&IVrgGP9I)zrY^XlQusDn z?Qcub6qv^jkUE^tlQW?;;o!V|w>~ksaffu7H@oL}aaIcY@Hc|e>o>1BCuP3OzE?Kd zp(WU7tD!j~H5T=>bBIM~fy<7w=PTFj6Y&W;6Q$-Iy)=@KLL4GBX=RP1VYcRl;b4E1 zNqzw?OBC)x55`|n{{GDjfcPoNlcY>(+c(pKpk9v?%d=+BVQB(qVA4CR(;BHzZ`$%3F{;`@=|FVs`P`*!thoK{a1LU|Ue0+y$H6 z{hU4{{85|=cctLlPcX6)=?+5nz6tuo=k?(pLIgl_a=U`Ix2=W%ZE_YNdl|9d{1JJb6(D z=zDnG(&Cu!W39pAF2H?e7cIjjU+VT!27N?WdQ1lN*!40%yBHcXP#@v(=x-!ZF>GEg z*kvTdQq?jh|1va>)Y_^V;R(B$5L>I^sjjwFcz(It(c}tPfCTx#k&=$wo)&En*KF~fn)$`KEZk0sgvpY&1Gbz+3Y#yZvOTk)bhdR3#p zqSOl>v0#4ie7~HMXZb5iiS>4i8wc`6IZ#k!Z!}p_YS8+Jh_WS&esJ#F-m3!9;4z(D zMw8iU(RTuyrF)AljEW@KtoXb}T#mR zI^q>7uuv)G+*rn#neecF68M}ae%cgI3HGWDj7gQ1 z{o=s^U*Z!+P;?<(*MKtaa3$Mtj8d1nnz^Y-OmRzGEcQpZK1c4XM|vGZ%g$OR&iR=g z)e`ZeQ#qi!{;NTg@q^tw3QC-*@yasVk0Xe{(RpklS(*5c(zi~i_@G!2FJ z_cee{2`jU>DkICeSv!rfl_xvrSP=6;gEim$Yl|r=cq?M*FUGTx^&oQcPmeTU&r1gN@sZaKC6#+_?Fw{^*mN@6m(hmymKTA|Dq;2Qcp?Xfc6^4WYxYesqZft%4>Af0JZ@WYkJkq-W+y7gt1=Hf;=JJD0LC082O_b{{G}lwY&2 z%(=fE;2bfz!%v3>(mWiOp^JC#yx&wZqS=si6iad@Bxy{{;#Dq@Q2#tLNv_R~P$94NK^cMQhAjwb)80i-b$2h+`$p~yv1zW#RmpGPx=dukc-5Cloyq! zF#iU<{IvX!<#zD6Dam!-SX*0=$vZ`c&g7iLfJL0w`%BC2LAZ9jEzHT2EjkWN_4m`d zoLcfr8K=$Kjy~cAr^8CZ)fCSwX4m3&ZBZQgBmVmp}Nj69>%)bJk!@_nze^Og>8?HSRE4E?!} z=S-q(dencoWe}Uf;4VTh40_ciDb;WKgJ}J?T>hAa^G8gJzt7+Kqi+Khx5;=3gm!B? zb*n%E&$IXu;`O(<0JcWc^x8Y;0lrMBaDcXgIIxq(cW}y&pi}_-kXBNb6vUfdtf|fX zlMFeC$tnY)E;VdQzze_F>t>zxF(V$3czs;&mOdLY+e(PSqDvR?kuNIywQtc~v(oy4 z0$5?&2#>OblLPI}xw}>qAo-u7zeYI>=Q0E205DyC!6IekHay~EFpXbUu3!ga*;--x z-opU6^1l@;{T~Huj#AFDz07`a2kLKm&m~<%fBN1~;xI7Aqq?jZ+1 zvZAb6Iin9}f5BE;*<9#i!|qq;1Oy3%-r0kt36~ zqKRuN2umy_2b$Z>{D&h7gZYIY-g#Nse znl@*uTN>=^FmR~8=qP;+eP^@9`mpzK$x*&XXdc>*NW^Cz`EGi?lFHY%qyikU#I}$NEp&a%-~w7*3ABUr~H- zRbMY&^KsWcseVJ>yn9*&jiCDwHVTkn-iuFORnw$YUn?iNdMYm=ad-BWne9$JTQHCy zld9=3Z()su=+$&@hlm&FmXGV1Me7m1VYHl0b;vG+1`(|_-pm&4-j!Ba3DT~e$@}T; zRt6=NRxX2!l4ER`YgxH&JE++QO*4E zXOK^2M6+bdIAlpgXBbcR#1NSTB$ABGpAKyn`z@NGOb#A_({z&f(NHs|m`4`A^09VU zS+K`}3&*L%CFyp?F^xGI?mnfVh_~U;w-heiUd*#3lZ8oo3%yzaIGsAX+ z5}%E9cF0gPjmHgcdL+{tWLC%DwffUAgRLvrKn*%vd^gi^cG2K)F`LApo^BUJjuz9Y zj5erIZ%6Y<^VwLN8bELr{ZQh$$ml(xr86#fcc4HFDYbjmbIpB)u4#gnYO8=o(MrP< zud<1_`)1tpFAJ-OR28?p`oUPwM&%S9VSy(Ho{M3FKgwEB4cgbyvv=`{T<48}h`BZS zSpqSsQgc4TiaTs84g%;tA2slGMHS{GKHEn;dXOl?puhKQ$&+PjT7gwujs^X2dbL`> zON?O!MsNM5Pk7+SQyC>i`jUC#Ji-)j44?a$m(X>gQ70uacM zkx(+LXy9w)b0iIMOF-zK_~tXRu*fF6xTW-QCHAT#AfmF)$yCFW02 zNB#tMr-r>9iR!{6#k;U!gY~=!HhAT_kR#iJ?~6nhOtJ^$ykq#Fk5#?R+sO{{L9}^A z(Z17PWbUU{V6m7wtv!(q_`)*>QZdS@b(iLTXEc@>uLY}*1H?F%BKh_?vKe_Z$?^_I zsyG`9XJhLA8N5ox*-y_x-R1YopL2;7IMuy)gvHgoY%NL-s>%Ay?+t}^U6)XZObplUg@bl5qPz8)u`_Flu-PC0(;@v$yl5< zy(=ztfweShG~gg25+Y)4+$ZMOCe&p=PtHC|s$in`uO4jQV((-rFApimG82|Oj_mc5 z!<3Op!pS!Agrw!4TLD~c$9RK3M`(9;>ktcRdM4WX5u+#8`Mps{T!0`!y%<^R_PNn8 zKlf^eE0-41jHgGAM;jphq1#MS9)Mk77EuRRm=c;f?&r|3mBuX+8WV*?SzOUC-1@sK+koTGtwK* zY(A1wY-2M~AmBA?tii`4P}Sw(0h{KImYugr&DTqZf}5d$YNE2_Ip z&{YaUi##BkQ+OMTq(fcFpj;2J>$Kkb;bD1-7%eEP?oF{S)LPmRJCQ%0zH}S^;I1et z_)e%>;l4Md;V0{Fm3;_$=|8q-0u{l;pP}<5d8s-L0%eOZiDF8*@of`$*A>lP<=gj@y{2 zy9pX1pWoUDX3VBvd|+X=c+L3%CCNud0+Du8WqP_DqOtkb&4Kmavv&CP4tvG*3hLTj z{c>&ArEAiw?W8lBf?cNaM3yXA^t6P&v(vunlr>PD`)6A`w z-;#HSZM$b?rJjWqFHF7wd3_H9W`u?;=Q1cclXmclp$RqIig+@KtWwv zlA8xyJb10sn?M;a?XPAsUiH1J|3kcmUs*atc0@h=R<`9KCcsnDsT|zH94<4p zq0yC9W$M;ir6_3Qmnc3#BQNzPZ$iVCnmYG~==;4!-RpPDjrmB*sbQ8q@oW5)SeB=M zxW%fd5?N3AJh>{;G>v+l}^BYIyYItV{)+u)8J%& zBDA;m!JJVUuCRoyplrMx#>$q;WwS;>rYpO5e$GjP2(75OnWEjDlDHOwrHIcubTNXd zsnIaZF}7UTBLlMYCR)p|!E+{#Uz)#N@t_7yv&}e%mrh&xXpHda^3M@H5;ojC(6Ek; zksD9=T8LR|3^i7H$LFl37Fe(+$aD!TE~ln$o%reO|HTJ{)^9fF<17s@w+YE^G-Pgq zv@ZX%vG$kT4a51|AsLI=%ZSc^8tgYe23GW6<5U;LcVM8$$ z#5Pc^uCKNVY;X>Q$}0o6;$04Vu(?Xvr!bVUX({T3G`W&j?*4!xG;(rT>N7U)0xHsU zQg_l!Gi!%l1d|+Wk#^(Md_;JEc{lncHr~$pP_nMAKl z(Ls)#w!Y`{BJG!knE?-Ll`prXD+<6qP9ZxFt_NMRZPE@y@8b43OWAi&>7*^RFL}Dp zARsp^d+gwvi8Oi2vBd{gbB3F=%`21V$ud{1yo-is1s z=T277Y;PN_bH&J?^7f&A=Zy-n5j#p6#l8jL^BB9wC)0~tW_=iY!2bfA*HhU)TT;yj z@v;(X(VLgpCQ(N`%C|07ouaB~nCsuM#!NG{3)ko?Qk&v#QQA;RXh_dF?px}rv50j2 zBxDvI)YdvC(K%r)7tr;bh-`3>pI>%lw!u6C(a{7|dq0YY-@JNXN-b}LHf|5cqi+R! z)s4oE!qR0$gakvLI)c3z7Hz^pi(iob?yk6fHa5kvfrr0vl}57snM-P^^fh725mN=e zp%T7ScHP*c6>Pt|7SuZJ92^pcWXj;ruQz+gVfC^bX$RB?^fv4pdF#nKNWf^x*x_01 zTd2g#!SOB@Rx2tL1xyG=ab8;jR@EpmDK`+D#u{}XHO0k)(Fs}qc6bDW(!_q@ac7n< zXei|~Hbm;DXZu;SkJotV(z1LSCaCx0>=Zy9nx0=F_neNL39fHCwT5U5Xq6OjXP2(B zLlQV3Pz-&NRbfwot(c&f2OMc_ITEwn03{`Z($h36I_3werw^V;kPLE-`*2}YxK?4O z3vv?*T>3lm@Whc5v6$$bqVVeoKNIXII&Nq~rZr4jY+TWGpN#<&iXp$-Ay6b`I{(`AG8D$!HN)b>yYsxn|e5;8xAtMScMi;sqwwfP}Bt8@*5h2~G zH|r@Z$jQzyqwDR_JR6*#t=BnQSPxI9j z=RiTlwTkj?Z2&8ZUa1280|zE$y`^fFvwFWg4^CXQm~ib#hC1Xr}#oie}aSVEOo zO}}RE#+pQwW|{C$c<4C^gm!9=Nk#W*otx&B4=G;SlVWCkfU$pAAd%fH?#z4Hpjnf$_a>{)IitVZz>p^H=5}q238S#~<+qJ|`Kj`OJQiwV#R( zFJ|_QPuszkl1R&C#$(C0v`J?kWS*87NM3-beEdrYA?8IKf%2lnWDV;o2F=nF{`%OV zV2~n>u{w{ttFulAX@{Zm6t51j{XuO~35k{?hK)X&-dsInAzcf9R<(i>ZFpnZojINb zO^<}=uaSKr@CTpUsR~$D4cl^z7#qWga*J47)z&`#Hnt!-zufo{=Lb9hU;TS*VW$AKMLkDWMFR-TI5n^tfW1 z_+7M-x7!kWI?ft%g*tVwiwZ%|2M<@{i6E!k{WgZ(2>$Cs>=dJck18LEJ7k=?%<*56 zD168=GjI^u1+Lk^O)X6nWT;H~Lk&S4*?FY;I=szoow~0k?J_IdO--H5dEe~I2r3BS zbRvvuHxXv;>bkoG7shJh*6CSMS8E<@+%U*BL~JW}@O`+UqAH0iDITQr?K6WyKRF_# z71~Fh)5E8CWkF7>2PE_?>{UNEX7qzHUn}`-``qCP44ScDzF106k2M?)pEi83=$Yox!3a)PH2#KRMW$O>e zut=EA;!})h$Ib|nSO;#^K)M7aBU~h=O6n7OK5%HX_Zz*PwJI9#oSabS%dHhz1PITb zWa;KwCFNRO(5#{OTORu0TWuig4w{KG71gtesX-^+a&*{>x8@9=C)r&yc8M*jG*r2NMLBwy zW`lay?n<-fZ;dV)s()fm1y=ou*qLEH4av7+>)}2o8Kd639=NUxnJckI6zMPw;AS-) zH$RT-nZx&={D!49>9<@E6+NwZFFCHXtAGEy+}55-V1nM&Pf;Wb4oVzaM}srtaz#m# zYtN8Yzl29qB&L()W@fYSAn%N?HGv4N zM-`Yf26xM-_=b9XbbR0g^`VhAboLMqzF3m++qVMuw*pZlrKtYFd;Wosdl16kE|+q) zMNOMg(Yi&n3R1l=*xxMolnCe98z&RS^j0e`P4c<)U2YYnn1Q*^0YaT5b%_7FeXsva zW%_@(;s0P||Ej5@9kr>I%~1}U76mS1u4Yz`pv`VG%=R$9J@;`Ew;G%_z*Na_(#|{= z6M3O%x`h0!)HX}J1C3@6m{156~#>HC=ywxc9qw0#Ry6)yqRI} z4hMZtYL2;hdHraSoBxXNyYt9Rh9$*z8)Z#_cWKr|x6A+hgx2kEZ&qGZxQ_5b-;Ync z30e@?ydCC`FS=>1F{u3b9K2&Lzol!PKo2j&QEe%7JcBi+Sc7T~WRUrkJGl*xRff`{ zfn8tJ4YsVTu$PGL{1{04X91s&TBI*Sjiu;+8%`m!;!Yt_vSxUAZspI6qDjU=hb&W3bgYdgkENgzCXvQ@h%TRn{`b;1B9Mx&hrDjs8_EQ zzcYU^GACg48_>t6d{L#?N4QsX)pX*@n)MrT^}yX}zLUp-m1kM)<5x?SA@G?7jwi^* z;}~z_R2L_pj6Sh)lG6LBno=f#5jF4QH@~ls#_waRI{R(tzs-Y{@~;HCUvsxs|66CQ z{5u=X&;G__zb$d{bB&Y|4-7-mDd`Wt^JDh!ivdFZ#$>+{Mdh#KM=MI%rAgjy2*l$8 z;1}hV++|!}$euC2`!=W&>UJH3L0IF!OW&~p+V3&>95wVvZvgz#<+U;P*@s44^^L83 z!y0R=)ONGXI51iT$vTmrFbjuWq+pY7aFU)0e(xb2W=^frFB7yoVR6 zent6hSucU)f;iL9z3$^aRXaGP7T7#uAYy%_@;3W(aJH#ZNNRzJ{?|ca6s8RnlzS*B zgP12K@YtWF;QZ_6?mv6O|8pAr7aRBgln3ki_JTS>F#{TOM2&p-oje4kv3>ln(AsO)|5*qo=9zR28*e=X3Sa z`n2gJd@T}nhcWJ@z^h07n(N!|42Am9Gb6sC%l>ONN{@yDGxOx&8u%fpXkrcpzDbOb zj6a0g?r>sSAC&o2k;a=-0{(f!Q-rYvQs7)Orf+Y8HmjgQ+mvxkS4eMs@yd?O3_#p~ zYtg-xOOqA7t}-q1YDPf^BK<+w6H_W7-=p~8b&+yUwB(QT`N2W{|HEb0&rsJG=P^KW zpAbSzQ3P@TLzeqhR4_~lYa+eQ&0eaor&PeYx#W5^$Z@cBbA7%7S0Oo+BcFBEY{p^1 zOj#2o&ktD5wzKfouYK*RMQQ$>UEiD(R;r~BWP9vx-a{|gf=ZuXFTx14LH^WZR@UCu z@m!rF+C1ah&V6qlp7l&#$K5g5F)KQ;e7g2+Kv3+)ADoH5Tf6^A;r(AIIiC`ES{U}w zC~|rVN(>c40#q9sUXBvh8WisIz$+nK+z6XftPz~NIx_bd<}xK`n_1#iSOdK#MwMCL zP7Az}3&K4jrB4fg9(DLqOG=ynR*7~kUthMPEx4MQR<=X3Cn1<`d!pAS& zgXHw4ev#B)T~hrmla`nY&ck zM9IR*Fhfpcemm;x4zi(Ml%2YK98W0tCXKLFC5J!5`vhQwYZ6l@<2SBoLla1ZBsfpO zD-2YIY?nLOg=Y7$%&$(>DA2epR~1y$>CeEasZ)Ol#s1FDpK8q~fW8%#(@1@@%CY*? z-R%70Kw`3Cr-wF)kI;Ribc;x(AVA+TmNIcLG`4+6!T32nIHgFHhINhhFYD9%8}*f_ zgYDEpM2>f?;_yrE>UAvi+YHPzaEySlRedMI9zp`Y@#)u~wu(qlvb7eVc{6tkp+xkB zP#OV>takNra&9EYClxUX(9l#J)?C$c&4yQmrhPsc;yzJw7*Q0f1)(3`ubp~TRZ3tR zD=CHkzstTu{lBxY>|vdCHk&lr;3Iko8_dR0_vw9RrO#YyNw?|a^B)9nWUgBl#N^U~ z_r@rXa6bs1{2%gv2+*Tx8j>hXBQN~#Sxl#QX%My12z>Ob~0>Ydm(dd4=+MLx<*dlG56y# zeD%`_toJ-*FSYE!W5`t)HlYhmLM)QQ?V@s?634hZ-LXJ4Fve!)+IkQt6}Z#d;=b zDNw^q0XM0sut`Le^@L;7rR^5cC$&rZLf+OWP}7h<97X;7&D>s2+l7G;}41=9W6BG_nQIs@ye zDs(O@ZzsSKM>u_kZ1;D*BlQ?4^LnHHDl9JZ{impR*P69P>&5M^ZAk1Iu_kLG~u`)=Mry&UoR2EG*Xhwa0X^)Qr)$_ zHtpfT@6A||_K7Dw=Gf?_vv;`UP4~B3M#Ew-Vp-_@*@IcCw}Bt$PPnyACBz(dDHhgk zecjJN@5ysVw%zqP&MLe9Rsfrai_o`*lF5{(^~XGKo0R7rU!);dz+m!r z6;Yn)<;9*0O*?@9f%+~qxsiC8u$pHR4PBGsEH|%~nglJ>@(e@p4v(b8U3*kLuSdR;#?Ne=ecs0hkc*BrRj%><8%g2TFttU?#i&?LwtN}IkV+iLPXvE_< zKChdXxo4rVDk^IF8V>M2CGPR)(yUH$c~X5$>W_BM;7REev=;0Sr1#2Lj@;mpg}69& z++Ri;_QGc0Tfz}q!>TyDvy0Hx$hKEt>Rmb1#Y+x~Y)PB6iC3_?_;1dp-C9i8YsAcz z?){WfAXtz5mWu&ha?(MSBGwp{Nz%(?=>f}$>!H!-L+)2S4W3VK&aU=~k-`JtkRFj! z^HzP6UI%@XHboZ~XQxxz=m2MOWTRIpXYEY7@j+0ZmfD2s2z*>&d2_v1wBqekffNga zjGfc3k}ci1(&XFkJoGm}m0gCgMs!#M@0@8hX~D?InR-XR*{;Dba=OY!<<&h7{`h%5 z-kb*NUbM84=0Y-`Zv8Rbb)kJ;WRXb#Naw0J6=NmTU&+O65K!7fT`N(MfcnSaPk=2%@ z)}IjJk_$hTRrz|aG_3i(rS@im`U|AgQwB*t&Ja{M-`ze7w|ea27Hgx^uakzV@Z zd-bV`P|*as~@(&kZt1b;$sX$AiYK8%udCas); zyatIJB-KJ7)PF{m;HX36?<$Va)NpXzG%xMxB(dvmxD-M}NEqTm(U|-#YcMM32 zi{w>aKwhE=(!*DGo_(nDd_@Rv-I2a=t5vg^3CySQCSfNbVGR03%e8RqNUM_ic8?2Y zX$y=nva+Xw*KQAP4_BsUAkXQMT$vd~Qdb?l^w~Luxz#gkS@GKnx*Dc9nb?J&!o;f{ z8T1c@KI4r2NaHdYeNi{lt>y2}OVVj=?9z!W;WF6Hw|a#ELGITdcE6A04})a*?B5XM zYNT-w+2Y9byvR*VWA#;_eKea1oIQg38x+8eP|RDi9vmS64tLU&b2PnQ@~o@hnoU_`Gc` z|JBYbtC?-OgX5#wl&NnIlT^xz)FDt+>UXOc={P6fDnR6$$Z><0$!*%fiW@1hBU(YV zbUsaN(K+>Xee2I3aV@kQHyHD#2MljiWP4=2grzloHnDZ()-JfKr^YhL@6mq&PQ=Ku zw7ujZOdsMVZCBSC5_O?elaH{&CjI=BF!INkUh!IW-vC2}jFe@Mo$Ib9XQk^_j+>kV z3QVS=Wx&EPddJqM;9(YrgNtO=NDb4uVFdlRsm!$QBl?vJdrmz}oRy>YQ-bS*_AFVfh;Jrjb{4)GhIHKD$k2E!wu|!X_{RxA$geoFlbVqq;%N{9es#Qm9!XjrR=Pvpv7X>{%MtC$p?hy-8&Gt6(`ofJh#afISu1Y zKP)Jmdi(<=t?9=*denLBy5nKgA(^0?XZjmGclGu4gL@P!SF>5Sm{VXEulaf=E!1c8y6j%SZ_|6%$8SU!uLJVr zc#aVz9$ZM`KhGU$E6a4v4tkVn4?K0hNU_YE#m6gmcQE*TNA^Euq74bYCw&yYsoU3J zm>dF`lw!+U&MZeZ7CV{<)p*^Q$CSP2Zukj-IEELVxn*Z_wes(I+YR-a@1cw8?R;Nj zJ-TpGwv+iZ|C;|(!i%(??F}p7Y?YM?DMJ8=6V0RFl}v@F9BJW1sgYLnFn7Fm_Vv7* z0rHGNx9uZUezk;lNXH9ad9X!weQ8P3C&6SgMid89FlrNTV{IhlfCtI@cD4yJGQzrpz?KrjB>W7F|8Byzp(-aEX{5X`4etQ0_7n zf!D7nbHbfOCp@422X}8773a3Bjgk-o36hZD9yEB6#w7%IcMHL38f#n=65OqEcMlCT z&{%MHcXxMBzFvFpv)9_+xo7P)&bjygIAipWcZ|2kob}FGQ>vb-dblkI7V5pu5E?TJ zOdcyOe)`u`=IuXKCMp${j?i+Jfw<`dhfy=a^d_sWaXtxkgqGt|BX$%asUB1@9)y<9 zp1*zZSJtp$6s|PwV$vn6e`*`QeDlal!rWxWCY{xswliT5Kxlz=MDZ0L-=gbRJq*w2 zmrr{vH&4!-cI5tw0QBd|sT+N_tln!JFqN-I{K1R2UX!=%zmWEA9r?TJgsv=`eJ@iM zmTES)ZX-C-b?pq=th*Ov1fRdWy6kCL>Z^&4dja>5)} zm0G?2-M7u)?9#13z>lt;H%QiyG+T0RgV?{+f4Vr~qTX+}6%KyWw?b#NrAPS-w%M#d zJFi3=JYtTVldoA3+HvP?<5O_Pd(o^Ioc@>g_H=QUoMc6Le*89rlfSMU3}+RC>?#`u z+oLCxQ@SRQhVDBj{nQHh^A8V~*3lgOSAaANa5a=miMkY#NvD$e-++() z_v3L!^G%uQDJ3ZB4!8c^4&U+VM{$uu8-VS#ghsG+w=kMfcmB9RMqSJ&Oi?3(2jF_i zfJXR740~$ppC8KiG)~UGsi%B`t>I~{XW($nook}UIw5W%mQP+wT)qS3+X@y83uI^R zzQpFWjf=oI!Dyyx-Q8xLsCMmurETd7;tH^S=}*!!Mi52hT7p{&!rEUVJ-MLEs*vrR zvyO-FImJBfJA&=Wdph^UZBB^doL9vuY-~nU9*D~=r1QHiV6X$~`T2o$Zl^D(Sm?Sw zLXs*Ox{AwjFA15ip7)q1W?5B&eVqjyT1I@6QbUiI-W^lKGf#yt&+D#SbY7UYk_M_x z+s4nZf@<}x7(6DL8w7;%SoTOftA*^`jO*L^wjTXBb^GC==b2TaQ6G28kV4FGMNHf% z{t0fFH2YdyUghlNlgdVy_;-YBbIH#phZOug7Vk4EiUmziPCm^r_jZI#JZC%<5l7)K zU>-vnqi}T`D2Nmz8>7nfm#8r}2if7s{z8folGk_chV~Lqa8B_x%#uLDC$wG4u881a zVc(o_V@u~hyx*R`?4Di}O(yI6%>2ql;f1|I(m;yc7Ey60dGMCggnC4shWcRdohs1B zU!vFf$M&wHY`=};h(!bHW8!i2(z`GizYvfN+qMI$=bKwqr9M&3WhJ$1l#yQ)HzLYZ zdhle>k_j9X%*(68o0BKaX+j1r4n0~k^PK_6 zuy$!Oq_p6e4h2-RI`zX&yK6QNu(gts>g=h#uC};$H9PliX1X*mQ5z@98ZGTQfn*De z>d3f-yPWz9iC|Uy7ZP*HiX=w)QN8JFgF^P_y;sl!(I+XN0byKMH~Sm*ND3o0K+ws1 zMIFza5*KBg5|OIy`osRE0; z=k5W56+wiCs)|%5^9pmk&Pxh$QpSo>$2;77-{`stKN8&(?lU?U__D0#4lXyTyw~^VrY?8ax zOoufGcatdri~cv2={+kphFf^y4$BmwMIt|Kt|t{lt8k0L_u;|}PQl??*_p!nm2*<2 z?)n#6??LUPI^Blioh@*)qx-+QI@4V-pUs zt@dX`*5BgsAc-VTFJ&H2S+3#CJXLh*V*81|F-2glX=s0lFSQPnl*L#n@d(c?u^t5K zQb{uhrjtH)e_L-Gs~^0giwgW|>ZoCr;;(1Gcpz#aPgrg;^o_G3PN4wPF_anW-4`f# zhI27Y;n~u@fMWLq27Fu&>FKT_jbQ>YB_ zq4msa16OWvP9@3g#nl694R=B6Q|>#U*CXTOGMj?2>jWz$LD8se`yqOFGyE@*933br zo|_3T&iz7?QtJK3!+Wp4LEFz?V3+WsEHSn)mYhfT$y(PpxnHrZWbuTWJYJTXUdjL& z#u+mWtajMCP9({KcS)nB;C|3&$YRv*>J!4qY+qmn4^|~Zv=%JeCJi*9wPNCl@BdqX z`hSi#eIC+I#$hDzuEdm97ohi8hIdQW=HGG^dX%jQ(TTTIIPdw!qmO}#97@tN>b(nZ;o~GSve1;C}qa9pUytVMi z-XzV5ez^c{IDZmG${CgsOf&kbcTEP{?;ONzeWeMi_U3{mVWdopiEqXK0eAjyJ~HAE zHmKi=z?VPFUrGZrd~3%OAH90^jHCg+Q|fx4Jub*aZ<5oq2S%e$o6BuY;*;KB=LwK# zgN5sFd)wvVB6zf8t4e?TSNQXPp4`7I=5w2WSJJce*!d^K7*=S`Q3dgzs( z(WNyqF3hNQkH!=>hLoRY4Fxy6d*xm~wwgEDw2V?r4Yf0WRkd-f-sYBDQymvIQ42Tg z=|2poPECIyGtDSqt!EFQqBpZr?A$7QxJxpk&JDx;ISo-n^BiOt$;)d(~*-Y1cviFlG?DT1a!s~O# zbmruzpzD_}yYoDF5LXgJKx>LmPtEfC|Bo8@DRKX-d+3JvnBvV37K%quwLVG4_ZOHH zKuM)x!H|pJuHD|RkY)@EKIbf!rOqN;0`RXH73JS6Ajrb4Uu-=9X0N}P*KZJ-A=WYwx_hn^@$X}u+-q1AquwuA%P zStL8A{^oOY_~Zo=wZ%D0(@Gh(WLpF6RM4nH-FW0zWk$)P%KDOsiwk?crO(6XEc4<- zjyn?eaYxXrJB6gGH8ct7#f?;KeC+=JS4w}0iJ_*7iwn{S#5sYxp1l0tLk;Btgjx`@ zP@f}V)ib6;Q`$&kdehNs+hXVRZ34(k!nNo03H`za%agWH9eh0p15UlghgCXo#T0#A z=cpYxEB5W?^hP;F$ReB56PrQ*PTqFR+Av9`wy@ z>(~|Y1uHC4KWOVeh_9gyxDL*iyDg356MId@#ZAI@-Fq#$dpKXgB={vI6)Y*XeFa0_ zNE%NvIz@>!03bGpP)VbMHN<)bBB+mLWpKUct2a-8D%3%7Cc?a z#lno*BqXI7bT8yUPqqPfQ6X7lVf#B&MPQG4DLbylp=eAy&s*1QnfieG-wjsgyYG(H z&&4e?LO6JUDJu4iPoP_loa`el?s>RuEsS5&38=$f@sXpLDJA?_aGmO_9lkL~+)NkX z5pZXp4y%ZPO+6LShkW(rH!-c`f1P_=a-LNt2+`zP!o(9z{qlIsuNod)X&vR$n+St2X{G7c*>RLMqaSZfvS{SU;IKsOQTOOA3Sa1 zm%BQtjP_KRIs+txL~zzM>k0)F=QQ8j-&b4{c;$Iq&-4h<0dJC=6OY!+sjpMZH-T(8 zX7Sv6#>Kf8fA*)>zqLiG&srR@5%Ht#fMcn1Q2K=5uz##-$cLDhuKmD=Lpv}rci?t< zcgG8nZ|41!+%oMamxE%^>`$}Q-#hLX8(^`W?9!CoUAr+pes}jzc{zV?bMh0dCDh*T1fol-J$ zTmvcy&q4i#1V|NWZ>YhEDjUEAEcU(_vr+p_U`0>-3rS$)u`U&=iou}D$W&xl)NwP( zWMgiG1z3+$g!l;~{UFN@G^TC12s-$DA&72IYOX3S_F|Yl4`L=u5{6|wy)61=7mcc& z)v2JC4(#VYt;|R7QB;YZ)sOd~CIcVu!_aN8GTmQ7TnowRg0hY;a;59% zE*k@ODH#*~by@viN0kjGQ7}BSr;@~W1I`yX5 zNe@#0r*4#=iI3x#iGj6L>T7Tk5IJP$CKpVKWySle(;3i$xK3ii3Y0mwHG_v<0l)jic zJy);wu%@{Uz5Jm_@mv+Yh*;Gu&6kD3BiaR(Lz;UuH@PRQ5Fx7z)-Qewwm$y&hIeY| z+xbq7KMC}G4pe-yDwR1VZhO2fJteemipQIt;{X0Cb@J-k$mthSwe-*60kMBHMrgcQ zu5wMF|A%)jx=Sm5Q$Hu@ZW#fu1r1B$H{;t)Ba#|rmyLdZMgM#6CF61z-cZ9NL+t;i zVAUGp;s&1C75KswrCyS?xE*mU!Rb{Wv2LRZ>YTR7Un;6j*w7R?utG&&{Z0)*VYwhnuVSiEPEdJ@8G|XW^;Za ziSw5p9k=fNLb8f^z)cEDE1#4-lU-%+8ZgK`epmndJDlGe*RY~>Z`NiVU=8de2e`-o z_%G)yareQb8Bu70$#Y`Y5E=as^QrJh3Cyz0i!Y+@N3TQwC*n;_Uf38qP`i_3(vvNS zz4!?Pye|2dW-ZudQ}Z-LBLhwIqFO@B{BpmP(Y=>yRb;v6{WAH0xn0WE-J?U+ipEf_ zDvx3+P!-1(M49LpVRQ_Z8bN=J1Rnwrmrke>(qu);m(YpQfoFIBKxxO2cvW z&*7eqP|Mqi+9PlXt^}L*S|(Ub{E$TC&t1-HjEim#CB=}XKxpFkA8RC24E~7(23RT6 z320j*)2!5-*u(r~d9{8Hg|JV6(Gj`rBD(#8)u?85;;pC()QDb;Wb#CnLd%a?dBNcj z+Aa(x?9R?>K!ycMS70I2f}VbDWjwZe$9m8?5R95;zU9Idvr3hljY*zt_xcx7qQX_Y zH`&5ZX3;aJ%aF98-H8bDSf0ni z>~ZvCCs}j*p!(ZBiJaxb)PaU6)X$r#zD;qO0d?q54ENwBdDNjN4%oXa7Qzqeh3?AH z(JFe1;{3%Kfx*)M{mbu!0BMsMzTR1|nl_EPV-?=KFAp z!@uG3z&8_8N%iO@B+brW(YZQWbXd;Hlo(0#GA&sV7r7#qbYmKYDQf4pRUh=9hb$GZ>5@k0*t?Nk{mgmQ4PN{8A+(b=M zhUgBpIIWGC82I|(VSOSGPb-g=RZN3Jv5?iQ&~ac$?9RWA!QZP64>fT8WT~(nuM(bS zbHw>uaYh!em6{W$5bxZ~pDiIFxEqqq=RyIQLRRH@!Z&!FVtQL;|2*2o`w&FLYJD7` zo@RtTuU&&2;VMlhoR^7gZ|SXHbtww;hlQ1!Rv?(ELis?-E<6-pvnCk0H91nnpS^9O zQ_b4sy)Sp@wZUEUlrLH_6Gn#4qUtkq#hZ@yX)7y|yg3DY7ZssCm4>2Wi&KLllB9FK z>gDtKFu8LZYtCmaeU@hiqzB;WeF%!t`(~S3jTPx4X+`%G@6xB~@xjrqMC^K$;XnaacHU$amP1hCXcp^V`r~{Fb*7}YzJH}l z7}c$YdgeHk43P`)R2tPenep5gb7fUbO^%GI4DjSa2s||YhWiF9aBNEYO4i+a!QTrUq;2tw`;pkodET_1>02>!t_O~{w{()~+ z!gzIUWz6_-liJCq4FcJ%2Ry~hE*Rk#WAg(bWyKv&YM-P-q*AIvSw%uFK-WwXmkN`qNUpSIE` zQkfa25v))Pd`(U*i;s`&(qx>8c$T-io_d!!`nIk&xmV^vB;JHa)V|K-P3iKDP=@JNcuI?ov8mhf7VRc~)=MS2$)kTp|b%rllYd&X4 zKJkYv>05_NQyekpxk2!&f51eDr8X^?P>o8W>00T}9`+qo7+g?Yqhvb{3Dz2HWml1rHN(dKX9^42Azp3h87tB+ zq&pq*!&~u&|=3#QwC>UtC8xl&6>fb;bN^ z_Eg4MM9JRL?rp^Wd_E2y_#)Ng!BF`jIi8E-#wvaGp)=OoK`7QWi}D6}>L1P2nPu&D zz{wfW12D+D=6kKpmi)LmaQqjHfa}T4d>k^Sc!x%hjNRP5xXbS3OW!C8UQCzd#_pQv zwb-2g=x0RuXinVA{j?Su%p%Ndx$4g7O^OO_srtH1F&i&l_3CxPcnXaQJigwfPy5#^ zAvV_fg_9zZ2<+wWrPd96>48oan(O*w^&*NY*9`PJo1;BL6l}Gfs{~|11rp`U6NXp= zBL*;m!8-B&nFvTnIq7N|O`C@esW>BhGIP-#iu~4_;2|10+)8K9*blCEjdCNL0ep1h zAGT~EY*b&+7qk1g7HrnMgwa{F!d~xBI9)LUZ6SMO#ro39;tDruvoJ};1tV}WPCx}n zcb8yMQX0ea7JK=^r3O7_QZjK=st!>Ac>~aCHTcL>z^e0_+Y&bW^bMD4sJ<0cVjL>f zLQ_(b`;uMCeBX(~ICKD;-#E``H_Ni21gggcYr@#=(zI`s4Ehv@11Vz6<=-x5sj&*m z@QZ2~5#n}BSd@nYJl-CduG zM2!?3X|TarQIy>QD+JGGAK7n0v+uqNzn+7&M&YfChZp~5ba?wzn>w-9rh}LjgWQ|k zFW3k)=Zp z)w=V>(u(LldLRMmTnqsg4Fa&2uU->U(ROBb?6xC5pmA1CR5itu6Pi(UXjJZ!RyI_% zHdPyS3D&j73dZ}wIKTF!4E%ZB82KA00;MpX`zWA24aQwDAywBFVxX>Fj`ptJX^C$6 z0hUgemb9g<9w80k(l*2-ZYm2^Q5jcLgGqRCRCx)owf~Si`nF9(YrkRMKDh`EDNX8= zmGqmrmX%IbO45zBTbRDhxMKT-q>x?kwF=89BC2Jiy2LjRJ$F zdM~qv=N1lQ0v5MdTm_x-hT;rq8v=_z7MGZ?coY^AD`=~9i_6h|F!gJ%TZyWVeOz^8 zsLy!T&4la892`%Y99XT#eWhoJzKaQd<5;0EYp_pbi2de6@tt`wU-7c=Nqx=U^YVj~My#Np;N!lf*Xj6Yf_w7= zu>$zJ_hjf7ULu=+YCwyjhz9%Ue7%UP+4x>G3$<6=Q}Tww?qRsPdbmH`Zt)va2+~sy z4GR#URp1|7pyJ&K%Dyjy3XUyDQLT7)E z4kb9}fI6;x)6Q6u9((qnNgGzj)}YGRxICsbY@$W8#8a?uL)w5Ke}Yq3&Mx*e7Wx23 z?#!x}T`F9jhQJ;8MDP)DPB6e8c}COZ3Hi6E8GVZ%g_Q+aHA@2?MN|dJQ2)p?Rq>f_ z109kh-cq{!cfn32O-^NsAuhkTZ7yngQi(a>?bvX!L4Uj7F`bzQnp{{T@0$*4F} zL-?bLZ8$fVq|^Z2;Jig?%BbMOI{rV2%ovq;5 zvB*PJo)^ZsmdLTb(#$~Wz#ghxYzrpF-ZG4#h?+sPj$qyGRFhh@x!mY{ig4mgN2MTQ zCFaxiHX4C6SKu#3MVKp<5rzBTIe9g%`91qt z>b7tSpfW`}7R+@|>~x5gS|=qHjGhDrA%iy0FLpy}a*Q=_4|KG1E%s*Ov|Sik)*_G@ z9J1Y&d&`;m!Th%iGai3dCKhP;?=R44Tr50EQL&W(x;5~W_rt)uXqC3HSmQWO1V8D* z+!Haqu75=i{!yy@Uv9%0`M;He1s`EHhm`1EFu%CO(9l7z{M& zPCBCAr}Z>`*;&e)W@N$x^35m6t<~Kv)YVi66&Mq9a`4HxR1RWSopnwxj!ope-K&X; zcv0qhz{Mt?v_D(PYNqvCdp&LJ? z39$6Fos*X|5M^__iGkcn=pc0t%R`R2`7wEc)SMR{CXd2^_1p+rT4GBotCJ3c*K<6j zvLC~1Xd0)BPKx`v=bS(}W6mQJVqF#AV0nbuU?(a#p6ll-3Hq*Z)|CdKN|9Lck4>8T zb{@>L18k!rd|RzSp&#?@h_eQ!?|j>iNM%al@E_w8rbw*0pwl zN!VG?0+3}`znIZ-*NP!kt&LRd6;z89`JQq%n6P_BLnAKV%l)Zp5lF z*Xz*Re1bCCfj_2YgYc@)1t=66riwtuY2z7?;}0Ma@x zZwRvb1OkZwO$!Pzz=l-1g)iQExCH{;k{bh0!kA^IYS8cbhnReNVxCn@+qjx4$U_!R z81D4dlah(c>Ns^v&s7_TJnf(aYvAt94U-tSHo@fQDNb8&^(MJV-9c>oR(2b@lgBr> zz|zj=vwLA|NsDg{dg$*O>CV&|fE@j+{=+WnJ#!$7;T=v$*qiKhp*Jz#&VwYqiw-#rj4*Np#33!4ook!>K z=<`zo&P96$w{^Rej`1>mp+<24ToVc2wD%}kLIYvLk1{hjfi5lWA)L+)>?^AAE7yxI zNg_76;YU$TYaW>yrj4gV_&y*+u}i?~F$f1FbI91l-Z)_u*+t`7sHoqfEx`kpo1t?i zo2vTQEK3bx8DMhJd!)>*umBCZzqMzi)1#vo?ow_zR-a7+x>?Vf=ud>_XReBZsY5$n{kx@75 z6{Qst;WqpGCHC(FZCVVL*73z&^5k&RFfkT?*A4bDA$(N8A|-8IZYeYKn~!Tv;+`y> z6*Fu}0wGLE5IM@qN33aOiKFOF;$ksP@_|Sk0W!-ZA~v7rq-Xda@KU`LOdTYnvMbA} zL+JuFfCh}-gB+@%!%DN;3`~NTNmkD{hj|E0z;yXh9iW=i+b>(DrLHQ0SSBb-w&CI@!)hhCaz3nW-ciHQ{tV}ZrL-KiE zB}SI5<5{viiaH*^&8w`bDNSK2Esf8AT=f)e;o9Fa3~JG|PrkzD5me=p_kk`|9FokJ zpX0nzGMgbjiU&G6sLJ#(bSRY+VYlCAOSKT^MLwG4hX|Qg=}V#)y5L&z!kF=VsQdXQ z{1N5X4a_|pdhHTDy@h>gbczOZ|BUp=nkbF?q-e;eWJTY@ zt~azS|4pS1$R)5eF0^KBY;t5s?P^@*DnObkiYwX-ODAydji>pJJH<>VbVBwj6F-1* z7Gav=Sp@?dk7#?2c#+wQFyOE|ZNS@-TJ@3dUGg|tZwb*Uj7fW`{COVeJO{y{PZ?Y1 zef$iRAh%5J&=qAe@xx1LHK2)cAA{8=q8E-v!~m`_QI{;iTNevvttI?1(7j7gfjWxz9p(^86vX zY+6ihT(H+LPkCa2+80lVJC!__FB_34&H)_X4n!a<5{m9l!qfdlwKISLkAY;%Q_vJP<7c;yZdgx50D|Lx8UvW`B%0W}+84(ed!PA&|K&UB`X+p(qAzHPQx zO|!o!EAQz7>v+tR>e$$*3S3+7ItxPA$9Ucu);m-Lj^Jjc-%PyDO*lbk`EJ6yN!PBs zkMV=?FrJtdoD2;iVJr2)?e^V}Tb%`$E3he8v>i+&Dx0 z|Edon>2(FB%4{$pB&}!kUz1f+~fv52vtm@M$~u*u>t;)-+ADJ9KidtN}-teHyCkP9Ymr$4zS;O|;PcJMrE&a0Bc>o*6Xh3peZId`(!G)}27Iz*g1& zO=47M_|fP?qZ^Uk?S+Wo_f5AjQf6m#)E8xJV?*LUA3%QP*wSyj7(?hRBkPkWJENb@ zN{UzV08O3Q#GN~d(}!Y3tZU(XM%qeNz|zPjo^=qZyy&`TOk?K#K(xM;akS8U9_|1aU(lk7@UyP1lbW3I$D|y-*F1c|WX_^Y z$~`k2so^B^Gi+|m$3KbS?g%apEO#3U;lO>;RV+itl-4%vTg_uHo||UPUr3!buNW@I zHWj2!(E=-({_YW0KPJCkn7i=Pyd2c<)BNGH=3+aEYdFX2PWt+OYbCykPKb5WW2!Fs ztt;!qHNxz@{o4O$9=5>1=K(lBt5NqDZTpPWt4&tZCo>*QK{wV6uv*6ZvvXDt9yVd# z`DUfS5RRAsap9(9M&6*(cJaaH(Ty0HI}54C9MtV8ty`G76b(sIk+kYy>q&`u@2*^4!#FS@-XBC#2gomr56?uq~ zU9v?FQ4x@4EQU=eM05{5gU+SPio}_4J1ZOGTWbt~n);r=cKH}qNw#Gv{OJuQ8?zPI z6L}Rv1{3@ItcGd>TF^O62TS83*QO+wXrW35hY@v)mNel!8(EoY4}b>IY;)Yfuad~-j+llujN2lZGN+Mtg2P-QkH5)T4FmJ*zOqyG>NpR zJC!;jM#XA~JaymN(KMqnaT0Zg9~D&@}l zbB}1<7dj0%mdeKG*BjUIHM92n5)(0c1;snN44Xv|_u#-H`ph)mHS3RDi5t;y?h7jG zy-{0Q`OSeg?H^MbpN7{?PkaLN;C#0s-=^XhD3Y=Y`lr~XX!D~{mEh@dv(}{35`#*c z)n#*GDog92HhvoDkVO~kjbK*NRLImS2N%Rj^w1B*c!3&`A*ZlB!>>t*oVov55YV$( z+>BRNBmJhmLYCCa6w^jAa^%O(_QpDdL5sI&*K;L{XuEKMaOxC(Ucka?PZtQ2OT13U zOb%~R2X*X0;XCEAPPo`=;^=cEjup=AT{CruEG)6{r$|e=^~t%fJ)hIDn9+>&h$7dj zw1`N?Y&a)r+wTf!T;ZbV^P}9($nk#iFM;Fp=EruA-h$ElOzbwNSXsnfqPsBnD4SKq zwcWGxyICi#eYNQG{3^xZxq)&qxM+7lELJtvhzrvkt3R{8{$KCE>0MIe7VlXqc;Y^Zr9e%4VWprJsO) zzwP)?$Gc*u&OCHl5pCO~HA7X-lO}De9YdT-G;L(3nu}5dM7)8BV;;sE)*lzL67o9~WbpoQ{zOdYe-Lu~zaSnSE=K z*au&$W><%Np8)>+x5~@$LdSF0@Q!Ac;a^A}J34|-vzKnkMTgS?nq%bmAw&qizW+cs z0v`wVu?hI_cI{SJ=PfOPoElOaNQ>9D);$cCc>T(Vz(nrkl7+HaFGsEVOHLT#7clV7 ztgd>jzK8fRZZFu?jd5kA;mL0DCvi&?>&fc9N12@=#v-;XpLVrvc2FsfIy%`P!5a--oKXKM(`XNSBy+gS%c4VMTz%IkC}Slx&5 z?X1vG`o^Y}&bR>DnjSjB%YEnB%B#CfOFJkFvVvRZO(Xl1Ilh8Q!6ei0W)DOr#<%pQ z$HSsu3|zNRYmLIjRnP%hlA1a;iVeZR$+_yLMkNr`_hxC9xt^6gty>a{6N8U3p6
      hec!H{4mW9*=p7)zcUt& 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/system-flow-diagram.jpg b/docs/diagrams/system-flow-diagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..acdba43a817f821da2ec06a90860b490fec870e8 GIT binary patch literal 134834 zcmeFY2UJ_hvM4IsaYBy0f! zB!WdWB6&>CCJ2y7CP#tEU<`bG&wuNl`|iE}o%{a(YyI`tt-bc%wY#gks;jGe_wK5F zI(qsA@Uy;-o(|yLIRN0?*#S76IX9}OrDgZn)L2K)5c2nocEB0@^eX^>@$mC8)qVJj zxrOB~m%seI#E-lu4!&ML?*9p#)w?(RqjmsbQ0hOS`M*lO>geR_a7OU;>=f}iV}2$q z#~IGy@^}2+58VFmxY`dqz|YI?jOOtV+y`!|b%r~h;rCqr0k{7L+`-G|2mPxv8Z`{s z{|B!h@I&J3P96x@+5O7d$q(=am;!VG4}Y}(?D-5l2>`(TT>#+1>witNPXhp|UjP6+ zjDJlNc?$sC`V#=C>igHUf7OZC6Q3vlQr%By*YnQK0Kis00C2+s0AT+N09>>Bm%Ovf ze^v$C<@y~4rCB_wM$#I5we z1o6Zhn~RTk#>%{Ezs=r2c`cBJwb}yo5uyNd#HDP~X`mUc-2>is`=Zqhv{OctyIeB=(Ujx%K3J+mro5EU`KZ)4;K2tnp z0Ipv+PI`a3Zm*l6_4ocO6vUn~}Jx=XXsx-tsc{lyA$5`8}t8d;^b)5D1^^Vdr`G4v!=~a0loxX6;>t^GN zOW++bKoN3NZeBG(6vUgxXoyl z?m;!Mxo#NY#=p!PeZEXrxo1+`-`BhzmR~Q+t_s1Qn{KcvEaI|yu}4YJz)u)RSr$A>P5KhSDL7 zMWC4Fsrhy0+TDiGT)>Y}e&3?i6Sv$T?%V6@-mOR$z+WsZA}90D#Y7F}SmG9y5hkp~ z5ClS78v&E~qB*a-)`efF;IY(785Qu!l5kr`Mu)QY`&!pY>S{>Lg|K!OL@vtmW~|Sn zJn{+&B*NxJ#tr}uxT^@Y|L6lXZdks7Td%AUl!tI)Chz6gB6#%+yJGrmqlE~YUe#fV zTU9pk&O(mRzx@zR-o-U`lB!A1R(Wn*Ugs%W`eDdwrbf{UA|Uxb*HoRUjYuIVd3*9z z8wpVJVJ*3h^DO;gj@1hRk`6pQZuWnz$vYxBN(s3RdBo_gj9V8C4s!S7$0au3{k=b= zhX4_->!ex61aZ&iam&4Rg%t9dX9}M0Pju|N$PNMC%_^n(f?7&wuWbgqTsc>U{%_|1 zS8mq0YskFi#rV)9JD)9Epeh@#EJV=HW_M+$WJ>jmGW8fciRFfMqm0Avt?q5%oqhC( z!w^D+8GXA}gwrS2_=7bjX@Zf)ec`ZMU;h$RRop+l zW$*6s*VgxjL}_wt`n5It(d3;Wlg8%}M3Q!}$YY?oK(~}e$K?Kfi-T_ejb@5=Sdz25$? zvK@nR{v_<$$-H+-{Rl-YqQ@m4y_@+0VdVQ^vVGQhsUXu6g6;YgOCJkYRmS%88x59? zSb6cTCSP2cY)N-lU&wlmDB>gdrgeqPhA6=y)4?_5YBr^)+Odbl0wA<>{W)`27gsg; zii?U+M8ud39Ni)VGbfuaUN(UwEvfQwa$qVC$TYU)n9(jQXZDPYzFc{8Qxs4zQ3tTpqFq;#+Y)F-C6q_J?y%4 z)+0-_rx8<5@D(W`^tCpdTCL@Mms+W+#k4A>olR&F<|4kwIcEdOlKhYid&aZxj(|mR z24)VeL}t|Vpf>$pRytoOO1=$_?Rz!9Ct1q=0tMPM#EmjosLe&XvTIQTAWi`)SSc6? ztd4%cPqX+;vfmq({@VjTq=naU(BL<9$S2b(;I%^M{Dc&$VjDtF&Dcys;?0OM?+zS7 z=db6dO%KpzG6LeJ=5G|n;WCfZDZaReivEd+QmzcD;vFTB- zHop+Kp%`MTfYUwYHPQ-JTfsW)1-%mQ5?b_y$FH8?UK@6bnGE_M{8LJNz$Ex z>VgtZ)f!`Alok`Lr *K#%zmqL|5$pLi)=ux=PD0;@HBa ztq9rf)J7@>`#hKsT*;?QOzHqZ?0*z_FvqY2fB5K!^~SmOdWK7_g+f53hwt+z+(CCl zM!RmSYlFHyuNV8~kY1F5EhC#}V|u5xb(K&2YhaYvb5GVen+dyo$Rxt?6?L&h1(KV7jv@awen@q9@!ki< zvPVkDi$2_-l;meJop+ERq`u4-ct@nJt-h;!qC{I-v8MYJ0JNE3^Qr6^guvl8$=rcm zET*-$kM1r-ym6f>(|vv%vr@dP!S+>Csd3-jhWi!>KZ)e#I@)aXRI*c+_|B(!Qs^F{ zf3SQCK(`_VzREmr6y6FKm{hJ07vBncx-rjsKdC!-(3$(Wg@0IhM_;1zDv6Gp^Fp0B zfdpDHkzujffsGvWgTu&P@*IIl3+BRWQ(N9z9?|y}VT@ZU-7uK5zGMPDs6^L_e_!rW z{m@X6P8&{@HYu1&1fHolO9w};AqxwR7>e}LbZ3_j7F z_hs)}kYpm)n%1&2+%7rHwmlESxwDnl2Fhnm%j{?v-vQTU?+AuvAKGzS8HsXlVjyo; zO?Mk%l`kzT%?6FVncTX%FU<5DS_^hkqMUQ{@!u^k(pWO8FnEz7A$Lu%U3;E7!P;zxh0CdER(A; z&~-a8*-2S5zz^2NK5L|jswKQP;rQ9QxnhwO__?|2gL%v1pf-D7NldLS=(f(HaDT5) zkBKTis;$Vv})=hUE34hBg z1V1QZ(M*^+TQhx_5a*+eU*9On?%c%XL?9 z`>HSfeZE%mFA5c=$<+sS@041p{`UI^NN1=IdJ!CvFq=ZTQ~B&grCggxd(lc8sXH=m zg0mpHX&!{AEx*3zpWKsePhV)<`x|v7=?yP|lUih=X#g=l_2u?+k#oI>5)G^frVC!Hm|A>}>kl5FnE`Gczx^Pb1 zY!mEaw%#1pL}9u(o&p>vT|x@{PVRh_9#a9P1bT=e$z?hxmcX{wtYB{tqQ^g<4M(cm%%0{#ys?EA z)Z>rfG%SjW!XxoBX&xZ;lYV3(GN3E3q~!kG45Sc_s*hdYpch0&HPf^qy5wc(l@|X+ zWQgc|@Pr+s5$?KSUNj=Hmh^hcN#7;Fl`a`mVsP!gn5BbPs4s=f6nl*AZlDbx#dF;jEJ{f3wXZ2Pt7+evo>27Yl0_({HJh zRW^=UFzPVd=8UVCi%oV7+VWak2N&qwSIeCU3i%G1B{D){+;pxw4i;&oeYKQSc$Guc z7Q6p+vR1l<&3zvtStdq}Ks za-#O4aD1tIqTd83Hwg#?zDtnM?1)LSxs7hw6|bE1Px@j;7dD%{8R1HJG=s{OZ7oA| zaac=~d5ngO`+446DonjbvX_E?KZw#79m*)lz<&D6gW2twxNJnRzc*$h71kQ$>I#~U zDwFMR6CQ8RTpD8oigl9Z>y95uUr60c8Dg@H;QM)<;vH%GhOrw`f;U_(Ys7G|BT%47 zSwBoODoQ>F3BJ$QTpEL&=nM|;RJR~jtBL=-Hu$IMxXTd3a@B7%D}6*yj(QED`&VbTB#&NvJG=PeWja4iniDQUaNh)T<9trZ)4(FbX^P+ilWvkB6J!J=r4tojs{8 zdd(!-(wq+=zg`LF7KIP@yKY5?Sr&V_+k&F)LiAP<&p~KtHF{=?O*8@*q4w8PXz z>V~ON%wQm8P$w2)R_#C09ia8lZ~j9chUr_UY3hGIj~Z^wxok4hD7}-J6fRUBsCkDy zfF83I7-&Vq%D5}puYEfO;Gb9oxM~a_1q|N}9)?WBGCt7MOCt)EL)nbgeAKrIl2C;1 zp^=Xexe6(qYl3~;XrZb%1zu&L9b(M7|B|35Ie-Z2<$g)Ca$V!nMSO`N#%kM}N=hOY zfU=zY>le2YJ9Sk9inoiDZA(y^sAHbNRok2es`rDeCoG7t>M>MqUV2t=4ylVnZ` zTu45JZr$sMiYzR9r&_kh-t#`+Y?|uCC|79OX>C|b)txnC@X+mysg;g6{8F6W6BIdE zNFGB~79=gIbakl8mz@F_x)+%j1Tm5~KA{H~d~WG6gAEkHj`ReT!hv;MCUXR?j?~U*a8}lEK|BLJp>9@^1}=h^E!Z9lc40-v?uQNw)fr? zyC;tklrS+8C&u-UyDQzx1)huad7iCcI4TiXIw;>(f+QRP&GC!fuK5m(EU}@DtCC2h zLdKv`w_savD*0Q~Y}=pe$$5IJ+0T`2Z;WLOru77<9?LA0Z#~t>dfKIO?m)p!R66Bg zZ1xVexUgtrqN!%n*QTYZYeBDS@)W>r7j(@Y2{n-wl)(-)31i>YyPIjAm|REA(@z0E zSIpH!|LTVUH!Oo3@DC=1n-$fZEeGJbw4ZCOF`=fPNzc3H$tfyM{ zV?7g3Qn!NK4>g|FOWGJVg~j`Ah#vBxP7tJBc1F`s)SFyHE#RdUq$IjOlZJ9s+)rcqYIn* zEy>L(8RA3IT@!`BstZ0XTU1`>Br9;jOPvvZ8zU@Eevb+g|9V`EqBOwNQr5DpNw@-< z!TG06_j}sPTlom7h6`WDCeNv^ZhkMDGE5pAFe{3SFWW+FK%Cb-+3)Y${si~sb}}k? zvuDy^;cuI54Q{e1;mt|2bj|W8GnPun)ATTZwAUG76o3m<#7waFdX``kyWW39EktX}qr0N!#MpwYz(^mreYPmNV0R7raM!{i1jV@wvLxTR#kCUI!Kxxe z5ScXu4?f;KIi{x?7FjGj(DkY|rA_+M-8_6-OiToRBC|M<%^HK)kiXUG36-qM0rO!G zk=T2!s3Tj`y&{jt6e`b{NY$OoLRFT-gGE%!Og} zDw25!@-XhQlY4A6z(`_NJdj*fh*vanKg=k-U=nCiCf_rX+bbqe<#}vikahyQb%b}m zv#HqE&>XUh>xiCGJ&$Zk=^rrGGsyQc`)wrxJf2>#WVs|_Pm*C7FcL^sS+5Prd&zLn z*1S?ISm&ns>O|(i($Y=s?s)AKC{bQ?EV(-_`ss~E3B{?W4Gwa!@I+m;y1yR0LWQHp zkAR=tCfMK(b)-o9lzWMrhAxU_uCqEMCC?UBM`)rG-82cRA{$hhCtq(ZZvqzl>#a@9 zOvU{E5zwaWK9jcDkA&(c4;hkT;#=foJ9^?$%Y;<1B$-zITC+t(>Z|?7Gu7B)+ED~7 zZY+7Y=#HT)u<&k~h&QRh{m)G@p&+KzR{%uYtdAs3^|Rzb%a zM+(Y~1ms-Wji(?V&HG6q_0K-_;+}GY5H|g${Wg5x8+I(=y~Yf8YZ(FD4n`_1v%&&j zXqU=O?%-*TC0yY%5m9R;D^4ey2t(ovVwdJ2xZ;7uRM@TmV>U#&b@(SHBu6o@jGqw|vPYa#aypRCzy1erW!- zfRSQVFpG_fNHo-l@$^!z2Vz!N*?K;{7osaYE>&k~@2rI3l8&0Sj48Dtf?K;1uz%86 zG{7&MSQ{c}j_vbA_snvmm?-NxpRz2THn4r%3xQ_ZHYN8$1uqKf_Z@`UcNe~VbmXvHJq&l-JX1O@vm+q>$XhlvPZ1x5^?(Wt)x(CAoNala22{DZ z3fr6O$4xH=RiCHzgeyJ8j(siP3Y`cx-8cn^+ni+F>E0QIQ zwiaSt9nle*psRbWjTc!T8)`!6n6FZBw`*E3688-4$I6i+j^8P*^cXvCxn{8}nY^NG z4Xn55R7N)j+CI!Nk8E64vKh&LllrATBUGQsS77s2&36N@{`mpqOZH`-qf{x1IJahc zUC*Jd$0cUV;xg@iQnTg#r+Ke6w|rKxA%&Lgw&~IF2)mNNNiC#f>?~5K!$eKCOsP6e z=+IP|OIMsd*}5Q#TF5h;me(2HsYQ4b(=(1855H5~0DD+~qmg`3>~T)}Ccj6Sg!OWa z*)Oe8=zU*ZKW&)5jMb1mus<>h*$}+79$Mlq>I5z&T{p}pYmI%=AsYe)X3KC2ubY%`THO?HHWL$P^9yxb&^@|u!jL)h9b6bW>U{#? zpo2505C=%ww|*WLWB{HI4pMa<#TAS*iB2nITR?o&lBDlwiUI+ zg(cTMYSm*u=qG=QLlwMa#6Xq3)A1anq{9KvZ@7waVfx zE0KiuGOy}CY?qPkTPDTOeH<~Ju)i>_b!RChbK$mN{9be)I(RH=x~N$WL~EVWdNpqP zOeMYjonLGiY;+P`WL71X!^+fIo8K-}8S)smtg)jfK9Jg+*mAb>R|#?}9YI^BmDw&5 z^U{Pu?!C#p$4CoIFIXS}mC4na1C~ysN27}dl`hcwc(uE0IaWSJ8x2?H16AukQM|Se zdl$Vn8onD&^^^tmLIa+tiZ=cBtBnvg$NJuu&Hci#r|PMXALq&qB25*?NbZ{O9J4JED%3=q?*#?;l7SWHY( zmhelaNH3C#_Lv_6T8#u1e#_npor70(W+^n+LLG+%L`e0zL7PT=+H43|jR>05E)CpC zP|S_dmA%i&YQovR@3E+prG&?Hm>g3oLqMNB#uU-(U9QeriKfeaendxwUbGP@9++NU z^k`98*dRBLOyTjk}imFp?Fsord?1>=L=1tmXxqPt57z(ir33DsGm4hu-- z&V=0=hvZHiX`P5>Lghe!av$-S3)z=dM#%1z( zw5}B+R!U5VMxd4o+iO}`B=?fA^okJ!RinYZ*j}Hm7zUAkQ`ey$=_|$g7(SR2|Jsxw z>;QMTe^x3alPsiMG8J;?UZcx#eyxpT@T~`=nh5+8%OJ;R(}~3DL zJma3+9updp>;5s;sKiRwECgs>mSy_Vy22AFt>S>!h>{Apc7S<2Apu(IuN8L8OS7C* zV5nzP3Bv3hXPc2`DB{r`lVOp$w3~yJtruc~3CfoUwx!&~B18zqDQmvpI?Di*%#&c$ zqDwXVO4wFH%CIMf#osh~yZEioujz~4a|py#FEOrc?;eG&p!?XbUw-UG8*pfkY8@x@!KBK91k4(Vc@JjlG~-Im+PV=& z;H{)XGpy8xm#c_Ihqd6RYb{eB#}4dXCY}OTPXQUHfOn^WT}{x;3T~;f@8&>fn}M7E zU$uW)KvI5v%H39C;{&e%?Sxg0D^diR(cquW?1xzq165L zfH0%3@1fn|Up0_+j$f@uj6h*7k4EZ;w3-e&OEP=|jBxcgY_UFgLq}{fLGqK zr-1W;svZK#@yT|Z^$zLfS2|0;0d5q_=<5<9{=QyJ z@u#}m+|Qk~QL?-R^)SB~A#WkKH;oEn9VeBxes;#*JHt@B@=@Dy<&uJGYmn<*d|A2+ zx=?@*p9xJ~cQ;|h;Yd-`wG86#YcPYX)!~Lkfr_#s%;;{!zNf(@u&&t3;qvHGzvEOs zFZeDBv`6m?>96Opw4tcpol-T2#Ed!6M(nW}7ZwV_ZvCOU4J6r>DI&u&wWB@``I>N& z4?R{#>>;@-R>ceh)1wF?_6@OH7KF(As^uJg`E6Otqxc*|LX~9zFvq-AxsK0kt&xp0 zig=H%>bMB6e?DaqhdKqg-2gjGe5utyuY7w%gSk#TKJ0Ig#5 zJ~fWd4FsYygad^?YiFs&Ut@SjPn1Jn|Ls*@;!K zQ%&t#Kq&|RoSuQeYRS9%>JN|ON4#4ewvt`lXxx4b@G0QY-1q(Uh85w^w$ckCWG)2~ z{f;U=)S8f~9$#guQg2$>O%?>9iyCWt`tr47<25uwXuLZIv$~n|GIgC&n=y4`O62Q(e^uD?z~4W^(8^Jhzw|aMPfm+I(d4 z87`{u6tMAe_Y^P$_H{)D`d^y{9?{?m9>yn`hIYrsUUEzQ{c&)t>@s2?+q&WLyh^mE z%_6QXC`P?iq(Tm}Me3y45wh>n-qJwqc}~(=#)!Bf_4d}hzlzZ^K8Nw;l{Rea4US0R z)Nt88O|}+}O_kd-j`8)JB@PVSh0;*F4*N#hJTSX`8``gbyGf}FOlYU>AQGpw?Y(B;jaDr)NKp6hr>I5FYBfauoroO3GeeWoDm7|+UhpHZ$bQxF~6?Bq- zf9muY?LzzcBT2aEb!AS=LK-LZsfK~stuQ`^ICuJsI3uy1f!BY#1-k!#-`Ws0;iwzu z|03I9+IJO)I}65(6M#BhmP_M!Y;7&&I}pFMMno|GVm#ePz8-OHz3iJ1OZWT^T`x&T zpp#*zfSXzTS*D6l4H6HGW*|#h-p|f9J4ox~McDF`ZsWneT}NSO`3qk|1UJsDlG!6K z&+h)U+F>zhf4Mg=&J6mxpA)JoR#;!!$I9wH7BjV`!NTB%REia*d{=tR^sh`%jZm@R z+!<_d*cMJJ3TaH^pxpY(P!@?|rij1>TUO`J9YgLDAlLM(63>l-!3YX#mSQG?&(YUOl`&{zb98ztg6r$ zy&>)x{Aw}owzP#d%*KkSWcamWa#E^1tOp0s@Y?v>e?i;lWz(G!MrUv|mspkPiC|S~ z^YMX@{ZsfBlP2u3yjg?b#fJ@+htxfu-Ak{fbwZrL{6^^zl*hcbu{>@P}39 zgJykTBi+!LLM-)bgCAt#mnfzA!OE}&p^}rM_OoiO(8%>W8KzU<4=Ez}`#M?f27;GG*TRu#Wm`z{sp{S@#C zMeH02O6KIA(Ni{>syR6J3u-A*lB_^KT9>8N7DIS}q@8^`!Ii!5)|eAk6hrz~>m>p? zKeS1tc zz4e@5a`LX+(2)9Mz@vRoCi(kHUe74w%(+p%8WxM2jqJBs^+=W-_)MdzBcysKtPd>& z$JAGbrctE)YrD|zU0qD$T;vTQ+GtvTYBS=~?Ej~kMNyMi%C3;~d24P;fxD;L0 z3tg#2Fjnn9{1reMUt!XAN@t&u5?r(tjP@COBxUj@Av>Rxe_4H@CjWfMxW1z0kqn=b zSJviI>LSX8Tfv1V({q?^%;J=yntN#{XbIvf1GyVp#JA;M~_i; zGOrn%Iwd1P2S6Z{R~ z;ePUchrg9EJavW$Pn+rq4pObI?5>4Synmx7e!frliD&xDY;G$hIr=MHR6cebBW3|c0L@x8sX zcyZCV(*2i@JIiSeiBhF()#hbikk^72G+|o37~yd2TZ_*KjF7n{ea#bjOJXa zgy0ZngTAj@Vs`A`=@VwqLiX4PnaM{DGPXd>LN+9=O-s_>R7T`3{PVfj&db#FU zgTp6xXf0SK>sC!J=07&-e?IkRrs)9sj`?Pqtn~tIvM_nPT`XpteV}7qkNp(ztZ^WE z0S;eskZk&%e)uP%4|}nI3q@5@MikYyNh!ovkE4p9@pq~l%LDdbPko$n&d#mrk{eWT ze3L>9ACwK;HQIorTQBZ}D=h{McDmMEJ2>uo?!1q^a01OpKzMlHL`xw@C_$m zC%@hF9(D@}^Sh1CaD>aEsJ z0nQ)YEYOMRRT4l#jZm@Lds7;$OU+Z;;lqcV7fc;87I7B$#febAZT4*T>u3An7+?zp zZ93)BKD=+G_uyeCkJBWMkYzpm;0{YJB1MA~HCGwDqiu5{on#NK4wupqjQ!K5yG0e| zz>T`+z7-;B^Df!UigO@^muJO;hJcL|@Ni;8diuq5qz)s1RP)M)5$lt~O3KU8%A5({ z2dDpWah=OyJ;y(UOk(}4c#juX>K7BwyF{y%VYiJu+XXr^^LO~-<8=Rop@k2&Os=o{S$TPk4K2h90`jEBGXe*Pb;#`ZdEyzz}>-1mLLX}iq>&$#G z@}kaqu3}iFn!CtpIKG<@2Df@_pf|a=0FL??Whj^`lV}cUc z0M(iFVb(R=&J;nbU9MtBcm&5?)ze0 zp4i|=4*s7=1Z70kx)phpGOS)oOtXhFGUY1y=hTiOAh;D~0uz*9Z9~K`930 z_!v*%Gorp?pNP8%d~3K2OpI{jjV!|H@$L)1DMoE7Jyq0Xs2Z())#o1+`Vc5!_xrm> zYqy+;;MT{YFmnowYCba^x?#Z^|0yz2j|vio!|(4wf2COq3$+&q`ff^iqe)bj7dDtr zB#L`Mf5p!O9Z*gsmJA`)TAkvw-Frm~s)6&M$xwAE zNCPQ7FS%lqs`nOuH3yxv1%BoG!P%V?<^(;+Xk_PPUcfn1@ja@o1to~2ZS$-k+{Tx0 z?aPx32tA{N!0hun87ksI2I7c`f{b?;qapqgv4_SazvT#nRD(A|j{YgS)+mRnvUCIc zG5U4?Lc_8Koz~GgBsGn-7pde->{SUCB`~6hX&0YOzc!m4;d$GNLhtJ>qnu-PhN$!E zRw5DNTIHj2*)HDdxE7;Kw;>3kdtFQ-(hpWKtge^2@aFiW-^86Vz3Yc9y`m92FN*mz z`*>JIB^agHkJ0#U(A1Hl#~-7c8+ubW>mWpXnG?2oP~fA&pqB-%I8`_4IhNvWd4u>k z^?Tc=k)~U@Ayz_0@kQ=b)t62T(X+k807S~9@3?bWi&UrDCwh|QJu)dj;@X=pRe2nr z;d=ZY=7{!X>$b`S0#Ft~rV@fGP65C9s)d7I%D0zr-3E~heB7iT_fw5*)G-4k>CU?6 zsS3yzzF*8#bcOln>El5ao2 zuY0oQC1zZ3NN&Ox{ke2_^rxF&zN4Okp6Vtvn2p4DnW7a3V8Cv=Z*v>X*WnlsD`5Z!}2}a#9 zOUbx}u-0tn-Dg%Awk1U;R~(O2C}I)^ss$r@9LolN`*uGllM!Vj5(^0}rL%W8(~Gso zq5_b9@OS>8_ks`X45VSXhv&!VQiJj;ZJcy^7iAFhLox}w2s?#LO}^wuFP-rnJC@0B zobE@I4mk15J4K}91HU{^g@RIa>q+FEsHKHNb3$jqJElGB{tE({^j8{kHMG(vIdp(c+Q+ z9a7x*)aO%xet9-?=qbsE5#jh9W7~E_RS!@-Vas+u=6(O4Q-iN}95CwR_QB&D2l`>9 zJE)INn*Ugvrf;~`F|aqh+T>foki7UTYEN@~#J6!;Vz05Ed@kt}@DKUNJ;%3p$jT=Z zx+^)4yR&1+XDbh1o8D8vpZRAiX`P}=uz#fZ2l->PiFe|E=L4CjH+g z@&BJe7w2QtViA#P<=9@UhFXUj1XfoLGu3Mt1#NIiJ0!Oem404U}iz#8Of4QJN{?9Mu z|8d2Cc}9Bzm(6;liOV2nJZy1yV8f3%=H8vG3nJUoi#wx>+2DfGMgS z`Rx)JCcH;k3qR>R$_5e4t4;yaANKjaq2?uU2jBawOb{xZP&Ljff-}9yqE$VQ>Xm}C z>RG^*Z6N*YObjga9zYD2-qgZcB&E|WMDU80%0)T^^{$5Yk%zwO9z#r}SYH11vtLN? z>0PhKfh8Pp4-$@2af9f`2_IU8x5>uoO|%$G7R>n~3W1eux}z#Dh$FA!L?|w!MkWSv zPG(n7uI~zqtEkhuFo?;=a#-I5=NIcn_Xr`@9p=Tp?7cK!vs=kn_jdUZcu{AiJ+WcK z3e_;SvNDDKt|j1&QH9mQwi{W0lFCOZN-L2+B=^Q!m-td>oS3qmr8TOF@5nXR^?g-= zxa%F#uPVJwhwUT^W)kQSyNQq{w*~EtI`P+(%}1kI>}EQZ1H#qXarX83Qq}159f>{B zHXDe_onfj)N(ZN^n@c^S3~c4EN<#5uwr(r=;^(3waU%xZkSBxD5T8}s2Vq#*!q=9Y znfS^PgS_gGlIdmo`KDD_Oea|07bn$2@A~dJ$=aL`&M4LVYg6=R`MjTy7n{pgMHERH z2vY2{d+iLx8iUw{qtV9cmb&^wI#70=xR_G+4>M8ALSFSEMro6hmfyeaQEmMD(BA^W z!)79$l@Rz*RIw%-DMNnOvv*;%1utn)-njuBU!p7grW68Q;lESSGJzQh%@FSNDw=F@ zY9rQ7hME$s{0Hw8M!|xCwP7}niR8KO_Tb(}h>|~;A?#6#6%;9PYX`k7BC8WPDK6pK zn=!94imU2#V#s8a78_NYh!YJ~y!QeB%7K@L6q5cr{=9tnNx}MuJ`y9_qqHFP_J>)s zLrEUSm@@0|*5P1JZ(*SYKKi=;aHWHNP)KC}1XZk__>=2)J++*{#m|azEiwak9=#}; zkA+yU6b99O3(12K+$H+Uz-D1N!Qz75x}X3AbnkEoo7*;>kTQ5PeUVdHWP7~OxsCAb zc|(8C!}i>?vU-}EQRMqR{vuyzUqPC-t~bcCF1WWxnZ_J(Z+%b%Pnc5zj$^4RooX1j z<-#o2tgY78k1P2#R^n{lgpNfd2`C(2#GDntlQGJcE&<&$s>yqlk+k)J2WZRSpv4kg z3fGp9-+&}*3nvFuM|lgBj6f`DZW-p+mH)+!D)KpWOqh3T^RbeVOX>5kHpW&o2}>(} zTNySjb0s*zY=>riDoveYm^1GE2}&J)wGVee!Zd!=MsSC>@)~Sx3sg+tnKQ?x@S+NZ z92z;*CQXF{ZArzD5bI*2i>M;?iPXTN6jjYQ)j+=66ocf>Fi8Q2YzMq`3Q?Pz4dDr~-9eLi^%Q7{;k>8~D%s~|DSoh~ga{P+qmQEdf@6Ff) z-lI$!JhU_WSn+$2sW<9#6RDKD(f-%vfs(u44v&iz+cQp91RxQdX_YMOTxON#BVY5` zzz9wg?!GdTxN+32DCy1DJ$j$r~ECqdu6$gEDmdj zMyk(ZBQPh=&C-l1r-160u6x$Bb!ca~jKAsKL4W$K@2 z_-`qNc;n;XlSRdal{u!1nB>X|;5?nn%ySNS6cd8zM(pErgL|;V`mtYX2E2vEJk699 z{X8M`Zl91?_jt>~4MK|FD?Oef9}gUyuHInHb!hVX(}+r&mygOcAMEL>Oh#QL`vEXR zBx+N*zRi=;PRUs(?O;PsN(B)wP61`=(jPTt+dItZ_93=y1mzOcI__*s3$_z7`Rm`s zyfAHJql?Z-pSFA4F0njXnxI?KKAcZgigmVmEvD?8P$s6R-;tTL<3&0JiuWyK8wn}ab2rgCCnc1LiHhXO(^OEo7v^&q4SEyXjKwY5(`XWaRG zi)HBFD)XOxy8Ul@l@X z#l-~BVZG7ognVi=O?{xbh!9Z*jx`e^e22KlFTZ+i))Cz5E>?xqB+uoKMcCAL2-}=} zq9nuo_Bn6F-n3%pz2DF#rhpDot0hg8$^VoQUN*FD3f}LS~zyp z)dkT$@z;ujr8z0(%{f$QSL>{2&D6SyjvbG_sgu~MnF57<0=;P(vmWc(_8Vlkqetfj zbUz!69T2q{E~E&jM%?SapPP0HNN>$Y$~0#mM>@hsdbCO-LSlrMF<6jK#RO-eJq=e@-9se;@WGwc#JEr!*3Fc0Q{?=|8p(>{PUeQFnJb&C1)O0o`u@p5ilo520#w!E+t(t%gdhRi2ZiW73da{ z`cuF5aD5%(y)4#8Y?-PII~+=KU5~T$P_cM1=~y+Of?9hz0TYR43B)fB>dgC`{h~rD zIm#*-%ijOJNAo!A6!4fRuJUu#+MWC?%@cSjOo8KJpphsyV^RX@)x|MKu=bvx)zoR^t zbadw^5c}$Ic&fn~YW{EffzV^i()E2@Gs-|KvraTVi1uA2{XM|9`2Tdg{=2&VmkC!i zistw0RC>^b6zFyNkqO~9ssU3O zq3hL(OLYf$HN)Clx4678@){uD^~KGNEIhd4n)7mDaOy=(EJnQwXLaUpq%Qm9wXSow zi;-f4LiGf|BE{)haj}r#nw33Dj~TEuVbOoO9jq~*?65PuMrQd6*&070h?rTX=PTZ? zrK-md1+v7wxYJ&;4a*&sU&-o zd_#2Qep{bLuiBUesO$Z`(uaj(As*S;GB?8_r!aj-@13(x&h~H$VC-2}_TmQn(o^DU znzbC67jmdE5Wf}@T2Nk_%m#4{wpy)|IPq!EVf>WU!M1I~MnH%FGjghoxIw|gHn!zs zW%`Ea-ee)NOzBY3hL(wU`Q+SZ%Hp^HTvEX2n{d$cDLQgsP4)6v#aN2mY1wl4!ct0P1mT|>ZeO}n=g8=xzjJ0tbFZ`-E{BR zOtqF>va#M9c`{-Un(*m$eFt9w3FVL?HQp!Y7rz#e64`79EY69Mzw3m*#Vc*La`Zgj z_ORfdo!+&|;7T^79#FiA3UIUJBrLHz$mdmTQF_J*+;*>OY&~i*a^cP90Y3A3NhSef z2rUj-t2**E{F=>P08sOM{J$LF{~^J@nN$8>pW8P_7?^!MS7J&2Lc^44vR_y0!dLxQ zzN{0RN?W#b^Z=vAyS-P3Q_a&a|@ z!5hW8J{ztQDCStcsTn|OUj)`5x@ZVwqu#600+(vI$rzo5sSx)r=ZkOr`;R9##e@Us z;r+Ta6J*E1$kVqnwurbWu9BLpqlKXTZ~q5-?-|h4x~&Vdin{C@5mf4uPH0jBhUQiX zy%XRJKr+iXFOxSH$&t@ZBmzp=Bm@|=;G|7{5P7wD0Mh{U#O!}k+ws$lelnB zN;LQc*VW24v=>+9Z3?Q1UF)zs>Kj$~xFYYOe%H}dqo~L51VBvPZ8Z{65aZodwV1=O zTg(ASRWMk~N^i_JHw;xw-m$lrS)q$hLYlugpj!J}yCof#9Y>5U`jW`Po%>=-0l2>rKbn~E3$(=ux%qftO>tI5 zu>1M4sya>dm2I zH=Yy{wl7&I`%(itnferoZn=JN2V!%V1n7D?q*eW{_0vL$8CDI#?Weu^3MoEJUiO{S!p#gWFW z)zaZDef*$dAVmX=iDPAl>4;w+U~Wl3X6*9%&4be_`mXxs)j}p}<`qKMp%1}?Y=K`O zre*)z@BN=1{O@THzQe^#Z<*C|V>&M|fIwMKF|i*t%mG4!@aKw94JMlr9KeKUl877@ zYbrjmZGeWp{6xW;mI$M`D^VtoqoAEHV{9fH;$(_jS=VgxdBZ_eTbfTGI#RA_A85%@V3mK^}_dE<0USe z{V_lAS6zOiA&@(mbt$3;@o|cw8vgL)ehSaJBQTv2rYe20LU;&2@qS!bX>YizbyY-G zVQrnRNZN1i-&fl5DMw2Acll)vAx@O-lW^5eh_%MHw7t|2PU#eor$}ObBY7t`%?!J- z_LEg1ckmCZG9vs$=@`}CVk)Go1-BYfb3RJZpa(lzkv)fDE4ROHD(6y{dtLa>DSDS4 z50A3SgQVcoKvs5m`L^c@J)g-m=FN0AXv^kWXY|gJA5T|@w@)o=4$O_;act)DDLdk> zjhS#9Do!|%%wlyu#MC79%%oiz_)jb&_o~-pPm7{#`QrbLox^@$U;Awh^8H)IYRHKE zsOmA~VrJo=Q~2=Ps$XAuTIUfn#k@yi_tu`kUESvEIRfp3HmQr%(Tz-F-_?6eBPhD5GALd{1a9{gK|WgnPhdYBJv zleo2(F@6+~KixZH{;@n85LPcgv@@HS7((4c7H(D^Zp|4tl7*X`LbFq1pkw`WuU=7C z8rF9BPOY3zABust26GX-=|jytwLB7`2hP*heo<|Gs^e{;W-s^9+|e34t!yKWR38_7 z1wgtk=iuxBretOc6i^a*-;D{*?>5Qp3;TJ)!vN04*7^c$l^=kd5ulT(JT<1-duq|g z-zGC+81Z(%cC5)xI@&ivF-)V8Cknkhu62TMz%+&@NTiEV@_M+keG%^N9D~)f^(=h1 zecu1m)|E-qxT)mB8x+y7nujJW@QYMV$A_>6TP9DRc%9i)-1*qZY)(cWTi$zYFlMxS z)lXzx(n@iRvDwbT($c_`4yLl1JZc9N^;mBP;9x>R=s=DZyR1@Zzkw>Wg?Lr}1s)`b zNhT0p;jC)J*kriuUR6(La!aVomN;?@O3@I?EAle>1)$Qfi(c9Go=nO=b~_pE*=-{+ z&OF$t;^;NE1sO#}mJ?9BE$kQkz5xT~t)U(lI^#R*{xG{mM!E8uo`FqM?DlIwE=E8Q z>t+f#G4XvfttA517-fe*{}=E8^9nc$ zbTVqiZ81sskd*GlrZDEtG}<@Q>E%7}PIII0z0h74j>llB(PoIbN5I2|IqDVD<*khY zirf6`T7^Z(63gYO1)3{BV%s!Dp}3XqwZ@r~B( z%_s+y()GkY?io|GwEG3#;6i+dE0XB^q{nWSZ?W-=@S&oiWV|1Gm*U^85X~@$nUrwt z#zo^rQ&hyxG2PrzIPExvnCJeiSw?J{z7l^7baHW8#X@(*y`Vs7oQJHh$n*KV8C+9n zKef3FK_C!_T@a%}yI<al^L?=mz6|}rf1&>U=91ID^(6uy{?^%G7zXr} zmfn1HA|!dq^GiH<4*q2k_5PcV()WM5|ML3jeJ@J26?qK~;t{eB(x(D1`=oTMw#)&( zvM3QBdEYNwbWl(2L&_ftdCO@YNJ%{1=Vrx-=B@aH=9^O)X2~-e!_V7c6Qe$wlri4k z1Ms76Nvld^JZ`yoG$(q>Iq^n`+fyy^a;IuHQ3fnDHTYG9>-1PfgIx0PIWP4uBMtix zy%0bE$KWG;oP*}t$H)><(nUs=dws% zOoPZpvDtwG7|bZx*+r+|kDTB~?c1m{C31$p**g20QR4E3P_8LSoeRE+C%0KvHr4$? zVQ|`md`5m&ccI}>{^9&e(zt!PbkS}~(z~P|!!71g=E>(*U{>)>RN9!sFdBV0zVL?S zsQdUAGRA+pQ8pjDN-#a6{oB1FTPF@D17=@Y<^%GV2u2GP%_ozQf`>EYhQpz1zqc$m z%-*wn8?xdTGCdd}^b9FuCat>2bGLw@2XoFuYi#{N*3pCUs2U`o*+>Uj3UJpg)P<&; zA7F(kLF{^zz8cTl7d{n4>^@BP>R0_^hqEVVXp-D+P<)m+h!y$^6I@}_ z46aS&J$fpuUr9fh@vvri&AWN4Wy(8RB~*vihNLT+ra#xV z7?gPNFRe?#&6p7=9*5pXX63Rqy=~Rjau%ara~}RYCjp$8O4r6QC2-4WiNfo z7cg8pT3$xRlAp0HroNc-(G611uY5Qt1$m8-&$#}y5lbkp)+Od+Hx2XSd6C(M2OXiS z&>|2~ASMKH+`mus_lUEQbIr~d?hZ`f#am#Obr)=##Fk+|kH$!_kcfgH85hQVA!Iqx z?n#GBscBQ5ry{PxQILU?s3m6Rwr-2F7xvVe(H4Ztt(=K(T1srR3&b=1`E}kCRxh_q zKrLw|T?!csGr*Gz#u+M&HdVddd9ED&qd%gdCG!mbwt=`2x5(!dpDV({{FK9+a?g$U zQZj8|AFP*X$-`6;V0VRsddObz2b~}afo{^SGbY-VdXeZFG(!sQI2C2~DsrAOvFj~c z?fy)TyK5=jpP0S7cyQh=)go^IlUdx`F?`Vu#z+n{2B7cF{jo&l3Ue_jJ?sg^y2-nu z#5=xAxo!VK$*eMq$4^k*XF#$6pl2=9CaPX01F3LPj4s?~$?U5PylQTjJok#u|M{%FW^Nm0Y2}}s&1-z>i`d))=lQV+|%<4yB z^KcgR0gzAPdp+L#<%))cr2Us7hKG2sM?szqh@6P$JyV62Qh_FUX=(1!4(i4w8(x8g zMj-$wbgxfK90EDwB9(KN&DQ6Xw;x`vnvCZb5R0x>l_wYXYWbEJpa7R zMG#^3eLoP_WtxZXLC&Us$X0MoAGP!)7lNZqB!Os^LMWtZTQfIMRy>u|tF?-o`0S}1 zMpP2Ns<9G+aCtL$>zR{-nBNDi;}5l-!qfyk$)_)vq$>WZH{>5t^KC#%03gH!yU_Cv z#L~*@RLtEz`e6&xg<$y<3MB;~3dT@U6)Sy;Mhe=g{6&GoEMlrZc8R&(@ODhHTWOl70Qa*x z7SJ!anch#fu9k4^%%BTE9!5(wT!(*Zloh6^nec$$IV7pY#^L(p?mGk(TR=H)kRy?u zz#Su(?*N|QRI}Xt_?kgWP353z#N`K&GO(>G9)*+~1E$wQsBZkur)2s*rV;JCO%au# zzNu4K8Xh&_q(zWMFHlM!Y0PYlb`r)zs@>FmpbOQnPk}_OW8)2)pd(uS3$hj?Pzcn35Wx;pjW`Q`;a&ehZfClRm!We#iq_DA{mOE?Sy^nf zWR>>e-VuK80X}NyK(nX2Vc!mBb>@-y1J}^7y?UEW)V6=A1L>L>>q}Ow0Y)Mx4}Xo| z$NScf%{*@()=-;>aP2Dv+m{V2I6Et6=1=N2j)=`N{mZkMj373vRY8Ao))6Ty``yd! zGJ&;MKjxQls&S4&=~je|MpF~DbMbHpTutGTN1mPo>Q3zOlxyIvbn5CTC;sS?t~9nm zaMjjq%Luut!k>1!{Bx1bYVm@-CPzz47?nFWuS~F)Tp5_qLNpTp5$ckO<4&sL;}mXM z#Z~Bd-OMkaqrTB3sO?J*4N3wv5=IOXhP<*fQhB#e$;-D>t5~U*a-f8`PAn>4!!`p) z>Hjf8^LBD3pl$ybO7Jdo!3q5Ro)TgSMNm$@1?iKe>2!nMMQ9w^4hs0De8+R8 zDH{Ee;q3R5U3SX#q8LS%@8AAXJq5}XX_p`e_xQ?9uJ9{A3wSWYmwEFrPUacl5MRZ+ z=kWfpX@Azj^-rzMzTFT=jme5=Imj@WT<`h4}g_z zIqB*5#q=|UYt|kWA3l<2!T#<&gb3{!Tf2~z0PZAeyJeTjq_GFKRNXTxNPbxhIM1A| z(E;pUwfAJ47y0C5mTWwvmQI}ccDXKGpx{MA#c|_18#dFMmKc~^)$sR@1LrySz8yS{ zzVjvWfrQKF^Nu^(%#c?5{zsKTmbr+$VCl4b`ag6QZ~(uYDn1l^Ji+_ktp7bn=W-Mi zw(U}8rAFz2<=1b{>pZ!!^KC;l3+=I8Rxrw*;@i?zqvW!8(V7@ zpz`OQ^66k51=Qt5Kcw8F4Gk0e;c^K<+UTwKHT=HFd&hW&Ljs(8Xk$ zr#L#4pFAGkhV%(>dMfv;8d|>^3wW@4Es{)KVYsXZ9$a~#8T3Xs2@CvY@RLYg7aU6O+j4gH zuA=LcTCPl$k#P> z{omYyq?EGWIenq$E6d>c_Bk8OyB|_N9QZN+&gf~4O#V1pdS~?m{_&ym$vb^RtiaG6 zBQ+F4ZH~1I{*~oun~%9v&4dK2t82Y<`O31#yi2JkC1#9Er1J6Ij?&KA=CzA4V|U(O z{ug)nKK=8e{)a36TN=JUKU2UzD#}7ef@3=x5zS71n}sddBxVvEIp$xHue!NRZ=UGE z*aMrgOxx`{y4wRtFK92yCAW8m1EkahEQuUv=aODHS)${;h$$<$E>)15+fkiCY=zWL zQqaz*YW9**XC~48Lg9LOr4C&>g%p|sQEBlg;!&>K@FStzN?_>Jyx3(tN*QOs9Wkw4 zbZ;7aMFz6$N$CV@xssArJ`LHK8|pmMe4+XafqR#-v=N`+=CIp9D%#CFX680^;Q`!< zn&*e3rSe9&EDy?bo+Wg6@v9g{uj!ay)NQpEg!x>y9j-IXd@f-4{TGv*HP3Zb@7cl; zN$?^=11_=?m=9fr4y1VBiJS1KeGQeYc5_fg-jQ+uE06OFkR}4|e9ACAQyifvj?ecj z&3$*E_pF+9Hl zl`pxZo!dhv;jAGNk**ysLkqmSAWQL)=q3t=(K6#%?qijWC~Av-Q_Z88)N$Z6Dxqa1_I!yG7 zZ>9;lhYQRdZ%AtGD%x#cb z;=}SF&yh7KjA*>4oV)gp4*RrAP=smc==xocDf}h8Y*J&=yLm=x?@Li7UNk#&ewekI zIr087=u2xaRv727t8exUp^z5x?K+m)TuXUq!=-lM{naOma5<%+R`67edyyFha=-n8 zSG?|G6X^y(E}2r^Q&G|NrM$z|*r_th){+ifs1+&fUks#&JSwo?cZEMXbA3>lwyb#eQb^a9EfQ!i{QMw2UDmi$o`f8DO5HLbMpy z_Z7fg_XMs|7f!}}uSOKux%7h?J z?4?&@U(OaI1LMx>@ubI(pVDnBS=WL<1Vc;&ZU$Um_)(LpQZ2s$X`TIo1X60lAbnj{ zg`7)5PumuU5S&e-lTSll8-p8$>_Y5y%y#O=y@eY>$9M&yra8sP82v8>O8FgfNYzCb zbo4uv-4|fs);8&&eDLb4!5i|1rl4-BMSaW@2(il-@e;}2AU_3szw8s>CeuSK|%P=Dv-cv)Q!nOlo^VF&5l+18i(m0AZWdNIP%Wa-Gx&%BsCRFl5QXIUL{14BH%j3A zjbyhp(_KfI0nen+ss*gi4}`b&^ej)%;8jXID7!@}8d@Ae=r5#RBA6q&qS!(OeZHfC zO-bouyM%57Wnz}s`Sr4?CtXIDr8q-uG$LTO$-STbU0=-)%%b-%p+L!iq&JHVOnjab zX}mL~7VBEFl1Rn~K6F&`U#RH}i#XmLW#t+}2ImcuM&WwMhn2%p90&eGp+lV6?-!g8 zi*E+JnFEVYVUk;3C6Aq9cFjJfBmnQo83l4XUr=D*IkJ~((PV6FeoB|LZ^(mCjo`MZ zmax4}*$jX2pwXQ#jQP}*eq=hWcbJlI=bo~fBF$JKr6u>#Ogrz#U~*jW4M3#8$mpBi zVTW`PtBb02#Cdmx2Bf_g?;q>zKc?z7Pe*8IM@tJ){4@35_ob6>!+ardFAbBlvtkCTtrcsZur>=Z?MFRZ5e?=8n}_4Z<~ zg%E`E9h8v)f%<5bE1Haq%(xhk)q>raY5sX*d9ZtIsK2P}%DuKUox3kbJpdr*m8<>d z4i-ZJ_yI?}qjq281s#dYI6?ulnH!k{!mj6zl5%VZ0=`FApAVETeZ_G$D}@@nd-yYK z4V$deVQ(v}8^StrVKhI=WZiXPTG2hPQRfjd`RXmTack|RAUVQB>bOttI|BBSm57$a zC*5XYRD};fd(LeJE)9cO9CX~hT4$m^2HJ8?GsyOC`;u6KLF3A(fizSiiBPiX4C4(O zw$r*E)~VJ!_S+7G?^q75d?#=Uk5~M|S(8M*9Y$$~ElU(0-U^ zFXo|_g!XiVJPIJdO4xPH8}v_KqYIB7Z~~C%i>mZcDN9ue{ihrrfG>rFSG}YDl%|lm zMZcmE*BFkpuPnT{8#S=XV%qLv%C2McEzev)&1QcV$5KtS0&;t{c$Ed%>H8ms3;)vJ z{~r9t->cH9TF7)_Nz>U9CpeO48Iac!C69b%idKxH!Pehp+B6VCBTzDnOlr92I-QPH z_T8-!Db73R-8;}Q^Yb#pXs*11*2epT8JRZP^)|xuc@sX@%3d*G^P%=N8$Zvlg@N)o za?KZf(Sr)T{g+Pf%k(#lMOb@)qDmj*x~kwEhBeLo8C5NVn7*)fsqX&uVTlp+=Dq%4 zHD-c=x-IRlz=hT*l@7IpScl3%tuBNo-UC33Wv~_CA z|AK|;a1%M&5UU&%RuecjB3(#*f2N)9?%$XA-}`qVXW%Q#aBYcW2km%c5>@+KCtC^! z>Wh{2?8K2Sf_r8{BCI2SqL=a0cPZ{a{rMR`2!knjy)c+k=w|YLnm2&D-b396)Eg-& z$M(C{T!~K)B`29&L*P+ldl~HVAC}=}@|{2eT3GSTCkC|2*Ze!irXY-~E zI|RmtN^c@1W}|PhoO}C^ivcP>eb=Qy(sOimGxw1up;wb?vClg*m}_f7P9_)e-|QCW zF^*!~2$pe+^L-l;KV=net*8Xd35nQ;P_N{i*b1|=T6*Z`gOy+i>;A=5OXYIf(WQ{r z%H%86TLtZ=GzCnDCT@;>ktFA`=YJAndy)(JwGa@g$c6abs zAMO5ULby};w8n>=Ahl!jzt#fVU*8$%{hyxV{7&m~K*q0iAoACDHup{Q#8%L8_Wuht z(#p221u}e;_Md6Q68GJyBk>edk($;(8SyC9SyFywXw@Y%?)bl!uq5wV5_;$9`79De z36F>ujMAe=~lANfk6%mvfJp zHA_k1%su((9N-^+x4)kKznEak8lfaH<-JyblK!idAmBy5Ns8AWY8*SIzq}h&%HW#y z3?S2`HJ0#kFu0c`!wA-0OJ)~Ix#tnKJHz*RgfX{k;+;@K@N$Ab4u!7YOGcim}2JR zvh0F*LxFSlRUTzCQr@XEj0i%0&U3p`Sj+J=0Y{wC+_s`%Oj^L3B!SXRuXkQ2FLW;= zn{EKX+(iZwmaE9r{=cHxNp{zmeeJ| zsOwuN-sQUX*ReC^YO#&L2@8Z+JE zaFjKZDxCY6)$7>nY?{bn)7D{D!kLwW<4$IpTh+e_5al9M{4<8EZ}zB!2{5T$#zexA zt5B>0l3|&(&478f_4n$i3QWbV;P^Fx$sOOr0c4o>P)$#}d!L_MS#|Va{tohIhPeX_ zmkoY>za1$N>s{jVHVS#+6DD(z34xjcdoo#vgBZO%aGm@ocRvp$=5i*WDrdKh>WE*O zQ0{$Mnc9GbwX2kL>}xIN5R~s~L|~z6>y^eU4gHGFym?jtTSH|SDmc-Z_l`9|KfPr# zO^+|(lOFY+1&<0F_{=GLjTpVoSxh2-Q=bfy!Ol~9CA;jIE?4*sE8ec4w=ssBT+~Z( zebYWkB2PtVu&&R{Ro~gIV?I)rxSpYnC{F$hr9Qg?EuVk$uRH z_{sg)ZC@MfO)}2lVra8^1RU0@T%m@Ic}_+&&0wNM8I}?yh4&Z%DFR3F`D~mk>}2z7 zos9jIpq5d`5{q?<(Cy%Y?)y68D92KeK*Ey+wG(6aR@8bXibU>!p{O~hlNIjE3iVT8 z7?2#6ZWJRdpg3ve&NtEO&SYNdC0!a_?0T4>^SA5N<_|^bw63-y zNey@V@Cvym>79A^?dx&qzUY8c@>^o+^_oSReN~%HyV{L_6Ciuia%i|y=%f!PM4WH~ zW#;C6Q6xs}ZNXx-I3_xW_X(;72;j`>18Fs}vu>2`DZ90*cnU>fDNnyFC_F-r?*a zj~BgRx}8T4Q@FwIt!4d{W$Nk;UitkM4p;vLJGxA9pMXP#mnw3E3IQPsY1xXIvLx+L zoF*V|C1?MVQPM*14ft~jnSQH)qfjf9%{#<8;&F6+at zydf>Tm3xt{tX5A&6&uTOomwqE^^RDGbjqU5!FIMobp$_0zoX%OJW+3oJGra|$NI$u zHq804KHrYwXHChgs)VR%#l_YaMhPoZ8)}&o+shft#!ixjHVLo=St%%iq7P93=xNo>2NzdO+Mo6)JY5A5?-fTgo1~ z9Tim?&2WJr%Y&Wb3>t>x7d2C>H?eethfBSHr02-eEQR<&DfGHy^qF46xmM{55tt~e zUC9ZV0aQMtnU*G{rz7)@5`5xf=^^0ci%d0c~fzNiP5?m&phDymjl0mws<8T zSEgx|&(Ij9UV7gc#VK9u-HwS7D&&Wu1xV?7CX^nV<-s2a7y zo&7BB^pS^l{Y3nklR&rKYX^|Pme3b2O%?2R5C&)<)f!F6_9RhDPwSi?NS$~mh_V)Jmwwjwm1UEZ&^(2a_ei?3#TFhAeLitL{ zVhrjTiOb^dz63BOBGzZ-=vs=+LHE)#T~UYg)ri9pvFhP>GIbT~eY@EP$@a0$-|M8|c1wwKQp z6XBvQ^IiByqxZjg=kkx=#J7~*n(-6R0sqp$;*~DIirz8v%SlU@s-e7-oBqn;$D4*N z`g~ktUH+McxAE;?WPNk7z2xOLyCP`7b9eS3#1+dXr@~SFD$J&K922_ssZFXK~miE-zjeZmc7hCcI*6S+`SuBvyUs zW)YW13}ROo9SbL5jkJ@i?SflB*7OwOCR%f8mK?R%jzN$57!Y-2J}u~@RQwP=?AU99 zTFA_ete-=C;oFZBBCVb0F058ft2w<$CH0aOUOdZK9?ig-jn;BFh(mxv-Rbd^>~zw) zCB(WTOEXJu5Gm0?#68qhO!y$+tMp}szIKqkypSo6-SKtyXqnF{w9KjQ>Z9#j#Z?_( zhn($6bDK>5IA5zltDSNJ4hq#!zk|e}wbCqHL@76_gf6bCq^=u49K6jv<4Jk~KPFV9 zo8Qo0L^*Zgo;vhnkaa6v_G5{OJAS9c))6DuyGc$YyhR;^P&f1o`3^HOzory=${> z7gLHCDQEM|f*0EjzjS-yUPswIq8Ih!hN-6hmBur8gfsiV*xNB_Lx-Kkascs2MnZn0 zyvXyj=f`HNLJ<6zlYD<$FNI;{)fe#r-JI{C1&gzk8P4K9cw#9T;Cw?J+90c^0g+7& z6;TsbI~U>Z8{GbwEqM+gPyB3kTRQBrV;)OxL4Wgu#vjWDi;()X2D&nn3|3Au?p%K0 zRX{PQEmJ|~KbCqkg}Hum3Y- z)++Xxac*m?Z*-HSst30E&y)-RIa6C5o_+hmMF{QShk5&zW%FC8_7F45)EAI~!CzB2 z?F(A?*y~9xl)mz1@fd9nEi>fuX>nE_jUEi5S$LLZWCs@Rnlind)j~EPo6vU;;tr{| z%{j}PUNP?35NJ*OHp9^s76%68Jx^p|>v4>Dqtl|;)Ktl6lF0>(i5kepruF;KN&^hP z&$$W`x_Z9K7v_KSoo|+bCfb(@2A`5(#6?EW1(1+IoQ%wH(&rb>@u{Z`Iv;a!HWV)A z!I(}|S#1$(B zm4{UNlvt{!)*2){7<70$bM-!C5_)AKela|n2CIZBIj11zXj zz^w7iC9Bj))5CbV2|xh%wHN)Pb5BbB#OyRd5Qf(u$^~6M+gkFgc7!0pvB_3?DfF2o z1vH^&2?oActC;z&N-NrOfh5t67m%P4;P{{OcDh>sB24gWcr%sq#x}f z!SK_0)1+x{*T|cV0#5@fmbbflo1IIui0e>=tS-O?e&6ytkUJEv%4P?1G|)Csh^^1E zMqtnDNYD=3v%^?j=GS^tK2pw+hVb#SQQUJ1_!Ob2(LRpyRgBHE_y$T#FIWTmdcv1i zI;U?i&#a1FLxurWqL5JZ!6g*Q(UV^y1`1W()LrGE67u~?z&w;IPD6)9(&>2~3(M=j z_6?tV+{ZhN4m6p7i)$`-gw*au|AzzW^^vVHM@pRYMrVf>Zl@g9?_nh7h`(;!o& zcfYbk1i$(P{L~q9JnbLU-TG|jIC$3ZS1<0bn;QT72RBxeaS7$2*RM6Cm;rY0wrHExFXl4!CQcEbCj&HZVs*f2v zFh}%J72$wFZw(_aeX|wTGOI_*DP#NDrMk&ao3q}OD4a2HjYEm9zLr!Uiybp!1DH*R zg*|gj2k>usw<5A1vdLZid3lN}l0U0ngh+b2z@!#}7IfZ)Trn)rY!)&bn-$=#bIsl8 zf{`lH0c4AiG1;nomO#r4$^Ce6j#u0TH*5dZejN$fUQfSx`u9(*P@1by7eNfaE-74# ziMqWy<4tWW(oeWWuSfL&%lf*^L}v>vRqmIK1MCPoGttm@q)-PFghPk`u4Nn5Dz&Vk z!=C%l67(ujGj?c2Or2P!+6jm`mtM6g4Kaf0FS@PBqJ>l*>B#vql6mz|4=i^FeGl9S zD_O$s9|~QwlS(WN4)VDw)ZK!HZ^nrWWyBSbh0;qD6g^0fmgm3#pwD7q6&o4uo^r^6 z3D8ww#shsMQ4(}kp%gT_^D0@#$+hp(4u-jZv!dTz|v{-+xJUO);^TDh4)lNF~C@B?&67}x{!+WV5C2wE}YBArnFRVmX3spaf`ZPR_N@2)iqF&)#NKsvl^z2nr<)Eg}xhGjF zoTkH`IrJV$O9?N18$Cf~Xt$o>iLk2K%;&f~pL1mR^WjJ33K(WMCV5HryIVUZS||hS z?#h=!wZjV5E!4Xay(5AV3&7D0pD66bwjhmRUg{} zGU*i}?L@#-ZgA03UlP~yx=}neYWKnr0l&`!E?edkqdQN+!tDEOVcPJgFa43S zTF__OGyKj+Y;B5=su3aVS_d-}0kL__Cv8L355uQv`VAlM#@fbJZ46?bQ?C|pjPuC? zH>J;aai{U8i$&ZDXA{cAub&G0Ci-csn$8P&H`wt#xsW8Xff|c5QFxKABj_>^Zlfo# z2SfCMkRS5`G7?m!dd-&y+Eq9CDn9T(?8B>gH2>5$MqW|8y=aeJ&B7~@91tkhj_iOe zf@$H3EyTLS&6yMPfz#vzO_VBdzRY^Me!*P@E=ta!(M3#J8g>Z}Bb;yQaQE6&@yF$B zXh~lYU?_fmeBn*Ud8f)xBT~$4%k1|prRb&Zxl}bX9yStxM06BGuCXRnO#{ZX1x!Ip z(QDjRDMzJ#o&R`XvhlEaDWiT%g0}W zy#A%_|H8HRugE`TNqk&$gxLZ!14e|>VMF-Pwk6KQg<>AJ^7mn@5@A9DT{Y4d+^vsD zj?E_hsX{NA-0fe>246MRZ99o5Rl_c7naidX?*t{<{`TU{M* zdzty0C#i6v5BHmyA*i)Xbo9zEKtyAN9Z||Lx|4IYapVmSx(h2QkQx6pTYl$Fh55dy zZ{f*Cu;2eQvwv@`RyWbIBAGMWe9AzAXrcq*9eJeR3)K=nzLrh`NHeoGN+-wi)oVZp zbwcnCU*05!RLUJg4_-QXcK4d070<}!s8XYQJdOgZp-DkPDr3AIRTpW;?$YFIM}2fq zzu!$JcwzyX#yi`?!D<(mL2cMfBcIo zKzx~2XXZ@K(-&WMyynrROMXFSz5z;G3d7r#)w$Mce^KJhPkCQimX}YS>Z(zp7Oo_!p^{zd z-;TT~DL|livL1s|;x3Z{Opg2Lx4W*5!A8}DHm}Y7r6N5$XqEi#8um%j(KB;0{w6n7 zTlmexRF*7TU+W^b8BD*AYD-0VhqV_B27`|qw`dG1+>xnkig}LHP~Ulwn3&XhwSA|F z<;UMNZ~t)j?}zgL-um)C6#QRz^8Y;0s=#V( zwPeyq*-+6oLKxKV%&*%*Os)1Vy5`#VXnWktau*EVOh<%bv-^vBd$N?5Q=fhlwa{T6 zP~B^pcR+nyVxpgGdcQ}Dv!0_}1M0R=0|Q&fL1SkWP>076=?vEz#vsbUR9xH{LYA6>VE4F_ zv)ip77xp6Z5)LXP^;_!g(Ey-**z=~DL7pBGld@1ZNA66W`x~+gmwyK(k-sGv~OCL>_wa~L=uoB0Rs7(>sB(S!6gH%8KNI^I#nT-|`%?{d<@7dq4gA;NuK2q38O^wsEhf;f2igx|Uw30fWtJF{joD zqtUTmwq|TjmW1iY>muj?!btyJ6_?_nP(rd(*pcA=HG8lI?rc}jtY-N9g5cY4L;o(> z!@ot)cFFB-)WEu){%%{u!l~*PDT>j~DMcccuTs5WyR3?^-9NnDkQ(x(o-%pcPvg3h6qNs!ZU$8r6;^N$9qbo=~)7xADiYX*Ep}byK>U0<}yS zL5Vb0J#;J{**Kpk8QrdVKw2C`Uid>XdTiS=O3~_7>OsDSA^FqT^kC`bd*TgWS+YCN z2;^w#vF3Y74c<1GB*rF!Lus{b-p$H(x&-8UIV8{Xu^hBIZa~nP0eBmeDewMQ7TL8vE{cpVslZw+fEsi)nK(J+vd2R^^Z>%hbGz(%{Y8cZ_~1tT z!b}Ts1F)pkzTeghmzhs2hd>&Y8U{hmg6&#Ik;wcI>0w zc*Ls(vlEl)G4pHBaf7eOjG%GFYA?OE-6)Q!(ug(Bo*ml>C8iNp6M8I{5h9`eI$t}ej`eUD^POF%-Y3K#KvR5}(pUUBgYcVq0-ar|T z^O3YQx1SX#D??0Aj5gEi4#f-iI>SdjI<`kEMYMn-%FiMb8tUDzU0SFfUZc%}XS>?W z%PzXHGXpTGMvqFh)J;6n-$sZDV^|deUya7iQw$iS)e4e(Kc?%sDYpq~uc%8NtU#Q6 zX|;X>?V}uubR!F8)A8dbde?kX4P93?8$ZrxW8DmP!5WXR0002D04aK{V}a#LylWUw z6=Ifd#4h0i8Kt=vNi~fo$4tDXz zdBY|q=E+^&qt}FrT-J8?YT{O&1u=p7*ETi=y&|^Z0W@RFw=|Pay_V9kamF+dtL@W} zN_~Go733OvFTozX%X*ojPU&5rr0yaw#uvp`D*;?m&P#m)^5UuM!VZ3H@|qJ%Cgqr>U;9N|wA@C%|3 z$_75WVqa?y#2Z?Ws8{l@WLVN#*Z9-@`o~iyyvkh_w2!raCH^uKwYb=)0rI_2BU*n^hJS9>VjpTRv~< z^(PGE>#fp~f^)I+2>u{x{-qa+LPZfXcWtZtc3YH^qVIlxYtuIU$zo$~IS)YOTauq> zXueNb7o6f7Cuy%QGfdf*kG1^30IsWF;}>0GC0_`u=T}8k>QuMEjC?oj=r^1z^6*HD zoid@~p~_3W<>pgmbp=}!0k#2Ad2ysr!px+R7z9Ch?B*|U3N5UzZtz+GFFuJFszZOr zDmDzREqYYgj#~e+j}O;e>r$Av5)j_s*pcLp>`W*esgol|7`(AJo6%GSvMbzCfv!W9 zv`5?FGTfC&Rc7KQ2;$Xf6dNfc;km%=>YPzeuH%MbWXgm>qUaixqX})&UG9S7kI-;| zk*Mg6nJ;jzF`)u^?dgYWMm0MROQ%?h9?|*z%xxgI>{p=6A&0eWnv8|zYb!a?P zPaR^7L{}mQS_6G_O`#w7c)aAJJ*TjujTPk7JQVQg(#8^pqI+rQhOh4G*_!I!c6M4MNZc~+ z)tt}Nxgp=3UcT8xu?w^^;Uq9NAaHC|k(WryjHO=3lC=Igj-56G4Q5ZX3!zJ@mRE=XcHssnvl>eh^LN3 zDh9004k~dk8HV={4nDvqNrfVYdHSj+i`vmJyA|@}tlX2LoO9{Q8D(*bCzs2>GEZ3j zQmRwgj#!&D2am17)CxbDn&kmyhNXNeQo1=w*~Umh##89iv)uhnxzXpk0B@D|hgz69 z5~wOv6}{FP*i)5jQ$dnG=iwpM5>^!O^SvcCXMlk8p?7SAUIbDgH0cnUgx+M-5g`~b z^sa`QgoK)e5Q<6n^|F zb^R8WD}Ek~RGy7|gOg5kqhM#J}plYid8qq4ZYiv_2`5tH!BKF^2pPSLx%BqA2O3DyI`Mj=nwL9|C-bqi-)yzX9 z`9AO8(Gzq%xA(kFP}<_mlKPhGf7_-#kc@MLH6I>?>(fHovNI=*$N+NyZsC! z&>@Pfx0CGS8#hG|_bt0j(IsjP9z@5t0oUvi}2r8b^$;#WeX2<@{7A=yiY^(vs-W^sKPq82c$RJbE zdo)Rl)4WnRJt)BZN*{#F7nRKQSmVHZd&gnV7#VrgxV&LvTPf+FPqAt8Gp?ZwQ9v$& zJ9!IIal_di@2&bA+PbDkc}ZU`T)joyavtvF@>LEW(})@yhgeiYz`m~COyg8co0$M& z7eQmpFEe?u`8DBrnTo-9_#T6m+%gMiIoF2lQt=kW$BoY1VI9`lHo)<+v!SWsTMKM>Plt0sab9)ft6SArr~$x#YDF2NiJLYcGZz&U-sN1QJl-5-|ag>bUEa9(SzG9fucbdi3Nv5Hia z9a=5@5#-=l+kHz`IM4;NmKe8$!j};E$7}r-K|5iwg*zaxN}GE8W(1>Tv4q+IQdkwP zQs(=rdP7_Y=MuXhZ>CPsNq2=n@quHY;+pQ(UYisL-(6x6zFqbbQ<*!!w)7#|+uvUj zVj1M6S}SWR>mPDEwUyg^vS*43&%dbWw%g7TmT~h>0MOrS*<_7-1bF$L1}Yg5d;EwJKZ;5o=buE9nOSY0KLtkXSXkGAz!pKQMrPrx*NV|%>#8#!IAMB zK$Q7J#ixyBOX*2e>gK$>$xM=t*LYQ*B|f4dI`qXA2~@R^d+=0WSi>`i`y6tAzn4M! zhDkS!non4$t*ok=ubLS*2aGXy*czdHgAM_4meKcH;?YkZn&yd&@f7_fRYA~0!L8c- zKK|Z;f-g1yU`hUiMVs3wGU{K+YqhiCeQGOmGvOo4d?|Htx&OgL=&%#q;q zVfD9I&MAJp)}^{H>Sp7Kzgr0_F7-0FH6?F*!TXr+HD-hKu&@`!21ow?WU-9X3z=Fm z#P}xCrJwX7eM@!?cXpR=_-g)cY2cD}Ym3X76`?_Ib$-}HGq1+kKgbnZZ~G@JNZvjj zyw69Gy-=h7*ZTaW7XMVAn!i@33EDC}eELV>1{H8PFzi#{3+mP1Ey2N#a68Kp#7Ld` zw9xU8)|Et^vKyw!`?LRK)n>{X;UmufZ{_q;v^% zkhQPY1^HM*{g}|V@}{kcA>RKMzqPaCOhT0}FhRe5m-%O0;oo`7Une6y_dVJoil0Ot zdyi^3=v)ZVj=pq1KzUnrf5&flQmpO!Dn;_Ug|syucz9tMt*_hW9NIZd+Qq91)7jD- zKU~xe{@9ffAgAEb&;gHMuWI0s!~X|pz}gUS3VE=>*Q#%lr5_Zw2F3Piw4Sc z@fpQ2<>_yfRKjC(o7?O}4dBdg9IuDS3Ew!nn9?!iQiL2@myD|v5^ax-zS-PT#^Yzp z0>B^v;YIH`T1z;NrDEoJqzq#;(r($Y#0yIjQWNL3P3AG7R;&zEJ&d&zu?R-3C|_KA z=u}+!(Xs|9!_AzS)KiI1`6?q=lTG>RbFjQAE+H#t`pno5?&CrEz+wJ(Iw`+7J0w=$ z3dE@7EesNKzplOz5^j9OX^z_z`dQ`Y%VnGPz27*#Gy6oz*^hYfIm3L0@Qs5og^9{4 z?)X&u>#@80f9X!PG7%mxBnE+K^6;+{M5_m;yn~D^Z$VK@V)Ha~YRSB*@|F-g424B9 zO+e&l;hCVj2akRZWfHZ^&52P{KW+3W&ngvu1}76nVcfo$Em~|#Kr~NFxCB->ix>bg z?}qFSBcK4^cHEXBefWG2zCi6Grm)|)-y~+gPY-)68b~_#GrN7d?EuhGit754zYQe89P~Z>rye(`+Ih# zGMB-_v)BB(#fpn6^iy{HhdLDmg)d`1_*aBpKF~lD-|MP5G*zxM8(c@es`yvJ%OE{P zxORe_F9rKXI8*7i&S4m0pA2t-f`p3s$N-y4UwmvuzUJAc@(WtUvTc6poKkq$GOC|A=`4XhI zo-mxGpEd8r31dW1yexx|SfP^Y4Vk@1YQG^aNS9PAr5o$wEAc241#xQ7#0OW1vUWm} zSI;h@<1{_ypgT@5T|6pwe$?zlqLt#JNX@v9vMq)T2ni?T3Ub>G1xr=g6wD8)Fr0*t zQv*Sxf}a6Mu2-<{KV)*8Dd0HlRSnW@h4q5nq@krn#x>$_c-pb^0K;5yEft`WdqEsp}ZrxVd%L{=JYm zKEtar!t_da9dKa}V7yyRa4ZA4C~hO?(n6#(6}JeV z3F#;HS3#Ek1%-3%b&OC!eqc18%0pEI`cOefKBJ zbo(~;aiqDq`Q7RPFla%UH*VzMSR&eWWWxdQ)*CrDHUuXr@nlhzRVsuD4=3c?=7s$4 z(~FkrunCC92{%z?eU9&MvPa@ouhere32~44<<>l-3hcjV(zCj~d`P=n_N$f6HVYG!CZu)5}9w|E4 z54>&o?gPjnVtz;C#f>;swd^o~h%oBv=9OP&+}s9ylE}Q{E+09Z!T%Yw&6`2R*BfT6 z3I#i_kv3B0SxY$kua+W{;v%~BbS;2-F@5nMZi~^r9nS;2BiPA~;YsNq)H7_`ej&Qu zUMJ<-1!8aF59@dE_U?7DRKdDvcJe#9at?0rA6w^-J~J7~iTb*?_Ko8MH`{A^_PRIL zzuksx7M8a+blWE~2RZnjma_l%JHf#SlbLTE#Wbz>t0h)4>HJ!*(%i8dS5MD9Sx1M~ z_ljGxQ(em^)OZg06NfbA=ZnMLO_0=gv(=VxnZOTljXl!h@ueyY6RpU2X<_(CU*m#5 z1(F*(DvExl{Hy%*Ob_?u&xR!^YGrG|z08;rS2f7&Ns4q7MJgsM(_8}Ue9hbhfqPU0 zB}~TB4HUWAhZWZa8|O$9Uv3VJZ`eO~w)C7zv~U_D!Z0pS6^Ml#b0|CuUTmp#1tR5d z*y7!H8&lujrp{Xt>}5MQ^uF4ai)WN3kYl8>d;yp=8)72UB*T66T#a)e^@toqCL6za z!-JyPkMRdgw5i26wVZ`NgSa0i_o0Xgakt@_^h5I3&TP=F7T1DiU!D#;t<;n+6S3^# ztg13=H_9DfE85XiiI*taIbXX1o_cS8WSs`cy}o&QT`Hd2r>HDHLiEuID86+N;w~NxaX&juVws7aCaCS30|?g} zmtCb3&ouU_Pu~eLeptoq8`wUqFz_!5lxs6iOe~uzE}2GRHC$L7^X*5*%XK63Cc)3$ zGL&E0&y-TlDWiUj_s|N&s!*XzvVrO76t~AR1~O6XM`_Q-dCp6aM8};M5yHAZ-(E3u zW0`httvPKa&!}Wi42SuJ_Ga3;E1v zpAs$BGE6-StJ5NU&7@n;f_!9dkBxONUumrb_ZOUZ`K7%X;x(#>M=g!*w=S+t@feMr z@j6*(5Z4xaM_a?E4qma^vSE|`Dl2Q+-yNVJR5(X)PYf9#Z!XHgtBSgjO5hI0Om76| zHHG^^zgI0YO6J>xnph&JRjIV`ajH%ZvOzk) zP|=sRmGQ0a9t~C{K3*t08<4;tl@}sU%G~V(>uJ=yigQ9%y6EZVcrZbu8>U$Q3Ma+& zogMADB}-cBeP0HR_Zvr=&+vJf`o5X3s&^;+f~6jn2^t1H(tx-qaRC9?A8=5y4np~mg zwv<4%{?((VJ85xmw-+KIeToql=A2XUG7YE+rhUUyO}0uw7c%_{?BjcY{E} zTh27(;@2};sqLn%kCyGN3ssq}FC+#dNaoTeZz-hfEBRP&#qKncC#H|6C2|o~UrFv4 zW_Igg3nHbL>W$+qUnGDY+akP8f~sU|EXNUe`edh18upZSE~JiMf0%)O8Z}x~;Iud8 zBxpA#wC<3`e4lakyAumrzy(sDN0iUassDb0)I;xS9CIY_< z%zh~FaRj|xJzND~kDoIIUP+%UAy=;K0PG%n( zBX(7T##X~?CMrdj8)VV0JT}tU(ZEN$YZP_3Va$*=$l5{d=dH@L>yB(ZrLBE$l(Rx3 zYCy_qX3R-Qv$31`?$xb{YToAfr~yl*GjHy-I^1jX18|R?_gWTM@rp-^!N#-7ywX53 zezGpU-#C^5FH-76jsUL}K1ocTBm7AJ?9G}gqTU_H@ zujXhxV5Hc|ZZK%9w>fBNJnCI-J+gi@E#?sm1rQDe-mw(qo%1Rm$QAXO|&4Bvw~7g=*oCd5#k6tBoY)k zdavZ+7MVRwYf-=fSs!y^JFL)pk!5z7L2gu~!Lt)13n05FdP`CPZ~F%Hvh?s735J2S zo=#;*P;D2{`sYN9xup2)oXE3ew~BbrH)8`o5n6p|ihPbfE}jW0^5sqJHTX4TW#R1> zsU)m~NkVk)66!i3uunFxYLF3gZ=dXPGZBh#tIO39H9!b#n1n(7sEM z&%y>0dF}6Gow^h<_Pu*oD!dbqQ46VoK6f{+BB*NQ|kM(xL9Znm~3MA$CSLNOsoHS@?L z9Pos{9KJa=&O{NU5Xx~9@S42{I>wB7u(RfnFCGmZV1$?VPDaJ=y2`F(dGF{QDlGIi zT(vFR_@YH>=U9fnXFvY$`n-p_rPT`bvmSMtfyoEzY6M42sUNTu9gI{9t-*xvj^bZj znNkI0*pcXpRCkL(2r#NL0dd{gYrG>s#BPFQdKGA&tBe$Dwny{#Bn^(|PO%q#{}~HX zP!RvA{hJk*qmkkhYP@F<8-BvsEsSX13lk>e(aJ}=2PvsP3VJeRYtqE^kZr5sfEqs!Tdd<=L@{i4PTh3v!{>9htU+npa z&I$8u`_pZW`rh?}&JIjQ@S%~>^4M4Z+RF4VuMz+3T!l`0gQKN|!J@KeeL7}JTmeaj zfiBQusK^7(01>^gsOb-H=Rt`31iQ+zOX(caCP9`F0az!o@Rv=B?sui+to-ZZtrlob zR;4n<rWNT70oji1cA_;ckXIh z{TeN4DFD;i`hvgA{nO{i zTD~^-8OtxLCN`9x#T+>s1r$VqhFE;cr`}jp`JB@(wwYy%{*2}8@H_sNwNZgDBAlfi|VR?C-{dGoT3*RxP)r2g)v zKnK@P%roQlk7g4F9Lmfo!y>8N8)@)R;_fbML0hNtzLEo1YV23uwOEoD?3Md>s$^rh zWj?2hYmQlkv};U=txR~bH@?OUhGq^i`<-Qq)25}1XiU2qyn)1g8Wx|Awh`IZf(7ac zdFPz65njVYlm>$!aVxo6c~q%_b|1E`iT_@y7x6%y3T8J#SYo=OY(yUr5@4TTH)|&5 z^V8YY%gHK@G<(y$A4Fd++sQ;1q=ws>hbEy6uHSo1_Z<8t!6R& zjUR%iSV9%S(Y5TJ7O&9`IZu~q(is1&;NY>DLe(281B4IuQ#|r;ij8O;mo=zX>2}T_ zw08caYBN5g+Hk-#xWMxx{o!}ouS2hT0TOK>)Yu�D1k^2)Zphg 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

      34xuju&UEz; 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 v>~O6-W|&gaSv~fjkZ?99b=^Y-N&W5e4EH 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 z ze4B@-s)~2H)Q`zG#v6)R*MMQ*sbP*W0>Ab!g`eZcMQ;5gH(|~CZ@8biL&2ZH3Mo7Z zJjLbO3NI17HAd2$R|BFgTy{;|L>^UoPz_APR#6on+S_|eu+DmXvBRv*W>{f z6HAd;PL0xt0$bi*@mBxwwsOch1~4TIW-a#@V2UaOn1WycQ|A8yOu2bkn@5vwLbQ`C zku4MU^dhhtD<`5hqHoi$M0UjvhP&jM*dk-OqA*X(|Tanv>7F??#C5Gc9(%pRYs*NCl`gtufZ0nPH7v zql1Pgu(>HIg||NHBSkUG%w5hjZa8J6-28=`iY&XurNok)|vX%$icF&C( zd86O8|MtV#R=CR#n?V&Y{!f>6{!c{RY?tG|*&@X{v1wM7|wW1cNq7?Z-{N*+BnWr*1xaV-fTPTEuq7*<@IV|b$R zz}!(2vYG@Mkt*Ng9B^W53@7#Oz#xCoW0sJTcv1WX| z8#B<0O^P8kyZISRCX%Xy?NWBC)9N8QsI|MznO@{?IF$tagCZ6-mH~deh=3oTc<8=E z{#=Tn%lJNU+Peg3sQ+{zsoi?YyEj7$8|UyzG@{HQGK7qv(tWf(j*qRWSvMeBGOTK1 zDJkw-;OBEo5cz@kVKF=dW?gLrE7)tMS^9r0<3APtgPQ=o8Aaf$OKnA)>DVb|=4#s6 z{7gr)u&^Qgk;ti+bSf9a^(Tu`A@=BNW0=K)oFJ4?$bZg+`1oL9NXtO9DkcdaZRpDT zguxA(6CE!PlC|@%V=q{0_0;@PZ)GL*nCAo-!!h(Eh3A#NDotiH8>W z|3;FNF%(Ol4%jX9V5VJQ;H(EXeP6Te3A4QvVt489!s@x3b_RB=4a-}(YjI4+>UUBm za&MX~Q!6*^JaSTdR$7@qXTOZ^qgsQHPj~KDJ162Q=_?rGcH^;}XYa-rsrCoHM@K(7 zdfVADS$J{u!SIW=M}QaO@E`ck5Mw`WUhx-g+ls3Vw`#M_&9$nXC;uk9Zf)AV-nh%c z-;L%!s9z_4+TZe*=)67l%zq31MOB`^u;%OIFvhQ|tx8^gtEk`bYwPSP(`q08JE!U! z|Lv&PwR!#`tm?xb{;!9BWmzS5{eE9G1t82M0uD&(2FV_6W{_EkN zEuZHvjC_6ggZ*{>r_1O3+@_Fq?+4r~I zUcF>excc0hQ{5kDWgSi0{Mf9@re1#ALC3rWog0FK9nOil9QmvSJjv?#xz$(wD@=dZ zm7gx26YLk-xm4s`w~I@al9JWDH{GUBDtDz7W$7C4D?4+3Sz7uU2B~WYz*P(5|C<0p CB!rg$ literal 0 HcmV?d00001 diff --git a/system_diagram.png b/system_diagram.png deleted file mode 100644 index e7ba1b6c564c798b71c51af2daa4c56314fe7684..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239025 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%0A&rArdDgY4=sHqlycKb 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�ZAp@{y5O~KU2aWZqySpG9 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 $QQM5^ez81cj%B#SzGnUpY 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 U9=&TqAuD)FQT2F|{Ds^91)wa+k^lez From b3c917133d182852f71541a8b896db6ca36644e7 Mon Sep 17 00:00:00 2001 From: anupkumar Date: Mon, 6 Apr 2026 12:28:22 +0300 Subject: [PATCH 27/58] system diagram updated --- .gitignore | 3 ++- docs/diagrams/system-flow-diagram.jpg | Bin 134834 -> 135137 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ed8ebf583..341e31ecb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -__pycache__ \ No newline at end of file +__pycache__ +.idea \ No newline at end of file diff --git a/docs/diagrams/system-flow-diagram.jpg b/docs/diagrams/system-flow-diagram.jpg index acdba43a817f821da2ec06a90860b490fec870e8..ceacdf9ac3e8d1f43f8f28358b474dad66fd0476 100644 GIT binary patch literal 135137 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

      FL@TB(!SbWUM6p3fw02m9Mbk(q)t^qY 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 *K#%zmqL|5$pLi)=ux=PD0;@HBa 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�D1k^2)Zphg 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

      34xuju&UEz; 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 v>~O6-W|&gaSv~fjkZ?99b=^Y-N&W5e4EH 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 z ze4B@-s)~2H)Q`zG#v6)R*MMQ*sbP*W0>Ab!g`eZcMQ;5gH(|~CZ@8biL&2ZH3Mo7Z zJjLbO3NI17HAd2$R|BFgTy{;|L>^UoPz_APR#6on+S_|eu+DmXvBRv*W>{f z6HAd;PL0xt0$bi*@mBxwwsOch1~4TIW-a#@V2UaOn1WycQ|A8yOu2bkn@5vwLbQ`C zku4MU^dhhtD<`5hqHoi$M0UjvhP&jM*dk-OqA*X(|Tanv>7F??#C5Gc9(%pRYs*NCl`gtufZ0nPH7v zql1Pgu(>HIg||NHBSkUG%w5hjZa8J6-28=`iY&XurNok)|vX%$icF&C( zd86O8|MtV#R=CR#n?V&Y{!f>6{!c{RY?tG|*&@X{v1wM7|wW1cNq7?Z-{N*+BnWr*1xaV-fTPTEuq7*<@IV|b$R zz}!(2vYG@Mkt*Ng9B^W53@7#Oz#xCoW0sJTcv1WX| z8#B<0O^P8kyZISRCX%Xy?NWBC)9N8QsI|MznO@{?IF$tagCZ6-mH~deh=3oTc<8=E z{#=Tn%lJNU+Peg3sQ+{zsoi?YyEj7$8|UyzG@{HQGK7qv(tWf(j*qRWSvMeBGOTK1 zDJkw-;OBEo5cz@kVKF=dW?gLrE7)tMS^9r0<3APtgPQ=o8Aaf$OKnA)>DVb|=4#s6 z{7gr)u&^Qgk;ti+bSf9a^(Tu`A@=BNW0=K)oFJ4?$bZg+`1oL9NXtO9DkcdaZRpDT zguxA(6CE!PlC|@%V=q{0_0;@PZ)GL*nCAo-!!h(Eh3A#NDotiH8>W z|3;FNF%(Ol4%jX9V5VJQ;H(EXeP6Te3A4QvVt489!s@x3b_RB=4a-}(YjI4+>UUBm za&MX~Q!6*^JaSTdR$7@qXTOZ^qgsQHPj~KDJ162Q=_?rGcH^;}XYa-rsrCoHM@K(7 zdfVADS$J{u!SIW=M}QaO@E`ck5Mw`WUhx-g+ls3Vw`#M_&9$nXC;uk9Zf)AV-nh%c z-;L%!s9z_4+TZe*=)67l%zq31MOB`^u;%OIFvhQ|tx8^gtEk`bYwPSP(`q08JE!U! z|Lv&PwR!#`tm?xb{;!9BWmzS5{eE9G1t82M0uD&(2FV_6W{_EkN zEuZHvjC_6ggZ*{>r_1O3+@_Fq?+4r~I zUcF>excc0hQ{5kDWgSi0{Mf9@re1#ALC3rWog0FK9nOil9QmvSJjv?#xz$(wD@=dZ zm7gx26YLk-xm4s`w~I@al9JWDH{GUBDtDz7W$7C4D?4+3Sz7uU2B~WYz*P(5|C<0p CB!rg$ From 6f876e15663010d9c76cf94aca340830a3dc640b Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Tue, 14 Apr 2026 13:02:56 +0300 Subject: [PATCH 28/58] Address TA feedback: make vector clocks service-driven Move event ordering from the orchestrator into the backend services so vector clocks are used to make real causal-delivery decisions rather than being inert metadata. - transaction_verification chains ValidateCardFormat internally after ValidateItems, calls SUG.PrecomputeSuggestions directly, and forwards ValidateCardFormat's VC to fraud_detection - fraud_detection gates CheckCardFraud on both CheckUserFraud (local) and the forwarded ValidateCardFormat VC via _try_run_e() - suggestions gates FinalizeSuggestions on both PrecomputeSuggestions (local) and the forwarded CheckCardFraud VC via _try_run_g() - orchestrator now only initializes services, kicks off the two root events, and blocks on SUG.AwaitPipelineResult() - per-order threading.Lock() makes the VC read-modify-write atomic, closing the race on concurrent ValidateItems/ValidateUserData - ForwardVC RPC + VCForward message added to fraud_detection and suggestions protos; AwaitPipelineResult RPC added to suggestions - failure paths propagate explicit ForwardVC(success=False) markers so downstream services can short-circuit gated events - README rewritten to describe the service-driven flow; full redesign write-up at docs/vector-clock-redesign.md --- README.md | 62 +++- docs/vector-clock-redesign.md | 177 ++++++++++ fraud_detection/src/app.py | 195 ++++++++++- orchestrator/src/app.py | 303 ++++++------------ suggestions/src/app.py | 237 +++++++++++++- transaction_verification/src/app.py | 267 ++++++++++----- .../pb/fraud_detection/fraud_detection.proto | 16 +- .../pb/fraud_detection/fraud_detection_pb2.py | 12 +- .../fraud_detection/fraud_detection_pb2.pyi | 14 + .../fraud_detection_pb2_grpc.py | 55 +++- utils/pb/suggestions/suggestions.proto | 25 ++ utils/pb/suggestions/suggestions_pb2.py | 16 +- utils/pb/suggestions/suggestions_pb2.pyi | 32 ++ utils/pb/suggestions/suggestions_pb2_grpc.py | 88 +++++ vc_investigation_results.txt | 57 ++++ 15 files changed, 1201 insertions(+), 355 deletions(-) create mode 100644 docs/vector-clock-redesign.md create mode 100644 vc_investigation_results.txt diff --git a/README.md b/README.md index 7da4b2c2f..df1d18593 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ docker compose logs --no-color --tail 200 order_queue order_executor_1 order_exe Point out: - `vc=[...]` in the 3 backend services +- `event=ForwardVC source=...` lines showing services forwarding vector clocks to each other - `clear_broadcast_sent final_vc=[...]` in the orchestrator - `action=enqueue` and `action=dequeue` in the queue - `executing order=` in exactly one executor replica @@ -75,30 +76,47 @@ docker compose down ``` ## Checkpoint 2 deliverables in this repo -This repository now contains the implementation and documentation required for Checkpoint 2: +This repository contains the implementation and documentation required for Checkpoint 2: -- vector clocks across `transaction_verification`, `fraud_detection`, and `suggestions` +- vector clocks across `transaction_verification`, `fraud_detection`, and `suggestions`, driven by the services themselves rather than by the orchestrator - order queuing plus 3 replicated order executors - leader election and mutual exclusion for queue consumption -- logs that expose vector-clock values, queue actions, and executor leadership +- logs that expose vector-clock values, inter-service `ForwardVC` events, queue actions, and executor leadership - a reusable verification script at `scripts/checkpoint2-checks.ps1` - the required vector-clocks diagram, leader-election diagram, and system-model write-up ## Vector clocks The vector clock has 3 positions in the fixed service order `[TV, FD, SUG]`. +### Service-driven event ordering +Event ordering is handled by the microservices, not by the orchestrator. The orchestrator only kicks off the two root events on `transaction_verification` and then blocks on `suggestions` for the final pipeline result. All other events, and all causal dependencies between them, are driven by the backend services through inter-service gRPC calls and vector-clock gating. + Each backend service: -- stores per-order state after `InitOrder` -- merges the incoming vector clock with its local vector clock -- increments its own component before logging and replying +- stores per-order state after `InitOrder`, including a `threading.Lock()` that makes the per-order vector-clock update atomic +- on every event, merges the incoming vector clock with its local vector clock, then increments its own component before logging and replying +- forwards its updated vector clock to the next service over a `ForwardVC` RPC when that event causally precedes work on another service +- gates events that depend on multiple causal predecessors until every predecessor's vector clock has arrived - clears the order only if `local_vc <= final_vc` -The diagram below shows one successful execution observed in this repository. The orchestrator starts `ValidateItems` and `ValidateUserData` together, so their relative order may swap between runs. The diagram documents one valid run captured from the logs. +### Event-to-service mapping and causal dependencies +| Event | Service | Depends on | Trigger | +| --- | --- | --- | --- | +| `ValidateUserData` | TV | (root) | orchestrator | +| `ValidateItems` | TV | (root) | orchestrator | +| `CheckUserFraud` | FD | `ValidateUserData` | TV calls FD after `ValidateUserData` | +| `PrecomputeSuggestions` | SUG | `ValidateItems` | TV calls SUG after `ValidateItems` | +| `ValidateCardFormat` | TV | `ValidateItems` | TV chains internally after `ValidateItems`, then `ForwardVC` to FD | +| `CheckCardFraud` | FD | `CheckUserFraud` AND `ValidateCardFormat` | FD fires internally when both predecessor VCs are present, then `ForwardVC` to SUG | +| `FinalizeSuggestions` | SUG | `PrecomputeSuggestions` AND `CheckCardFraud` | SUG fires internally when both predecessor VCs are present | + +FD uses `_try_run_e()` to gate `CheckCardFraud` on the presence of both `d_done` (local `CheckUserFraud`) and `c_received` (forwarded VC from TV). SUG uses `_try_run_g()` to gate `FinalizeSuggestions` on both `f_done` (local `PrecomputeSuggestions`) and `e_received` (forwarded VC from FD). These gates are where the vector clocks actually make a decision instead of being inert metadata. +### Diagram ![Vector clocks diagram](./docs/diagrams/vector-clocks.svg) -Observed successful event sequence: +The orchestrator starts `ValidateItems` and `ValidateUserData` together, so their relative order may swap between runs. The diagram and the table below document one valid run captured from the logs. +### Observed successful event sequence | Step | Service | Event | Vector clock | | --- | --- | --- | --- | | 1 | Transaction verification | `ValidateUserData` | `[1, 0, 0]` | @@ -109,11 +127,18 @@ Observed successful event sequence: | 6 | Fraud detection | `CheckCardFraud` | `[3, 2, 0]` | | 7 | Suggestions | `FinalizeSuggestions` | `[3, 2, 2]` | -Bonus behavior: -- the orchestrator merges all completed event clocks into one `final_vc` +### Failure propagation +When an event fails or a prerequisite cannot complete, the responsible service forwards a failure marker (`ForwardVC` with `success=False`) to every downstream service that would have waited for it. FD and SUG record the failed prerequisite and short-circuit any gated event they would otherwise have run. The orchestrator learns about the failure through `SUG.AwaitPipelineResult()` and returns `"status": "Order Rejected"` with a human-readable reason. + +### Final clear +- each service tracks the maximum vector clock it has observed locally +- the orchestrator merges every completed event clock into one `final_vc` - the orchestrator broadcasts `ClearOrder(final_vc)` to all 3 services - each service clears only when its local vector clock is not ahead of the final one +### Deep-dive documentation +A complete write-up of the redesign, including the before/after architecture, the inter-service call flow, and the files that were changed, is available at [docs/vector-clock-redesign.md](./docs/vector-clock-redesign.md). + ## Leader election and mutual exclusion The order execution tier uses 3 replicas: `order_executor_1`, `order_executor_2`, and `order_executor_3`. @@ -139,8 +164,8 @@ The system is a small distributed online-bookstore workflow: ![Architecture diagram](./docs/diagrams/architecture-diagram.jpg) - `frontend` serves the browser UI -- `orchestrator` accepts checkout requests over HTTP and coordinates the workflow -- `transaction_verification`, `fraud_detection`, and `suggestions` are gRPC services that participate in the vector-clock event flow +- `orchestrator` accepts checkout requests over HTTP, initializes the backend services, kicks off the two root events, and blocks on the pipeline result +- `transaction_verification`, `fraud_detection`, and `suggestions` are gRPC services that drive the vector-clock event flow among themselves - `order_queue` stores approved orders in FIFO order - `order_executor_1..3` form a replicated execution tier that elects a leader and consumes approved orders @@ -151,16 +176,18 @@ The following diagram shows the end-to-end flow of an order through the system: ### Communication model - the browser communicates with the orchestrator over HTTP -- the orchestrator communicates with backend services over synchronous gRPC calls +- the orchestrator communicates with backend services over synchronous gRPC calls, but only for init, two root events, the pipeline result, and the final clear broadcast +- the 3 backend services communicate with each other over gRPC through `ForwardVC` and direct event RPCs; this is where the vector clocks actually flow - executor replicas communicate with each other over gRPC for election, coordinator announcements, and heartbeats - the order queue is a separate gRPC service used by the orchestrator and the current leader - all services run in Docker Compose on one virtual network, but they still behave as separate processes with separate local state ### Concurrency and ordering -- the orchestrator starts multiple validation/fraud/suggestion steps in parallel threads +- the orchestrator starts the two root validation events in parallel - there is no global clock -- ordering is captured by vector clocks rather than wall-clock timestamps -- approval requires the full dependency chain to complete successfully +- ordering is captured by vector clocks rather than wall-clock timestamps, and causal delivery is enforced by the services themselves through `ForwardVC` gating +- per-order `threading.Lock()` in every backend service protects the vector-clock read-modify-write against concurrent gRPC threads +- approval requires the full causal dependency chain to complete successfully - queue consumption is serialized by leadership: only one replica is allowed to dequeue at a time ### Failure assumptions @@ -172,7 +199,7 @@ The following diagram shows the end-to-end flow of an order through the system: ### Safety properties - every order gets a unique `orderId` from the orchestrator -- vector-clock logs expose causal relationships between backend events +- vector-clock logs expose causal relationships between backend events, including inter-service `ForwardVC` hops - approved orders are enqueued once by the orchestrator - only the elected leader dequeues and executes an approved order - the clear broadcast uses the merged final vector clock so services do not clear too early @@ -205,4 +232,5 @@ Prepared input files: The required documentation assets are also available in `docs/`: - `docs/diagrams/vector-clocks.svg` - `docs/diagrams/leader-election.svg` +- `docs/vector-clock-redesign.md` - `docs/README.md` diff --git a/docs/vector-clock-redesign.md b/docs/vector-clock-redesign.md new file mode 100644 index 000000000..541c37f4d --- /dev/null +++ b/docs/vector-clock-redesign.md @@ -0,0 +1,177 @@ +# Vector Clock Redesign: From Centralized to Service-Driven Ordering + +## TA feedback that prompted this change + +> Grade: 7.00 / 11.5 +> +> Feedback: "+ 0.5 bonus point. Vector clocks are not meaningfully implemented. +> Event ordering should be handled by the microservices not centrally through the orchestrator." + +## What was wrong (before this change) + +The original implementation had a star topology where the orchestrator was the sole coordinator of all event ordering. The three backend services (transaction_verification, fraud_detection, suggestions) were passive RPC endpoints that never communicated with each other. + +### Centralized ordering in the orchestrator + +The orchestrator encoded the full partial order as a list of Python threads with explicit `threading.Event` waits: + +```python +# OLD orchestrator code (removed) +workers = [ + threading.Thread(target=run_event, args=("a", [], [], tv_validate_items)), + threading.Thread(target=run_event, args=("b", [], [], tv_validate_user_data)), + threading.Thread(target=run_event, args=("c", ["a"], ["a"], tv_validate_card_format)), + threading.Thread(target=run_event, args=("d", ["b"], ["b"], fd_check_user_fraud)), + threading.Thread(target=run_event, args=("e", ["c", "d"], ["c", "d"], fd_check_card_fraud)), + threading.Thread(target=run_event, args=("f", ["a"], ["a"], sug_precompute)), + threading.Thread(target=run_finalize), +] +``` + +The dependency graph (`"c" after ["a"]`, `"e" after ["c", "d"]`, etc.) lived entirely in the orchestrator. The orchestrator also computed every downstream event's input VC via `merged_from(*input_steps)`. If you deleted the VC plumbing entirely, the system would still produce events in the exact same order because `threading.Event` waits enforced it. + +### VCs were inert metadata + +No service ever inspected an incoming VC to make a decision. Each handler blindly merged, ticked, logged, and returned. The VC was documentation, not a mechanism. + +### Race condition + +Events `a` (ValidateItems) and `b` (ValidateUserData) both ran on transaction_verification in parallel, but the read-modify-write of the per-order local clock (`state["vc"]`) had no lock. Under concurrent gRPC threads, both events could read `[0,0,0]`, tick to `[1,0,0]`, and write back the same value, violating the vector-clock invariant that two events on the same process must be totally ordered. + +## What changed (this commit) + +### Architecture overview + +The orchestrator's role shrank from "workflow engine" to "pipeline launcher": + +``` +BEFORE: Orchestrator dispatches all 7 events, enforces ordering via threading + +AFTER: Orchestrator only does: + 1. Init all 3 services + 2. Kick off root events a, b on TV + 3. Block on SUG.AwaitPipelineResult() + 4. Enqueue approved orders + 5. Broadcast ClearOrder +``` + +The microservices now drive the event flow themselves by making inter-service gRPC calls and using vector clocks for causal gating. + +### Inter-service call flow (successful order) + +``` +Orchestrator + | + |--- TV.ValidateItems(vc=[0,0,0]) --> event a + |--- TV.ValidateUserData(vc=[0,0,0]) --> event b + | + | TV (internally, after a): + | |--- TV.ValidateCardFormat --> event c (called internally, same process) + | |--- SUG.PrecomputeSuggestions(vc) --> event f (TV calls SUG directly) + | |--- FD.ForwardVC(source="c", vc) --> forwards c's VC to FD + | + | TV (internally, after b): + | |--- FD.CheckUserFraud(vc) --> event d (TV calls FD directly) + | + | FD (causal gating): + | Waits for BOTH d (local) AND c (forwarded from TV) + | |--- event e: CheckCardFraud --> runs when both prerequisites met + | |--- SUG.ForwardVC(source="e", vc) --> forwards e's VC to SUG + | + | SUG (causal gating): + | Waits for BOTH f (local) AND e (forwarded from FD) + | |--- event g: FinalizeSuggestions --> runs when both prerequisites met + | |--- signals pipeline_done + | + |<-- SUG.AwaitPipelineResult() --> orchestrator collects result +``` + +### Where VCs are now meaningful + +In the old code, VCs were never used for decisions. Now they are: + +1. **FD gates event e on causal readiness.** FD stores `d_done`/`d_vc` and `c_received`/`c_vc`. The method `_try_run_e()` only fires when both are present. It merges the two VCs before processing, which is a real vector-clock merge across two causal predecessors from different services. + +2. **SUG gates event g on causal readiness.** SUG stores `f_done`/`f_vc` and `e_received`/`e_vc`. The method `_try_run_g()` only fires when both are present. Same merge pattern. + +3. **Failure propagation uses VCs.** When an event fails (e.g., `b` rejects for terms not accepted), TV forwards the failure VC to FD via `ForwardVC(source="d", success=False)`. FD records it as a failed prerequisite and propagates to SUG. The VC still tracks what happened up to the failure point. + +### Per-order locking (race condition fix) + +Every service now stores a `threading.Lock()` per order in the state dict. All VC read-modify-write operations are wrapped: + +```python +with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc +``` + +The `tick()` function was also changed to return a new list instead of mutating in place: + +```python +def tick(vc, idx): + vc = list(vc) # copy first + vc[idx] += 1 + return vc +``` + +## Files changed + +### Proto definitions + +| File | Change | +|---|---| +| `utils/pb/fraud_detection/fraud_detection.proto` | Added `ForwardVC` RPC and `VCForward` message | +| `utils/pb/suggestions/suggestions.proto` | Added `ForwardVC` RPC, `VCForward` message, `AwaitPipelineResult` RPC, `PipelineResultRequest`/`PipelineResultResponse` messages | +| `utils/pb/fraud_detection/fraud_detection_pb2*.py` | Regenerated from proto | +| `utils/pb/suggestions/suggestions_pb2*.py` | Regenerated from proto | + +### Service implementations + +| File | Change | +|---|---| +| `transaction_verification/src/app.py` | Added per-order lock. After event a: chains event c internally, calls SUG.PrecomputeSuggestions, forwards c's VC to FD. After event b: calls FD.CheckUserFraud. Imports FD and SUG stubs for inter-service calls. | +| `fraud_detection/src/app.py` | Added per-order lock. Added `ForwardVC` handler for receiving c's VC from TV. Added `_try_run_e()` causal gating: runs CheckCardFraud only when both d (local) and c (forwarded) are complete. After e: forwards e's VC to SUG. Imports SUG stubs. | +| `suggestions/src/app.py` | Added per-order lock. Added `ForwardVC` handler for receiving e's VC from FD. Added `_try_run_g()` causal gating: runs FinalizeSuggestions only when both f (local) and e (forwarded) are complete. Added `AwaitPipelineResult` RPC with `threading.Event` to block until pipeline completion. | +| `orchestrator/src/app.py` | Removed all `threading.Event`-based dependency management, `run_event()`, `run_finalize()`, `merged_from()`, and the 7-worker thread pool. Now only triggers root events a and b on TV, then calls `SUG.AwaitPipelineResult()`. Rejection responses changed from HTTP 400 to HTTP 200 with `"status": "Order Rejected"`. | + +## Observed vector clocks (successful order) + +The partial order and VC values remain the same as before, but the mechanism is now distributed: + +| Step | Event | Service | VC | Triggered by | +|---|---|---|---|---| +| 1 | ValidateItems (a) | TV | `[1, 0, 0]` | Orchestrator (root) | +| 2 | ValidateUserData (b) | TV | `[2, 0, 0]` | Orchestrator (root) | +| 3 | ValidateCardFormat (c) | TV | `[3, 0, 0]` | TV internally (after a) | +| 4 | CheckUserFraud (d) | FD | `[2, 1, 0]` | TV calls FD (after b) | +| 5 | PrecomputeSuggestions (f) | SUG | `[1, 0, 1]` | TV calls SUG (after a) | +| 6 | CheckCardFraud (e) | FD | `[3, 2, 0]` | FD internally (gated on d + c's VC from TV) | +| 7 | FinalizeSuggestions (g) | SUG | `[3, 2, 2]` | SUG internally (gated on f + e's VC from FD) | + +Note: steps 1-2 run in parallel on TV so their relative order may swap. Steps 3-5 also run concurrently across services. Steps 6 and 7 are causally ordered by the VCs themselves. + +## Error propagation paths + +| Scenario | What happens | +|---|---| +| a fails (empty items) | TV forwards `source="a"` failure to FD and SUG. FD marks c as failed, skips e, forwards e failure to SUG. SUG marks f and e as failed, skips g, pipeline result = failure. | +| b fails (terms not accepted) | TV forwards `source="d"` failure to FD (d will never run). FD marks d as failed, skips e when c arrives, forwards e failure to SUG. SUG receives e failure, waits for f, skips g, pipeline result = failure. | +| e fails (fraudulent card) | FD forwards `source="e"` failure to SUG. SUG waits for f, skips g, pipeline result = failure. | + +## How to verify the inter-service flow in logs + +```bash +docker compose logs --no-color --tail 200 transaction_verification fraud_detection suggestions \ + | grep -E "event=(ForwardVC|CheckUserFraud|CheckCardFraud|PrecomputeSuggestions|FinalizeSuggestions)" +``` + +Look for: +- `[FD] event=ForwardVC source=c` -- TV forwarded c's VC to FD +- `[FD] event=CheckCardFraud` -- FD ran e after causal gating on d + c +- `[SUG] event=ForwardVC source=e` -- FD forwarded e's VC to SUG +- `[SUG] event=FinalizeSuggestions` -- SUG ran g after causal gating on f + e + +The orchestrator logs should show only `starting_root_events`, not individual event dispatches. diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index 28f4c1328..caddb0877 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -9,9 +9,16 @@ ) sys.path.insert(0, fraud_detection_grpc_path) +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/suggestions") +) +sys.path.insert(0, suggestions_grpc_path) + import grpc import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc SERVICE_INDEX = 1 # [transaction_verification, fraud_detection, suggestions] @@ -25,6 +32,7 @@ def merge_vc(local_vc, incoming_vc): def tick(vc, idx): + vc = list(vc) vc[idx] += 1 return vc @@ -38,6 +46,22 @@ def get_order_state(order_id: str): return orders.get(order_id) +def forward_to_sug(order_id, source_event, vc, success, message): + try: + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + req = suggestions.VCForward( + order_id=order_id, + source_event=source_event, + vc=suggestions.VectorClock(values=vc), + success=success, + message=message, + ) + stub.ForwardVC(req, timeout=10.0) + except Exception as e: + print(f"[FD] order={order_id} forward_to_sug_error source={source_event} error={e}") + + class FraudDetectionService(fraud_detection_grpc.FraudDetectionServiceServicer): def InitOrder(self, request, context): order = request.order @@ -46,6 +70,18 @@ def InitOrder(self, request, context): orders[order.order_id] = { "order": order, "vc": [0, 0, 0], + "lock": threading.Lock(), + # Causal gating state for event e (CheckCardFraud). + # e needs BOTH d (CheckUserFraud, local) AND c (ValidateCardFormat, from TV). + "d_done": False, + "d_vc": None, + "d_success": True, + "d_message": "", + "c_received": False, + "c_vc": None, + "c_success": True, + "c_message": "", + "e_triggered": False, } print(f"[FD] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") @@ -56,8 +92,67 @@ def InitOrder(self, request, context): vc=fraud_detection.VectorClock(values=[0, 0, 0]), ) + def _try_run_e(self, order_id, state): + """Check if both prerequisites for event e are met. If so, run CheckCardFraud.""" + with state["lock"]: + if state["e_triggered"]: + return + if not (state["d_done"] and state["c_received"]): + return + state["e_triggered"] = True + + d_vc = state["d_vc"] + d_success = state["d_success"] + d_message = state["d_message"] + c_vc = state["c_vc"] + c_success = state["c_success"] + c_message = state["c_message"] + + # If either prerequisite failed, propagate failure without running e. + if not d_success: + print(f"[FD] order={order_id} event=CheckCardFraud skipped (d failed: {d_message})") + forward_to_sug(order_id, "e", d_vc, False, d_message) + return + if not c_success: + print(f"[FD] order={order_id} event=CheckCardFraud skipped (c failed: {c_message})") + forward_to_sug(order_id, "e", c_vc, False, c_message) + return + + # Both d and c succeeded: merge their VCs and run e. + merged = merge_vc(d_vc, c_vc) + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, merged) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + # Perform the card-fraud check. + card_digits = extract_card_digits(state["order"].card_number) + + success = True + message = "Card fraud check passed." + + if len(card_digits) != 16: + success = False + message = "Invalid card number." + elif card_digits.startswith("0000") or card_digits.endswith("0000"): + success = False + message = "Suspicious card number pattern." + + print( + f"[FD] order={order_id} event=CheckCardFraud " + f"vc={vc} success={success}" + ) + + # Forward e's result to SUG (SUG needs e's VC to gate event g). + forward_to_sug(order_id, "e", vc, success, message) + def CheckUserFraud(self, request, context): - state = get_order_state(request.order_id) + """Event d: called by TV after event b. After processing, checks if c's VC + has arrived so that event e can run.""" + order_id = request.order_id + state = get_order_state(order_id) if state is None: return fraud_detection.EventResponse( success=False, @@ -66,28 +161,95 @@ def CheckUserFraud(self, request, context): ) incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc user_name = state["order"].user_name success = "fraud" not in user_name.lower() message = "User fraud check passed." if success else "Suspicious user name." print( - f"[FD] order={request.order_id} event=CheckUserFraud " + f"[FD] order={order_id} event=CheckUserFraud " f"vc={vc} success={success}" ) + # Record d's result and attempt to trigger e. + with state["lock"]: + state["d_done"] = True + state["d_vc"] = vc + state["d_success"] = success + state["d_message"] = message + + self._try_run_e(order_id, state) + return fraud_detection.EventResponse( success=success, message=message, vc=fraud_detection.VectorClock(values=vc), ) + def ForwardVC(self, request, context): + """Receive a forwarded VC from another microservice (TV forwards c's VC here).""" + order_id = request.order_id + source_event = request.source_event + incoming_vc = list(request.vc.values) + success = request.success + message = request.message + + state = get_order_state(order_id) + if state is None: + return fraud_detection.EventResponse( + success=False, + message="Order not found in fraud service.", + vc=fraud_detection.VectorClock(values=[0, 0, 0]), + ) + + print( + f"[FD] order={order_id} event=ForwardVC source={source_event} " + f"vc={incoming_vc} success={success}" + ) + + if source_event == "c": + with state["lock"]: + state["c_received"] = True + state["c_vc"] = incoming_vc + state["c_success"] = success + state["c_message"] = message + + self._try_run_e(order_id, state) + elif source_event == "a": + # a failed: no c will ever come, so we treat c as failed + with state["lock"]: + state["c_received"] = True + state["c_vc"] = incoming_vc + state["c_success"] = False + state["c_message"] = message + + self._try_run_e(order_id, state) + elif source_event == "d": + # b failed: TV will not call CheckUserFraud, so d is done+failed + with state["lock"]: + state["d_done"] = True + state["d_vc"] = incoming_vc + state["d_success"] = success + state["d_message"] = message + + self._try_run_e(order_id, state) + + return fraud_detection.EventResponse( + success=True, + message="VC forwarded.", + vc=fraud_detection.VectorClock(values=incoming_vc), + ) + def CheckCardFraud(self, request, context): - state = get_order_state(request.order_id) + """Event e: kept as an RPC for backward compat, but now triggered internally.""" + order_id = request.order_id + state = get_order_state(order_id) if state is None: return fraud_detection.EventResponse( success=False, @@ -96,10 +258,12 @@ def CheckCardFraud(self, request, context): ) incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc card_digits = extract_card_digits(state["order"].card_number) @@ -114,7 +278,7 @@ def CheckCardFraud(self, request, context): message = "Suspicious card number pattern." print( - f"[FD] order={request.order_id} event=CheckCardFraud " + f"[FD] order={order_id} event=CheckCardFraud " f"vc={vc} success={success}" ) @@ -138,8 +302,9 @@ def ClearOrder(self, request, context): vc=fraud_detection.VectorClock(values=[0, 0, 0]), ) - local_vc = state["vc"] - can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + with state["lock"]: + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) if can_clear: del orders[order_id] @@ -177,4 +342,4 @@ def serve(): if __name__ == "__main__": - serve() \ No newline at end of file + serve() diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 46f75834b..002003e9f 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -71,6 +71,8 @@ def build_order_kwargs( } +# --- Service init calls (unchanged) --- + def init_fraud_service(order_id, order_kwargs): with grpc.insecure_channel("fraud_detection:50051") as channel: stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) @@ -98,92 +100,55 @@ def init_suggestions_service(order_id, order_kwargs): return stub.InitOrder(request, timeout=5.0) -def enqueue_order(order_id, order_kwargs): - with grpc.insecure_channel("order_queue:50054") as channel: - stub = order_queue_grpc.OrderQueueServiceStub(channel) - request = order_queue.EnqueueRequest( - order=order_queue.OrderData( - order_id=order_id, - user_name=order_kwargs["user_name"], - user_contact=order_kwargs["user_contact"], - card_number=order_kwargs["card_number"], - expiration_date=order_kwargs["expiration_date"], - cvv=order_kwargs["cvv"], - item_count=order_kwargs["item_count"], - terms_accepted=order_kwargs["terms_accepted"], - ) - ) - return stub.Enqueue(request, timeout=5.0) - - -def tv_validate_items(order_id, vc): - with grpc.insecure_channel("transaction_verification:50052") as channel: - stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - request = transaction_verification.EventRequest( - order_id=order_id, - vc=transaction_verification.VectorClock(values=vc), - ) - return stub.ValidateItems(request, timeout=5.0) - +# --- Root event calls: only a and b --- -def tv_validate_user_data(order_id, vc): +def tv_validate_items(order_id): with grpc.insecure_channel("transaction_verification:50052") as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - request = transaction_verification.EventRequest( + req = transaction_verification.EventRequest( order_id=order_id, - vc=transaction_verification.VectorClock(values=vc), + vc=transaction_verification.VectorClock(values=[0, 0, 0]), ) - return stub.ValidateUserData(request, timeout=5.0) + return stub.ValidateItems(req, timeout=15.0) -def tv_validate_card_format(order_id, vc): +def tv_validate_user_data(order_id): with grpc.insecure_channel("transaction_verification:50052") as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - request = transaction_verification.EventRequest( - order_id=order_id, - vc=transaction_verification.VectorClock(values=vc), - ) - return stub.ValidateCardFormat(request, timeout=5.0) - - -def fd_check_user_fraud(order_id, vc): - with grpc.insecure_channel("fraud_detection:50051") as channel: - stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) - request = fraud_detection.EventRequest( + req = transaction_verification.EventRequest( order_id=order_id, - vc=fraud_detection.VectorClock(values=vc), + vc=transaction_verification.VectorClock(values=[0, 0, 0]), ) - return stub.CheckUserFraud(request, timeout=5.0) + return stub.ValidateUserData(req, timeout=15.0) -def fd_check_card_fraud(order_id, vc): - with grpc.insecure_channel("fraud_detection:50051") as channel: - stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) - request = fraud_detection.EventRequest( - order_id=order_id, - vc=fraud_detection.VectorClock(values=vc), - ) - return stub.CheckCardFraud(request, timeout=5.0) - +# --- Pipeline result collection --- -def sug_precompute(order_id, vc): +def await_pipeline_result(order_id): with grpc.insecure_channel("suggestions:50053") as channel: stub = suggestions_grpc.SuggestionsServiceStub(channel) - request = suggestions.EventRequest( - order_id=order_id, - vc=suggestions.VectorClock(values=vc), - ) - return stub.PrecomputeSuggestions(request, timeout=5.0) + req = suggestions.PipelineResultRequest(order_id=order_id) + return stub.AwaitPipelineResult(req, timeout=30.0) -def sug_finalize(order_id, vc): - with grpc.insecure_channel("suggestions:50053") as channel: - stub = suggestions_grpc.SuggestionsServiceStub(channel) - request = suggestions.EventRequest( - order_id=order_id, - vc=suggestions.VectorClock(values=vc), +# --- Enqueue and clear (unchanged) --- + +def enqueue_order(order_id, order_kwargs): + with grpc.insecure_channel("order_queue:50054") as channel: + stub = order_queue_grpc.OrderQueueServiceStub(channel) + request = order_queue.EnqueueRequest( + order=order_queue.OrderData( + order_id=order_id, + user_name=order_kwargs["user_name"], + user_contact=order_kwargs["user_contact"], + card_number=order_kwargs["card_number"], + expiration_date=order_kwargs["expiration_date"], + cvv=order_kwargs["cvv"], + item_count=order_kwargs["item_count"], + terms_accepted=order_kwargs["terms_accepted"], + ) ) - return stub.FinalizeSuggestions(request, timeout=5.0) + return stub.Enqueue(request, timeout=5.0) def clear_fraud_service(order_id, final_vc): @@ -305,7 +270,7 @@ def checkout(): terms_accepted=terms_accepted, ) - # Initialization phase + # --- Phase 1: Initialize all backend services --- try: init_tv = init_transaction_service(order_id, order_kwargs) init_fd = init_fraud_service(order_id, order_kwargs) @@ -337,166 +302,70 @@ def checkout(): print(f"[ORCH] order={order_id} initialization_complete") - # Event-flow state - cancelled = threading.Event() - - done = { - "a": threading.Event(), # ValidateItems - "b": threading.Event(), # ValidateUserData - "c": threading.Event(), # ValidateCardFormat - "d": threading.Event(), # CheckUserFraud - "e": threading.Event(), # CheckCardFraud - "f": threading.Event(), # PrecomputeSuggestions - "g": threading.Event(), # FinalizeSuggestions - } - - state = { - "event_vcs": {}, - "final_vc": [0, 0, 0], - "books": [], - "failure_kind": None, # "event_failure" or "internal_error" - "failed_step": None, - "failure_message": None, - } - lock = threading.Lock() - - def store_event_result(step, response): - vc = list(response.vc.values) - with lock: - state["event_vcs"][step] = vc - state["final_vc"] = merge_vcs(state["final_vc"], vc) - - def merged_from(*steps): - with lock: - vcs = [state["event_vcs"][step] for step in steps if step in state["event_vcs"]] - if not vcs: - return [0, 0, 0] - return merge_vcs(*vcs) - - def record_event_failure(step, response): - with lock: - if state["failure_kind"] is None: - state["failure_kind"] = "event_failure" - state["failed_step"] = step - state["failure_message"] = response.message - state["final_vc"] = merge_vcs( - state["final_vc"], list(response.vc.values) - ) - cancelled.set() - print(f"[ORCH] order={order_id} step={step} success=False message={response.message}") - - def record_internal_failure(step, message, fallback_vc): - with lock: - if state["failure_kind"] is None: - state["failure_kind"] = "internal_error" - state["failed_step"] = step - state["failure_message"] = message - state["final_vc"] = merge_vcs(state["final_vc"], fallback_vc) - cancelled.set() - print(f"[ORCH] order={order_id} step={step} internal_error={message}") - - def run_event(step, prereqs, input_steps, rpc_func): - for prereq in prereqs: - done[prereq].wait() - - if cancelled.is_set(): - done[step].set() - return - - request_vc = merged_from(*input_steps) + # --- Phase 2: Kick off root events on TV --- + # The orchestrator only triggers the two root events (a and b). + # TV handles all downstream chaining: c internally, then forwards to FD and SUG. + # FD gates event e on both d and c's VC. + # SUG gates event g on both f and e's VC. + # The orchestrator does NOT manage any dependency graph. + root_results = {} + root_errors = {} + def run_root(name, rpc_fn): try: - response = rpc_func(order_id, request_vc) - store_event_result(step, response) - - if not response.success: - record_event_failure(step, response) + root_results[name] = rpc_fn(order_id) except Exception as e: - record_internal_failure(step, str(e), request_vc) - finally: - done[step].set() + root_errors[name] = str(e) - def run_finalize(): - for prereq in ["e", "f"]: - done[prereq].wait() + print(f"[ORCH] order={order_id} starting_root_events") - if cancelled.is_set(): - done["g"].set() - return - - request_vc = merged_from("e", "f") - - try: - response = sug_finalize(order_id, request_vc) - store_event_result("g", response) - - if response.success: - books = [] - for book in response.books: - books.append( - { - "bookId": book.bookId, - "title": book.title, - "author": book.author, - } - ) - with lock: - state["books"] = books - else: - record_event_failure("g", response) - except Exception as e: - record_internal_failure("g", str(e), request_vc) - finally: - done["g"].set() - - # Example partial order: - # a || b - # c after a - # d after b - # e after c and d - # f after a - # g after e and f - workers = [ - threading.Thread(target=run_event, args=("a", [], [], tv_validate_items)), - threading.Thread(target=run_event, args=("b", [], [], tv_validate_user_data)), - threading.Thread(target=run_event, args=("c", ["a"], ["a"], tv_validate_card_format)), - threading.Thread(target=run_event, args=("d", ["b"], ["b"], fd_check_user_fraud)), - threading.Thread(target=run_event, args=("e", ["c", "d"], ["c", "d"], fd_check_card_fraud)), - threading.Thread(target=run_event, args=("f", ["a"], ["a"], sug_precompute)), - threading.Thread(target=run_finalize), + threads = [ + threading.Thread(target=run_root, args=("a", tv_validate_items)), + threading.Thread(target=run_root, args=("b", tv_validate_user_data)), ] + for t in threads: + t.start() + for t in threads: + t.join() - print(f"[ORCH] order={order_id} starting_event_flow") - for worker in workers: - worker.start() - - for worker in workers: - worker.join() + if root_errors: + print(f"[ORCH] order={order_id} root_event_errors={root_errors}") - print(f"[ORCH] order={order_id} all_worker_threads_finished") - - with lock: - final_vc = merge_vcs(state["final_vc"], *state["event_vcs"].values()) - state["final_vc"] = final_vc - - if state["failure_kind"] == "internal_error": - broadcast_clear(order_id, final_vc) + # --- Phase 3: Wait for the full pipeline to complete --- + # SUG.AwaitPipelineResult blocks until event g finishes (or a failure propagates). + try: + pipeline_result = await_pipeline_result(order_id) + except Exception as e: + print(f"[ORCH] order={order_id} pipeline_await_error={e}") + broadcast_clear(order_id, [0, 0, 0]) return { "error": { "code": "INTERNAL_ERROR", - "message": state["failure_message"], + "message": "Failed to await pipeline result.", } }, 500 - if state["failure_kind"] == "event_failure": + final_vc = list(pipeline_result.vc.values) + + # Also merge root event VCs into final_vc for completeness. + for name in ("a", "b"): + if name in root_results: + final_vc = merge_vcs(final_vc, list(root_results[name].vc.values)) + + if not pipeline_result.success: + print( + f"[ORCH] order={order_id} pipeline_rejected " + f"message={pipeline_result.message} final_vc={final_vc}" + ) broadcast_clear(order_id, final_vc) return { - "error": { - "code": "ORDER_REJECTED", - "message": state["failure_message"], - } - }, 400 + "orderId": order_id, + "status": "Order Rejected", + "suggestedBooks": [], + "reason": pipeline_result.message, + }, 200 + # --- Phase 4: Enqueue approved order --- try: enqueue_response = enqueue_order(order_id, order_kwargs) except Exception as e: @@ -521,10 +390,20 @@ def run_finalize(): broadcast_clear(order_id, final_vc) print(f"[ORCH] order={order_id} final_status=APPROVED final_vc={final_vc}") + books = [] + for book in pipeline_result.books: + books.append( + { + "bookId": book.bookId, + "title": book.title, + "author": book.author, + } + ) + return { "orderId": order_id, "status": "Order Approved", - "suggestedBooks": state["books"], + "suggestedBooks": books, }, 200 diff --git a/suggestions/src/app.py b/suggestions/src/app.py index ca95bf0c3..b028ea4ce 100644 --- a/suggestions/src/app.py +++ b/suggestions/src/app.py @@ -48,6 +48,7 @@ def merge_vc(local_vc, incoming_vc): def tick(vc, idx): + vc = list(vc) vc[idx] += 1 return vc @@ -65,7 +66,25 @@ def InitOrder(self, request, context): orders[order.order_id] = { "order": order, "vc": [0, 0, 0], + "lock": threading.Lock(), "books": [], + # Causal gating state for event g (FinalizeSuggestions). + # g needs BOTH f (PrecomputeSuggestions, local) AND e (CheckCardFraud, from FD). + "f_done": False, + "f_vc": None, + "f_success": True, + "f_message": "", + "e_received": False, + "e_vc": None, + "e_success": True, + "e_message": "", + "g_triggered": False, + # Pipeline result: set when g completes or a failure is final. + "pipeline_done": threading.Event(), + "pipeline_success": False, + "pipeline_message": "", + "pipeline_vc": [0, 0, 0], + "pipeline_books": [], } print(f"[SUG] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") @@ -76,8 +95,68 @@ def InitOrder(self, request, context): vc=suggestions.VectorClock(values=[0, 0, 0]), ) + def _complete_pipeline(self, state, success, message, vc, books): + state["pipeline_success"] = success + state["pipeline_message"] = message + state["pipeline_vc"] = vc + state["pipeline_books"] = books + state["pipeline_done"].set() + + def _try_run_g(self, order_id, state): + """Check if both prerequisites for event g are met. If so, run FinalizeSuggestions.""" + with state["lock"]: + if state["g_triggered"]: + return + if not (state["f_done"] and state["e_received"]): + return + state["g_triggered"] = True + + f_vc = state["f_vc"] + f_success = state["f_success"] + f_message = state["f_message"] + e_vc = state["e_vc"] + e_success = state["e_success"] + e_message = state["e_message"] + + # If either prerequisite failed, propagate failure. + if not f_success: + print(f"[SUG] order={order_id} event=FinalizeSuggestions skipped (f failed: {f_message})") + self._complete_pipeline(state, False, f_message, f_vc, []) + return + if not e_success: + print(f"[SUG] order={order_id} event=FinalizeSuggestions skipped (e failed: {e_message})") + self._complete_pipeline(state, False, e_message, e_vc, []) + return + + # Both f and e succeeded: merge their VCs and run g. + merged = merge_vc(f_vc, e_vc) + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, merged) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + prepared_books = state["books"] + success = len(prepared_books) > 0 + message = ( + "Suggestions finalized." + if success + else "No prepared suggestions available." + ) + + print( + f"[SUG] order={order_id} event=FinalizeSuggestions " + f"vc={vc} success={success} returned_books={len(prepared_books)}" + ) + + self._complete_pipeline(state, success, message, vc, prepared_books) + def PrecomputeSuggestions(self, request, context): - state = get_order_state(request.order_id) + """Event f: called by TV after event a. After processing, checks if e's VC + has arrived so that event g can run.""" + order_id = request.order_id + state = get_order_state(order_id) if state is None: return suggestions.EventResponse( success=False, @@ -86,10 +165,12 @@ def PrecomputeSuggestions(self, request, context): ) incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc item_count = state["order"].item_count @@ -103,18 +184,141 @@ def PrecomputeSuggestions(self, request, context): message = "Cannot prepare suggestions for empty order." print( - f"[SUG] order={request.order_id} event=PrecomputeSuggestions " + f"[SUG] order={order_id} event=PrecomputeSuggestions " f"vc={vc} success={success} prepared_books={len(state['books'])}" ) + # Record f's result and attempt to trigger g. + with state["lock"]: + state["f_done"] = True + state["f_vc"] = vc + state["f_success"] = success + state["f_message"] = message + + self._try_run_g(order_id, state) + return suggestions.EventResponse( success=success, message=message, vc=suggestions.VectorClock(values=vc), ) + def ForwardVC(self, request, context): + """Receive a forwarded VC from another microservice.""" + order_id = request.order_id + source_event = request.source_event + incoming_vc = list(request.vc.values) + success = request.success + message = request.message + + state = get_order_state(order_id) + if state is None: + return suggestions.EventResponse( + success=False, + message="Order not found in suggestions service.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + ) + + print( + f"[SUG] order={order_id} event=ForwardVC source={source_event} " + f"vc={incoming_vc} success={success}" + ) + + if source_event == "e": + with state["lock"]: + state["e_received"] = True + state["e_vc"] = incoming_vc + state["e_success"] = success + state["e_message"] = message + + self._try_run_g(order_id, state) + elif source_event == "a": + # a failed: no f will ever come (since TV won't call PrecomputeSuggestions). + # Also no c→e chain, so mark both as failed. + with state["lock"]: + if not state["f_done"]: + state["f_done"] = True + state["f_vc"] = incoming_vc + state["f_success"] = False + state["f_message"] = message + if not state["e_received"]: + state["e_received"] = True + state["e_vc"] = incoming_vc + state["e_success"] = False + state["e_message"] = message + + self._try_run_g(order_id, state) + elif source_event == "f": + # f call itself failed (exception in TV calling SUG) + with state["lock"]: + if not state["f_done"]: + state["f_done"] = True + state["f_vc"] = incoming_vc + state["f_success"] = False + state["f_message"] = message + + self._try_run_g(order_id, state) + elif source_event == "d": + # d call failed (exception in TV calling FD), so e will never complete. + with state["lock"]: + if not state["e_received"]: + state["e_received"] = True + state["e_vc"] = incoming_vc + state["e_success"] = False + state["e_message"] = message + + self._try_run_g(order_id, state) + + return suggestions.EventResponse( + success=True, + message="VC forwarded.", + vc=suggestions.VectorClock(values=incoming_vc), + ) + + def AwaitPipelineResult(self, request, context): + """Block until the full event pipeline completes for this order.""" + order_id = request.order_id + state = get_order_state(order_id) + if state is None: + return suggestions.PipelineResultResponse( + success=False, + message="Order not found in suggestions service.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + ) + + # Wait for pipeline completion (event g or a propagated failure). + state["pipeline_done"].wait(timeout=30.0) + + if not state["pipeline_done"].is_set(): + return suggestions.PipelineResultResponse( + success=False, + message="Pipeline timed out.", + vc=suggestions.VectorClock(values=state["vc"]), + ) + + response = suggestions.PipelineResultResponse( + success=state["pipeline_success"], + message=state["pipeline_message"], + vc=suggestions.VectorClock(values=state["pipeline_vc"]), + ) + + for book in state["pipeline_books"]: + b = response.books.add() + b.bookId = book["bookId"] + b.title = book["title"] + b.author = book["author"] + + print( + f"[SUG] order={order_id} event=AwaitPipelineResult " + f"success={state['pipeline_success']} vc={state['pipeline_vc']}" + ) + + return response + def FinalizeSuggestions(self, request, context): - state = get_order_state(request.order_id) + """Event g: kept as an RPC for backward compat, but now triggered internally.""" + order_id = request.order_id + state = get_order_state(order_id) if state is None: return suggestions.SuggestionsEventResponse( success=False, @@ -124,10 +328,12 @@ def FinalizeSuggestions(self, request, context): ) incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc prepared_books = state["books"] success = len(prepared_books) > 0 @@ -150,7 +356,7 @@ def FinalizeSuggestions(self, request, context): b.author = book["author"] print( - f"[SUG] order={request.order_id} event=FinalizeSuggestions " + f"[SUG] order={order_id} event=FinalizeSuggestions " f"vc={vc} success={success} returned_books={len(prepared_books)}" ) @@ -170,8 +376,9 @@ def ClearOrder(self, request, context): vc=suggestions.VectorClock(values=[0, 0, 0]), ) - local_vc = state["vc"] - can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + with state["lock"]: + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) if can_clear: del orders[order_id] @@ -209,4 +416,4 @@ def serve(): if __name__ == "__main__": - serve() \ No newline at end of file + serve() diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index 7e5264c4a..0c8c287e0 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -9,9 +9,23 @@ ) sys.path.insert(0, transaction_verification_grpc_path) +fraud_detection_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/fraud_detection") +) +sys.path.insert(0, fraud_detection_grpc_path) + +suggestions_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/suggestions") +) +sys.path.insert(0, suggestions_grpc_path) + import grpc import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc +import fraud_detection_pb2 as fraud_detection +import fraud_detection_pb2_grpc as fraud_detection_grpc +import suggestions_pb2 as suggestions +import suggestions_pb2_grpc as suggestions_grpc SERVICE_INDEX = 0 # [transaction_verification, fraud_detection, suggestions] @@ -25,6 +39,7 @@ def merge_vc(local_vc, incoming_vc): def tick(vc, idx): + vc = list(vc) vc[idx] += 1 return vc @@ -44,6 +59,66 @@ def get_order_state(order_id: str): return orders.get(order_id) +def forward_to_fd(order_id, source_event, vc, success, message): + try: + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + req = fraud_detection.VCForward( + order_id=order_id, + source_event=source_event, + vc=fraud_detection.VectorClock(values=vc), + success=success, + message=message, + ) + stub.ForwardVC(req, timeout=10.0) + except Exception as e: + print(f"[TV] order={order_id} forward_to_fd_error source={source_event} error={e}") + + +def forward_to_sug(order_id, source_event, vc, success, message): + try: + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + req = suggestions.VCForward( + order_id=order_id, + source_event=source_event, + vc=suggestions.VectorClock(values=vc), + success=success, + message=message, + ) + stub.ForwardVC(req, timeout=10.0) + except Exception as e: + print(f"[TV] order={order_id} forward_to_sug_error source={source_event} error={e}") + + +def call_fd_check_user_fraud(order_id, vc): + try: + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + req = fraud_detection.EventRequest( + order_id=order_id, + vc=fraud_detection.VectorClock(values=vc), + ) + return stub.CheckUserFraud(req, timeout=10.0) + except Exception as e: + print(f"[TV] order={order_id} call_fd_check_user_fraud_error={e}") + return None + + +def call_sug_precompute(order_id, vc): + try: + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + req = suggestions.EventRequest( + order_id=order_id, + vc=suggestions.VectorClock(values=vc), + ) + return stub.PrecomputeSuggestions(req, timeout=10.0) + except Exception as e: + print(f"[TV] order={order_id} call_sug_precompute_error={e}") + return None + + class TransactionVerificationService( transaction_verification_grpc.TransactionVerificationServiceServicer ): @@ -54,6 +129,7 @@ def InitOrder(self, request, context): orders[order.order_id] = { "order": order, "vc": [0, 0, 0], + "lock": threading.Lock(), } print(f"[TV] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") @@ -64,91 +140,79 @@ def InitOrder(self, request, context): vc=transaction_verification.VectorClock(values=[0, 0, 0]), ) - def ValidateItems(self, request, context): - state = get_order_state(request.order_id) + def _process_event(self, order_id, incoming_vc, event_name, check_fn): + state = get_order_state(order_id) if state is None: - return transaction_verification.EventResponse( - success=False, - message="Order not found in transaction verification service.", - vc=transaction_verification.VectorClock(values=[0, 0, 0]), - ) + return None, False, "Order not found in transaction verification service.", [0, 0, 0] - incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + with state["lock"]: + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc - item_count = state["order"].item_count - success = item_count > 0 - message = "Items check passed." if success else "No items in order." + success, message = check_fn(state) print( - f"[TV] order={request.order_id} event=ValidateItems " - f"vc={vc} success={success} item_count={item_count}" + f"[TV] order={order_id} event={event_name} " + f"vc={vc} success={success}" ) - return transaction_verification.EventResponse( - success=success, - message=message, - vc=transaction_verification.VectorClock(values=vc), - ) + return vc, success, message, vc - def ValidateUserData(self, request, context): - state = get_order_state(request.order_id) - if state is None: + def ValidateItems(self, request, context): + """Event a: root event. After processing, chains to c (ValidateCardFormat) + and forwards VC to SUG (PrecomputeSuggestions) and eventually to FD.""" + order_id = request.order_id + incoming_vc = list(request.vc.values) + + def check(state): + item_count = state["order"].item_count + success = item_count > 0 + message = "Items check passed." if success else "No items in order." + return success, message + + vc, success, message, _ = self._process_event(order_id, incoming_vc, "ValidateItems", check) + if vc is None: return transaction_verification.EventResponse( - success=False, - message="Order not found in transaction verification service.", + success=False, message=message, vc=transaction_verification.VectorClock(values=[0, 0, 0]), ) - incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc + # After event a completes, trigger downstream events in background threads. + # TV itself handles event c (ValidateCardFormat) and forwards to FD and SUG. + def chain_after_a(): + if not success: + # a failed: propagate failure to FD and SUG + forward_to_fd(order_id, "a", vc, False, message) + forward_to_sug(order_id, "a", vc, False, message) + return + + # Call SUG.PrecomputeSuggestions (event f) — TV forwards a's VC to SUG + sug_resp = call_sug_precompute(order_id, vc) + if sug_resp is None: + forward_to_sug(order_id, "f", vc, False, "PrecomputeSuggestions call failed.") + + # Process event c (ValidateCardFormat) internally on TV + c_vc, c_success, c_message, _ = self._process_event( + order_id, vc, "ValidateCardFormat", self._card_format_check + ) - order = state["order"] - success = True - message = "User data check passed." + if c_vc is None: + forward_to_fd(order_id, "c", vc, False, c_message) + return - if not order.user_name: - success = False - message = "Missing user name." - elif not order.user_contact: - success = False - message = "Missing user contact." - elif not order.terms_accepted: - success = False - message = "Terms and conditions not accepted." + # Forward c's result to FD (FD needs c's VC to gate event e) + forward_to_fd(order_id, "c", c_vc, c_success, c_message) - print( - f"[TV] order={request.order_id} event=ValidateUserData " - f"vc={vc} success={success}" - ) + threading.Thread(target=chain_after_a, daemon=True).start() return transaction_verification.EventResponse( - success=success, - message=message, + success=success, message=message, vc=transaction_verification.VectorClock(values=vc), ) - def ValidateCardFormat(self, request, context): - state = get_order_state(request.order_id) - if state is None: - return transaction_verification.EventResponse( - success=False, - message="Order not found in transaction verification service.", - vc=transaction_verification.VectorClock(values=[0, 0, 0]), - ) - - incoming_vc = list(request.vc.values) - local_vc = state["vc"] - vc = merge_vc(local_vc, incoming_vc) - vc = tick(vc, SERVICE_INDEX) - state["vc"] = vc - + def _card_format_check(self, state): order = state["order"] card_digits = extract_card_digits(order.card_number) @@ -161,15 +225,71 @@ def ValidateCardFormat(self, request, context): elif len(card_digits) != 16: success = False message = "Invalid card number." + return success, message - print( - f"[TV] order={request.order_id} event=ValidateCardFormat " - f"vc={vc} success={success} masked_card={mask_fixed(order.card_number)}" + def ValidateUserData(self, request, context): + """Event b: root event. After processing, forwards VC to FD (CheckUserFraud).""" + order_id = request.order_id + incoming_vc = list(request.vc.values) + + def check(state): + order = state["order"] + success = True + message = "User data check passed." + + if not order.user_name: + success = False + message = "Missing user name." + elif not order.user_contact: + success = False + message = "Missing user contact." + elif not order.terms_accepted: + success = False + message = "Terms and conditions not accepted." + return success, message + + vc, success, message, _ = self._process_event(order_id, incoming_vc, "ValidateUserData", check) + if vc is None: + return transaction_verification.EventResponse( + success=False, message=message, + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + # After event b completes, forward to FD in a background thread. + def chain_after_b(): + if not success: + # b failed: propagate failure downstream (d will never run). + forward_to_fd(order_id, "d", vc, False, message) + return + + fd_resp = call_fd_check_user_fraud(order_id, vc) + if fd_resp is None: + # If the FD call itself failed, propagate failure to SUG + forward_to_sug(order_id, "d", vc, False, "CheckUserFraud call failed.") + + threading.Thread(target=chain_after_b, daemon=True).start() + + return transaction_verification.EventResponse( + success=success, message=message, + vc=transaction_verification.VectorClock(values=vc), + ) + + def ValidateCardFormat(self, request, context): + """Event c: kept as an RPC for backward compat, but now called internally by TV.""" + order_id = request.order_id + incoming_vc = list(request.vc.values) + + vc, success, message, _ = self._process_event( + order_id, incoming_vc, "ValidateCardFormat", self._card_format_check ) + if vc is None: + return transaction_verification.EventResponse( + success=False, message=message, + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) return transaction_verification.EventResponse( - success=success, - message=message, + success=success, message=message, vc=transaction_verification.VectorClock(values=vc), ) @@ -187,8 +307,9 @@ def ClearOrder(self, request, context): vc=transaction_verification.VectorClock(values=[0, 0, 0]), ) - local_vc = state["vc"] - can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + with state["lock"]: + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) if can_clear: del orders[order_id] @@ -226,4 +347,4 @@ def serve(): if __name__ == "__main__": - serve() \ No newline at end of file + serve() diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index fe4d514a0..0373fbaca 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -3,16 +3,14 @@ syntax = "proto3"; package fraud_detection; service FraudDetectionService { - // Required for Seminar 5: - // Orchestrator sends the order once, service stores it locally and - // initializes its per-order vector clock. rpc InitOrder (InitOrderRequest) returns (EventResponse); - // Example Seminar 5 fraud-related events: rpc CheckUserFraud (EventRequest) returns (EventResponse); rpc CheckCardFraud (EventRequest) returns (EventResponse); - // Optional bonus event from Seminar 5: + // Receive a forwarded vector clock from another microservice. + rpc ForwardVC (VCForward) returns (EventResponse); + rpc ClearOrder (ClearOrderRequest) returns (EventResponse); } @@ -46,6 +44,14 @@ message EventResponse { VectorClock vc = 3; } +message VCForward { + string order_id = 1; + string source_event = 2; + VectorClock vc = 3; + bool success = 4; + string message = 5; +} + message ClearOrderRequest { string order_id = 1; VectorClock final_vc = 2; diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index 6e0970015..101348eb3 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x0f\x66raud_detection\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"=\n\x10InitOrderRequest\x12)\n\x05order\x18\x01 \x01(\x0b\x32\x1a.fraud_detection.OrderData\"J\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12(\n\x02vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"[\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12(\n\x02vc\x18\x03 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"U\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12.\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock2\xdb\x02\n\x15\x46raudDetectionService\x12N\n\tInitOrder\x12!.fraud_detection.InitOrderRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckUserFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckCardFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12P\n\nClearOrder\x12\".fraud_detection.ClearOrderRequest\x1a\x1e.fraud_detection.EventResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x0f\x66raud_detection\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"=\n\x10InitOrderRequest\x12)\n\x05order\x18\x01 \x01(\x0b\x32\x1a.fraud_detection.OrderData\"J\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12(\n\x02vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"[\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12(\n\x02vc\x18\x03 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"\x7f\n\tVCForward\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x14\n\x0csource_event\x18\x02 \x01(\t\x12(\n\x02vc\x18\x03 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\x12\x0f\n\x07success\x18\x04 \x01(\x08\x12\x0f\n\x07message\x18\x05 \x01(\t\"U\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12.\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock2\xa4\x03\n\x15\x46raudDetectionService\x12N\n\tInitOrder\x12!.fraud_detection.InitOrderRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckUserFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckCardFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12G\n\tForwardVC\x12\x1a.fraud_detection.VCForward\x1a\x1e.fraud_detection.EventResponse\x12P\n\nClearOrder\x12\".fraud_detection.ClearOrderRequest\x1a\x1e.fraud_detection.EventResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -41,8 +41,10 @@ _globals['_EVENTREQUEST']._serialized_end=386 _globals['_EVENTRESPONSE']._serialized_start=388 _globals['_EVENTRESPONSE']._serialized_end=479 - _globals['_CLEARORDERREQUEST']._serialized_start=481 - _globals['_CLEARORDERREQUEST']._serialized_end=566 - _globals['_FRAUDDETECTIONSERVICE']._serialized_start=569 - _globals['_FRAUDDETECTIONSERVICE']._serialized_end=916 + _globals['_VCFORWARD']._serialized_start=481 + _globals['_VCFORWARD']._serialized_end=608 + _globals['_CLEARORDERREQUEST']._serialized_start=610 + _globals['_CLEARORDERREQUEST']._serialized_end=695 + _globals['_FRAUDDETECTIONSERVICE']._serialized_start=698 + _globals['_FRAUDDETECTIONSERVICE']._serialized_end=1118 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index fe8cb8ab1..da3d05955 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -55,6 +55,20 @@ class EventResponse(_message.Message): vc: VectorClock def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... +class VCForward(_message.Message): + __slots__ = ("order_id", "source_event", "vc", "success", "message") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + SOURCE_EVENT_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + order_id: str + source_event: str + vc: VectorClock + success: bool + message: str + def __init__(self, order_id: _Optional[str] = ..., source_event: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ..., success: bool = ..., message: _Optional[str] = ...) -> None: ... + class ClearOrderRequest(_message.Message): __slots__ = ("order_id", "final_vc") ORDER_ID_FIELD_NUMBER: _ClassVar[int] diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 85179fcb1..a54520a95 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -49,6 +49,11 @@ def __init__(self, channel): request_serializer=fraud__detection__pb2.EventRequest.SerializeToString, response_deserializer=fraud__detection__pb2.EventResponse.FromString, _registered_method=True) + self.ForwardVC = channel.unary_unary( + '/fraud_detection.FraudDetectionService/ForwardVC', + request_serializer=fraud__detection__pb2.VCForward.SerializeToString, + response_deserializer=fraud__detection__pb2.EventResponse.FromString, + _registered_method=True) self.ClearOrder = channel.unary_unary( '/fraud_detection.FraudDetectionService/ClearOrder', request_serializer=fraud__detection__pb2.ClearOrderRequest.SerializeToString, @@ -60,17 +65,13 @@ class FraudDetectionServiceServicer(object): """Missing associated documentation comment in .proto file.""" def InitOrder(self, request, context): - """Required for Seminar 5: - Orchestrator sends the order once, service stores it locally and - initializes its per-order vector clock. - """ + """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') def CheckUserFraud(self, request, context): - """Example Seminar 5 fraud-related events: - """ + """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') @@ -81,13 +82,19 @@ def CheckCardFraud(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def ClearOrder(self, request, context): - """Optional bonus event from Seminar 5: + def ForwardVC(self, request, context): + """Receive a forwarded vector clock from another microservice. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ClearOrder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_FraudDetectionServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -106,6 +113,11 @@ def add_FraudDetectionServiceServicer_to_server(servicer, server): request_deserializer=fraud__detection__pb2.EventRequest.FromString, response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, ), + 'ForwardVC': grpc.unary_unary_rpc_method_handler( + servicer.ForwardVC, + request_deserializer=fraud__detection__pb2.VCForward.FromString, + response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, + ), 'ClearOrder': grpc.unary_unary_rpc_method_handler( servicer.ClearOrder, request_deserializer=fraud__detection__pb2.ClearOrderRequest.FromString, @@ -203,6 +215,33 @@ def CheckCardFraud(request, metadata, _registered_method=True) + @staticmethod + def ForwardVC(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/ForwardVC', + fraud__detection__pb2.VCForward.SerializeToString, + fraud__detection__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def ClearOrder(request, target, diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto index 60820ebda..4ca9e4836 100644 --- a/utils/pb/suggestions/suggestions.proto +++ b/utils/pb/suggestions/suggestions.proto @@ -8,6 +8,12 @@ service SuggestionsService { rpc PrecomputeSuggestions (EventRequest) returns (EventResponse); rpc FinalizeSuggestions (EventRequest) returns (SuggestionsEventResponse); + // Receive a forwarded vector clock from another microservice. + rpc ForwardVC (VCForward) returns (EventResponse); + + // Block until the full pipeline completes for this order, then return the result. + rpc AwaitPipelineResult (PipelineResultRequest) returns (PipelineResultResponse); + rpc ClearOrder (ClearOrderRequest) returns (EventResponse); } @@ -54,6 +60,25 @@ message SuggestionsEventResponse { repeated SuggestedBook books = 4; } +message VCForward { + string order_id = 1; + string source_event = 2; + VectorClock vc = 3; + bool success = 4; + string message = 5; +} + +message PipelineResultRequest { + string order_id = 1; +} + +message PipelineResultResponse { + bool success = 1; + string message = 2; + VectorClock vc = 3; + repeated SuggestedBook books = 4; +} + message ClearOrderRequest { string order_id = 1; VectorClock final_vc = 2; diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py index cab75ecee..502cbe8ce 100644 --- a/utils/pb/suggestions/suggestions_pb2.py +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"9\n\x10InitOrderRequest\x12%\n\x05order\x18\x01 \x01(\x0b\x32\x16.suggestions.OrderData\"F\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12$\n\x02vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock\"W\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"\x8d\x01\n\x18SuggestionsEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\x12)\n\x05\x62ooks\x18\x04 \x03(\x0b\x32\x1a.suggestions.SuggestedBook\"Q\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12*\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock2\xcf\x02\n\x12SuggestionsService\x12\x46\n\tInitOrder\x12\x1d.suggestions.InitOrderRequest\x1a\x1a.suggestions.EventResponse\x12N\n\x15PrecomputeSuggestions\x12\x19.suggestions.EventRequest\x1a\x1a.suggestions.EventResponse\x12W\n\x13\x46inalizeSuggestions\x12\x19.suggestions.EventRequest\x1a%.suggestions.SuggestionsEventResponse\x12H\n\nClearOrder\x12\x1e.suggestions.ClearOrderRequest\x1a\x1a.suggestions.EventResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"9\n\x10InitOrderRequest\x12%\n\x05order\x18\x01 \x01(\x0b\x32\x16.suggestions.OrderData\"F\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12$\n\x02vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock\"W\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"\x8d\x01\n\x18SuggestionsEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\x12)\n\x05\x62ooks\x18\x04 \x03(\x0b\x32\x1a.suggestions.SuggestedBook\"{\n\tVCForward\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x14\n\x0csource_event\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\x12\x0f\n\x07success\x18\x04 \x01(\x08\x12\x0f\n\x07message\x18\x05 \x01(\t\")\n\x15PipelineResultRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\"\x8b\x01\n\x16PipelineResultResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\x12)\n\x05\x62ooks\x18\x04 \x03(\x0b\x32\x1a.suggestions.SuggestedBook\"Q\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12*\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock2\xf0\x03\n\x12SuggestionsService\x12\x46\n\tInitOrder\x12\x1d.suggestions.InitOrderRequest\x1a\x1a.suggestions.EventResponse\x12N\n\x15PrecomputeSuggestions\x12\x19.suggestions.EventRequest\x1a\x1a.suggestions.EventResponse\x12W\n\x13\x46inalizeSuggestions\x12\x19.suggestions.EventRequest\x1a%.suggestions.SuggestionsEventResponse\x12?\n\tForwardVC\x12\x16.suggestions.VCForward\x1a\x1a.suggestions.EventResponse\x12^\n\x13\x41waitPipelineResult\x12\".suggestions.PipelineResultRequest\x1a#.suggestions.PipelineResultResponse\x12H\n\nClearOrder\x12\x1e.suggestions.ClearOrderRequest\x1a\x1a.suggestions.EventResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -45,8 +45,14 @@ _globals['_SUGGESTEDBOOK']._serialized_end=523 _globals['_SUGGESTIONSEVENTRESPONSE']._serialized_start=526 _globals['_SUGGESTIONSEVENTRESPONSE']._serialized_end=667 - _globals['_CLEARORDERREQUEST']._serialized_start=669 - _globals['_CLEARORDERREQUEST']._serialized_end=750 - _globals['_SUGGESTIONSSERVICE']._serialized_start=753 - _globals['_SUGGESTIONSSERVICE']._serialized_end=1088 + _globals['_VCFORWARD']._serialized_start=669 + _globals['_VCFORWARD']._serialized_end=792 + _globals['_PIPELINERESULTREQUEST']._serialized_start=794 + _globals['_PIPELINERESULTREQUEST']._serialized_end=835 + _globals['_PIPELINERESULTRESPONSE']._serialized_start=838 + _globals['_PIPELINERESULTRESPONSE']._serialized_end=977 + _globals['_CLEARORDERREQUEST']._serialized_start=979 + _globals['_CLEARORDERREQUEST']._serialized_end=1060 + _globals['_SUGGESTIONSSERVICE']._serialized_start=1063 + _globals['_SUGGESTIONSSERVICE']._serialized_end=1559 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/suggestions/suggestions_pb2.pyi b/utils/pb/suggestions/suggestions_pb2.pyi index 3e893c77f..06b086928 100644 --- a/utils/pb/suggestions/suggestions_pb2.pyi +++ b/utils/pb/suggestions/suggestions_pb2.pyi @@ -77,6 +77,38 @@ class SuggestionsEventResponse(_message.Message): books: _containers.RepeatedCompositeFieldContainer[SuggestedBook] def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ..., books: _Optional[_Iterable[_Union[SuggestedBook, _Mapping]]] = ...) -> None: ... +class VCForward(_message.Message): + __slots__ = ("order_id", "source_event", "vc", "success", "message") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + SOURCE_EVENT_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + order_id: str + source_event: str + vc: VectorClock + success: bool + message: str + def __init__(self, order_id: _Optional[str] = ..., source_event: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ..., success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class PipelineResultRequest(_message.Message): + __slots__ = ("order_id",) + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + order_id: str + def __init__(self, order_id: _Optional[str] = ...) -> None: ... + +class PipelineResultResponse(_message.Message): + __slots__ = ("success", "message", "vc", "books") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + BOOKS_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + vc: VectorClock + books: _containers.RepeatedCompositeFieldContainer[SuggestedBook] + def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ..., books: _Optional[_Iterable[_Union[SuggestedBook, _Mapping]]] = ...) -> None: ... + class ClearOrderRequest(_message.Message): __slots__ = ("order_id", "final_vc") ORDER_ID_FIELD_NUMBER: _ClassVar[int] diff --git a/utils/pb/suggestions/suggestions_pb2_grpc.py b/utils/pb/suggestions/suggestions_pb2_grpc.py index 7199eb0ff..46f2a50b1 100644 --- a/utils/pb/suggestions/suggestions_pb2_grpc.py +++ b/utils/pb/suggestions/suggestions_pb2_grpc.py @@ -49,6 +49,16 @@ def __init__(self, channel): request_serializer=suggestions__pb2.EventRequest.SerializeToString, response_deserializer=suggestions__pb2.SuggestionsEventResponse.FromString, _registered_method=True) + self.ForwardVC = channel.unary_unary( + '/suggestions.SuggestionsService/ForwardVC', + request_serializer=suggestions__pb2.VCForward.SerializeToString, + response_deserializer=suggestions__pb2.EventResponse.FromString, + _registered_method=True) + self.AwaitPipelineResult = channel.unary_unary( + '/suggestions.SuggestionsService/AwaitPipelineResult', + request_serializer=suggestions__pb2.PipelineResultRequest.SerializeToString, + response_deserializer=suggestions__pb2.PipelineResultResponse.FromString, + _registered_method=True) self.ClearOrder = channel.unary_unary( '/suggestions.SuggestionsService/ClearOrder', request_serializer=suggestions__pb2.ClearOrderRequest.SerializeToString, @@ -77,6 +87,20 @@ def FinalizeSuggestions(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ForwardVC(self, request, context): + """Receive a forwarded vector clock from another microservice. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AwaitPipelineResult(self, request, context): + """Block until the full pipeline completes for this order, then return the result. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def ClearOrder(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -101,6 +125,16 @@ def add_SuggestionsServiceServicer_to_server(servicer, server): request_deserializer=suggestions__pb2.EventRequest.FromString, response_serializer=suggestions__pb2.SuggestionsEventResponse.SerializeToString, ), + 'ForwardVC': grpc.unary_unary_rpc_method_handler( + servicer.ForwardVC, + request_deserializer=suggestions__pb2.VCForward.FromString, + response_serializer=suggestions__pb2.EventResponse.SerializeToString, + ), + 'AwaitPipelineResult': grpc.unary_unary_rpc_method_handler( + servicer.AwaitPipelineResult, + request_deserializer=suggestions__pb2.PipelineResultRequest.FromString, + response_serializer=suggestions__pb2.PipelineResultResponse.SerializeToString, + ), 'ClearOrder': grpc.unary_unary_rpc_method_handler( servicer.ClearOrder, request_deserializer=suggestions__pb2.ClearOrderRequest.FromString, @@ -198,6 +232,60 @@ def FinalizeSuggestions(request, metadata, _registered_method=True) + @staticmethod + def ForwardVC(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/ForwardVC', + suggestions__pb2.VCForward.SerializeToString, + suggestions__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def AwaitPipelineResult(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/AwaitPipelineResult', + suggestions__pb2.PipelineResultRequest.SerializeToString, + suggestions__pb2.PipelineResultResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + @staticmethod def ClearOrder(request, target, diff --git a/vc_investigation_results.txt b/vc_investigation_results.txt new file mode 100644 index 000000000..5d2951189 --- /dev/null +++ b/vc_investigation_results.txt @@ -0,0 +1,57 @@ +*Investigation results — TA feedback on vector clocks* + +Grade: 7.00 / 11.5 +TA feedback: "+ 0.5 bonus point. Vector clocks are not meaningfully implemented. Event ordering should be handled by the microservices not centrally through the orchestrator." + +--- + +*Key finding* + +The three vector-clock services (transaction_verification, fraud_detection, suggestions) never talk to each other. Every cross-service interaction is initiated by the orchestrator. Only the orchestrator opens gRPC channels to these services — the services themselves open none. + +--- + +*A. "Event ordering should be handled by the microservices, not centrally through the orchestrator"* + +The orchestrator is the entire dependency engine. The partial order, the wait-for-prereqs, and the input clocks for each event all live inside the orchestrator: + +- The whole partial order is hardcoded as a list of Python threads inside the orchestrator (e.g. "c after a", "e after c and d", etc.) +- "Event X happens after event Y" is enforced by Python threading.Event waits inside the orchestrator's own process — not by anything the services do. +- The clock sent into each event is computed by the orchestrator from prior responses. + +The microservices are completely passive. Each handler is a one-shot RPC: receive a VC, merge with local, tick, return. There is no notion in TV/FD/SUG of "event A must happen before event B". None of the services even knows there is a partial order. + +In a proper distributed event-flow, the microservices themselves would forward causal messages and decide when to act based on the VCs they have seen. Here they are just leaves on a star whose hub does all the bookkeeping. That is exactly what the TA flagged. + +--- + +*B. "Vector clocks are not meaningfully implemented"* + +Once the orchestrator owns ordering, the vector clocks add no information it doesn't already have, and they make no decisions anywhere in the system: + +1. No service ever uses an incoming VC for a decision. The incoming VC is consumed exclusively by the merge function and then logged. There is no causal-delivery check, no "wait until I've seen FD >= k before processing this". The VC is an inert payload that round-trips. + +2. Ordering would be identical if the VCs were removed. The order of events is enforced by threading.Event waits and an explicit dependency list in the orchestrator. Delete the entire VC plumbing and the run would still produce events in the same partial order. + +3. The VC is also redundant for the receiver. Each service merges incoming with its own local clock, which already advanced when prior events ran on it. The request VC contributes nothing the service couldn't reconstruct locally. + +4. The orchestrator isn't even in the clock vector. The clock has 3 positions [TV, FD, SUG] and the orchestrator — the only process that actually sends and receives every message and enforces happens-before — has no entry. So the entity doing all the causal reasoning is invisible in the clock that supposedly tracks causality. + +5. There is also a race condition bug: two events on TV (ValidateItems and ValidateUserData) are dispatched in parallel by the orchestrator, and there is no lock around the read-modify-write of the local clock in the TV handler. Under load, both events can read the same clock value and return identical VCs — a direct violation of the vector-clock invariant (two events on the same process must be totally ordered, never concurrent). + +In short: VC values are computed and logged, but they are not used. They are documentation for the demo, not the mechanism that establishes order. + +--- + +*What needs to change (architectural)* + +The microservices themselves need to drive the event flow over inter-service gRPC, carrying vector clocks on those calls: + +- Orchestrator submits a single kickoff to TV with VC [0,0,0]. +- TV runs its events, then TV itself invokes the next service in the chain (e.g. FD, SUG) with its updated clock. +- FD and SUG use the incoming VC to gate execution: a service blocks on a message until the incoming VC dominates the prerequisites it knows about. This is where VCs would actually be used as a mechanism, not just a tag. +- The orchestrator's job shrinks to: kick off the first message, await the final result, broadcast ClearOrder. No more threading choreography, no more central dependency list. + +This requires adding inter-service gRPC stubs to TV, FD, and SUG (currently they have none), and adding the orchestrator as a 4th VC component (or redesigning the 3-wide clock accordingly). + +Additionally, a per-order lock is needed around each handler's clock update to fix the race condition on TV. From f8dce1381d309d11f76947575838a43e2abd1afa Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Wed, 15 Apr 2026 14:04:35 +0300 Subject: [PATCH 29/58] Implement Checkpoint 3 phases 1-4 - Phase 1: extend order data model so items flow end-to-end (OrderItem added to 4 protos, orchestrator preserves titles, executor logs items, frontend sends title-keyed items) - Phase 2: replicated books_database service (3 replicas with primary-backup synchronous replication + bully leader election) - Phase 3: per-key locks for the concurrent-writes bonus (same-key writes serialize cleanly, different-key writes run in parallel; verified with 10-way and 5-way concurrent tests) - Phase 4: payment_service + DB participant RPCs (Prepare/Commit/Abort on both, pending-buffer reservations, ReadLocal debug RPC for replica-convergence checks) Also adds Charlie-Lima-Alfa.md gap-analysis doc tracking the execution plan across sessions. Co-Authored-By: Claude Opus 4.6 --- Charlie-Lima-Alfa.md | 638 ++++++++++++++++++ books_database/Dockerfile | 5 + books_database/requirements.txt | 3 + books_database/src/app.py | 599 ++++++++++++++++ .../tests/test_concurrent_writes.py | 132 ++++ docker-compose.yaml | 80 ++- frontend/src/index.html | 6 +- orchestrator/src/app.py | 55 +- order_executor/src/app.py | 6 +- payment_service/Dockerfile | 5 + payment_service/requirements.txt | 3 + payment_service/src/app.py | 128 ++++ payment_service/tests/smoke_test.py | 194 ++++++ utils/pb/books_database/__init__.py | 0 utils/pb/books_database/books_database.proto | 130 ++++ utils/pb/books_database/books_database_pb2.py | 76 +++ .../pb/books_database/books_database_pb2.pyi | 152 +++++ .../books_database/books_database_pb2_grpc.py | 541 +++++++++++++++ .../pb/fraud_detection/fraud_detection.proto | 6 + .../pb/fraud_detection/fraud_detection_pb2.py | 32 +- .../fraud_detection/fraud_detection_pb2.pyi | 14 +- utils/pb/order_queue/order_queue.proto | 6 + utils/pb/order_queue/order_queue_pb2.py | 28 +- utils/pb/order_queue/order_queue_pb2.pyi | 17 +- utils/pb/payment_service/__init__.py | 0 utils/pb/payment_service/payment.proto | 40 ++ utils/pb/payment_service/payment_pb2.py | 48 ++ utils/pb/payment_service/payment_pb2.pyi | 51 ++ utils/pb/payment_service/payment_pb2_grpc.py | 189 ++++++ utils/pb/suggestions/suggestions.proto | 6 + utils/pb/suggestions/suggestions_pb2.py | 48 +- utils/pb/suggestions/suggestions_pb2.pyi | 14 +- .../transaction_verification.proto | 6 + .../transaction_verification_pb2.py | 28 +- .../transaction_verification_pb2.pyi | 14 +- 35 files changed, 3206 insertions(+), 94 deletions(-) create mode 100644 Charlie-Lima-Alfa.md create mode 100644 books_database/Dockerfile create mode 100644 books_database/requirements.txt create mode 100644 books_database/src/app.py create mode 100644 books_database/tests/test_concurrent_writes.py create mode 100644 payment_service/Dockerfile create mode 100644 payment_service/requirements.txt create mode 100644 payment_service/src/app.py create mode 100644 payment_service/tests/smoke_test.py create mode 100644 utils/pb/books_database/__init__.py create mode 100644 utils/pb/books_database/books_database.proto create mode 100644 utils/pb/books_database/books_database_pb2.py create mode 100644 utils/pb/books_database/books_database_pb2.pyi create mode 100644 utils/pb/books_database/books_database_pb2_grpc.py create mode 100644 utils/pb/payment_service/__init__.py create mode 100644 utils/pb/payment_service/payment.proto create mode 100644 utils/pb/payment_service/payment_pb2.py create mode 100644 utils/pb/payment_service/payment_pb2.pyi create mode 100644 utils/pb/payment_service/payment_pb2_grpc.py diff --git a/Charlie-Lima-Alfa.md b/Charlie-Lima-Alfa.md new file mode 100644 index 000000000..efe1746eb --- /dev/null +++ b/Charlie-Lima-Alfa.md @@ -0,0 +1,638 @@ +# Charlie-Lima-Alfa: Changes Needed for Checkpoint 3 + +This document explains what we still need to do in this repository so it is +ready for **Checkpoint 3** (evaluation date 06.05 / 08.05). It is based on: + +- the main Projects page: +- the Checkpoint 3 guide (Session 13): +- the Session 10 guide (database + consistency): +- the Session 11 guide (distributed commitment): + +The language is kept simple (B1 level) on purpose, but the technical details +are precise so that any team member can pick the file up and start working. + +--- + +## 1. Where the repository is today + +Today the repository is at **Checkpoint 2** state. The top-level +[README.md](README.md) still says "Checkpoint 2". The running services are: + +- [frontend/](frontend/) +- [orchestrator/](orchestrator/) +- [transaction_verification/](transaction_verification/) +- [fraud_detection/](fraud_detection/) +- [suggestions/](suggestions/) +- [order_queue/](order_queue/) +- [order_executor/](order_executor/) (3 replicas: `order_executor_1..3`) + +Vector clocks, leader election, and mutual exclusion for the queue are already +done and documented. The protobuf folder [utils/pb/](utils/pb/) only contains +stubs for the services above. + +There is **no database service** and **no payment service** yet. There is also +**no consistency protocol diagram** and **no commitment protocol diagram** in +[docs/diagrams/](docs/diagrams/). We also do not have a `checkpoint-3` git tag. + +There is one more gap we uncovered during cross-review: the current pipeline +collapses the real `items` list into a single `item_count` field, so the +executor has no way to know which books or how many copies to process. §3 +describes how to fix this before any 2PC work begins. + +So, for Checkpoint 3 we need to add two new distributed features, two new +services, two new diagrams, fix the order data model, and update +documentation and tooling. + +--- + +## 2. What Checkpoint 3 asks from us + +The Checkpoint 3 guide gives us **10 base points + up to 3 bonus points**. The +grading breakdown is: + +| Item | Points | +| --- | --- | +| Consistency protocol & database module | 3 | +| Distributed Commitment protocol & new service | 3 | +| Logging implementation | 1 | +| Project organization, documentation, collaboration | 1 | +| Consistency Protocol diagram | 1 | +| Distributed Commitment Protocol diagram | 1 | +| Bonus: Consistency protocol session task | +0.75 | +| Bonus: Commitment protocol session task | +0.75 | +| (Further bonus tasks from the session pages) | up to 3 total | + +So we must deliver two protocols, one new database tier, one new payment +service, logs that prove the protocols work, and two diagrams. + +--- + +## 3. Prerequisite: extend the order data model so items flow end to end + +Before any of the Checkpoint 3 work can actually update stock, we must fix a +real gap in the current pipeline. Today the frontend sends the real items, +but the backend drops them very early: + +- [orchestrator/src/app.py](orchestrator/src/app.py) reduces the order to + `user data`, `credit card data`, `item_count`, and `terms_accepted`. +- [utils/pb/order_queue/order_queue.proto](utils/pb/order_queue/order_queue.proto) + only stores `item_count`. + +This means the executor that dequeues an order has no idea **which** books +or **how many** of each to decrement. The 2PC Prepare described later in +this document cannot work until this is fixed. + +### 3.1 Proto change + +Add a shared item message and let the order data carry a list of items: + +```proto +message OrderItem { + string title = 1; + int32 quantity = 2; +} + +// inside the queue order message +repeated OrderItem items = ...; +``` + +### 3.2 Files that have to change + +- [frontend/src/index.html](frontend/src/index.html): confirm the checkout + payload already sends `items` with real titles and quantities; if not, + extend it. +- [orchestrator/src/app.py](orchestrator/src/app.py): stop collapsing the + items to `item_count`; pass the full `items` list through to the queue + enqueue call. +- [utils/pb/order_queue/order_queue.proto](utils/pb/order_queue/order_queue.proto): + add `repeated OrderItem items` to the enqueue message, then regenerate + the Python stubs under [utils/pb/order_queue/](utils/pb/order_queue/). +- [order_queue/src/app.py](order_queue/src/app.py): store and return the + items on dequeue. +- [order_executor/src/app.py](order_executor/src/app.py): read the items + field of the dequeued order and pass it to the 2PC coordinator logic. + +If we choose Option A in §6 (keep the Checkpoint 2 validation pipeline +active), the same `OrderItem` message should also be added to: + +- [utils/pb/transaction_verification/](utils/pb/transaction_verification/) +- [utils/pb/fraud_detection/](utils/pb/fraud_detection/) +- [utils/pb/suggestions/](utils/pb/suggestions/) + +so that those services do not trip over the new field. + +### 3.3 Why this sits before the main work + +Without this change, the entire consistency and commitment plan below is +theoretical: the coordinator cannot send meaningful `Prepare` calls because +it does not know the items. Treat this as a **blocking prerequisite**, not +as optional polish. + +--- + +## 4. New work item A: Replicated Books Database + +### 4.1 What to build + +A new service called, for example, `books_database`, that stores the **book +inventory** as a key-value store. The key is the book title (string), the +value is the current stock quantity (integer). We may also store the price per +book, which is nice for the final demo and costs almost nothing. + +The service has to be a **gRPC service** (not HTTP). The minimum API is: + +- `Read(key) -> value` +- `Write(key, value) -> ok` + +Both calls must be **atomic** on one replica. The database must run as **at +least 3 instances in parallel**. All instances share the same codebase. +Clients (the order executor) should see a single logical database; the fact +that there are many replicas is hidden behind a consistency protocol. + +### 4.2 Which consistency protocol + +The guide lets us pick, but we must reach **at least sequential consistency**. +The candidates are: + +- **Primary-backup** (easiest). One replica is the primary. All writes go to + the primary, the primary replicates to the backups, then acknowledges. Reads + can go to the primary (stronger) or to any replica (weaker, but we must be + careful that reads still return a value not older than the last write the + client saw). +- **Chain replication**. Writes enter the head of the chain and propagate to + the tail. The tail answers reads. This is what the lectures 8 and 9 covered. +- **State machine replication (Raft / Paxos)**. Strongest and most interesting + but also the most code. Only pick this if we really want the full bonus. + +**Recommendation:** primary-backup with synchronous replication is the safest +choice. It is enough for sequential consistency and we already have a pattern +for leader election in [order_executor/src/app.py](order_executor/src/app.py). +We can reuse the same bully-style election to elect the primary database +replica if the primary dies. + +### 4.3 Tasks to do in the repo + +1. Create a new folder `books_database/` next to the other services, with: + - `Dockerfile` + - `requirements.txt` + - `src/app.py` implementing the gRPC server +2. Add a new folder `utils/pb/books_database/` with a `books_database.proto` + file that defines the service messages (`ReadRequest`, `ReadResponse`, + `WriteRequest`, `WriteResponse`, plus internal messages like + `ReplicateWriteRequest` for primary-to-backup replication). +3. Generate the Python stubs next to the proto file (the other services + already do this pattern). +4. In [docker-compose.yaml](docker-compose.yaml), add three services + `books_database_1`, `books_database_2`, `books_database_3`. Give each a + `REPLICA_ID`, a `PEERS` list, and its own port. Reuse the same pattern that + already exists for the order executor replicas (lines 85-137). +5. **Pre-seed the stock.** At startup, each replica should load a small, + fixed list of books with quantities. This is enough for the demo. + +### 4.4 Bonus: concurrent writes on the same book + +The Session 10 page asks us to handle the case where two orders target the +same book at the same time. The primary-backup approach already gives us a +clear single point of serialization (the primary). We can get the bonus by: + +- Taking a per-key lock on the primary during Read-Validate-Write, OR +- Using a small version number per key and rejecting a write whose base + version is stale (optimistic concurrency control). + +Pick one and document it. The README must describe which one we chose. + +--- + +## 5. New work item B: Payment Service + Commitment Protocol + +### 5.1 What to build + +A new service called, for example, `payment_service`, with a gRPC API that +exposes **Prepare**, **Commit**, and **Abort**. It does not need replication +(one instance is enough) and it does not need to really charge a card. It +just needs to log the call and respond. + +We also need a **distributed commitment protocol** that coordinates: + +- the **order executor** as the **coordinator**, and +- the **books_database primary** and **payment_service** as the **two + participants**. + +We recommend **Two-Phase Commit (2PC)** because it is the simplest protocol +that makes the demo convincing, and the guide explicitly allows it. 3PC is a +bonus we can discuss verbally if asked about blocking behavior. + +### 5.2 How 2PC fits into the existing flow + +Today, the leader executor just dequeues an order and logs "executing +order=...". For Checkpoint 3, the leader executor must, for each dequeued +order: + +1. **Phase 1 (Prepare):** + - Send `Prepare(orderId, items)` to the database primary. + The primary reads stock for each requested book and checks there is + enough. If yes, it writes the tentative decrement into a **pending + buffer** (not the committed value yet) and replies `VoteCommit`. + - Send `Prepare(orderId, amount)` to the payment service. It replies + `VoteCommit` for the demo. +2. **Decision:** if both participants voted commit, the coordinator logs + `decision=COMMIT` and sends `Commit(orderId)` to both. Otherwise it sends + `Abort(orderId)` to both. +3. **Phase 2 (Commit/Abort):** + - The database replaces stock values from the pending buffer into the + committed state, and also replicates the commit to the backups through + the chosen consistency protocol. + - The payment service logs the commit. + +The coordinator must also write a log line **before** sending the decision, +so we have a "decision record" visible in `docker compose logs`. This is what +makes 2PC recoverable in theory and what the TA wants to see. + +### 5.3 Tasks to do in the repo + +1. Create `payment_service/` next to the other services with the same layout + as the others. +2. Create `utils/pb/payment_service/payment.proto` with `Prepare`, `Commit`, + `Abort` RPCs. +3. Extend `utils/pb/books_database/books_database.proto` with `Prepare`, + `Commit`, `Abort` (participant side). +4. Rewrite the "execute order" block in + [order_executor/src/app.py](order_executor/src/app.py) so that after a + successful `Dequeue`, the leader runs the 2PC coordinator logic above. +5. Add `payment_service` to [docker-compose.yaml](docker-compose.yaml) with + its own port. The order executor must `depends_on` it. +6. Keep the existing leader election for the executor. Only the leader runs + the coordinator role. This is already guaranteed by the mutual-exclusion + design we use for `Dequeue`. + +### 5.4 Bonus: failing participants and coordinator failure + +Two separate bonus points are offered: + +- **Participant failure (implementation).** Inject a sleep or a forced error + inside the database participant so it fails after voting commit but before + applying the commit. The coordinator must retry the `Commit` RPC until the + participant comes back. + + For the recovery to actually work, the participant must **persist the + staged transaction to a local file** (for example + `/app/state/txn_.json` or a small on-disk write-ahead log) **at + the moment it votes commit**. On restart, the participant reloads the + file and either waits for the coordinator's next `Commit` / `Abort`, or + asks the coordinator for the outcome. Without on-disk persistence, the + participant forgets the staged update when its container dies, and the + coordinator's retries can never succeed. Document this as an experiment + we can run on demo day. + +- **Coordinator failure (analysis only).** In the README or a short section + of [docs/](docs/), explain what happens when the coordinator dies after + sending the decision to only one participant. Explain why this is the + classic "blocking" problem of 2PC, and mention 3PC or a highest-ID + replacement coordinator as mitigations. No code is required for full + points, but a few paragraphs are needed. + +--- + +## 6. Wiring the new services into the existing flow + +The Session 10 guide says we **may comment out** the validation services +(transaction_verification, fraud_detection, suggestions) in +[docker-compose.yaml](docker-compose.yaml) to keep the demo focused on the +new components. We have three realistic options: + +- **Option A (recommended for the final demo).** Keep the three validation + services **on** but keep their role unchanged. They still run the + vector-clock pipeline from Checkpoint 2. Only the work **after** approval + changes: the order executor now runs the 2PC protocol against the database + and the payment service. This option preserves all the Checkpoint 2 + deliverables and gives the TA the whole picture. +- **Option B.** Comment out the three validation services, as the guide + allows. This is simpler to demo but means Checkpoint 2 features are no + longer visible. Only do this if something breaks in the pipeline that we + cannot fix in time. +- **Option C (dev-time flag).** Keep all the code in place but add an + environment variable such as `CP3_EXECUTION_ONLY=true` to + [orchestrator/src/app.py](orchestrator/src/app.py). When the flag is on, + the orchestrator skips the vector-clock pipeline and enqueues the order + directly after basic input validation. When the flag is off, the full + Checkpoint 2 pipeline runs. This avoids deleting code, lets us iterate + on the 2PC path fast, and keeps the demo flexible — we can flip the flag + mid-demo to show either mode. + +We recommend **Option A for the final demo** and **Option C during +development**, so the team can iterate on 2PC without waiting for the full +pipeline on every run. The main code change is then very local to +[order_executor/src/app.py](order_executor/src/app.py) plus the two new +services, plus the prerequisite from §3. + +--- + +## 7. Logging + +The Checkpoint 3 guide awards 1 point for **logging of component +interactions**. We already log vector clocks, queue actions, and leader +election in Checkpoint 2. For Checkpoint 3 we must add at least: + +- **Database replication:** log every write on the primary, every + `ReplicateWrite` call to a backup, and every apply on a backup, with the + key, the old value, and the new value. +- **Consistency protocol:** log "primary=X" on startup and on failover, plus + every Read/Write the executor does. +- **Payment service:** one log line per Prepare/Commit/Abort. +- **2PC coordinator:** one log line per phase, including `decision=COMMIT` + or `decision=ABORT`, and the list of participants and their votes. +- **2PC participants:** one log line per Prepare/Commit/Abort they receive, + including their vote. + +All logs should follow the existing key=value style used in +[orchestrator/src/app.py](orchestrator/src/app.py) so the patterns we already +grep for in [scripts/checkpoint2-checks.ps1](scripts/checkpoint2-checks.ps1) +stay consistent. + +--- + +## 8. Diagrams + +Two new diagrams are required. Both go into [docs/diagrams/](docs/diagrams/). + +1. **Consistency protocol diagram** — shows: + - the three database replicas with clear labels (primary vs backup, or + head / middle / tail if we pick chain replication), + - the executor client, + - Read and Write arrows with the direction of replication. + File name suggestion: `docs/diagrams/consistency-protocol.svg`. +2. **Distributed commitment protocol diagram** — shows: + - the executor as coordinator, + - the database primary and the payment service as participants, + - the two phases (Prepare / Vote, then Commit or Abort) with arrows, + - one success path and, ideally, one failure path. + The guide explicitly allows 2-3 pictures in a sequence. File name + suggestion: `docs/diagrams/commitment-protocol.svg`. + +Both diagrams should be referenced from the updated README in the same way +the existing diagrams are referenced (see lines 115 and 153 in the current +[README.md](README.md)). + +--- + +## 9. Documentation updates + +- **[README.md](README.md):** rename the top header to "Checkpoint 3", and + add two new top-level sections: "Replicated database and consistency + protocol" and "Distributed commitment protocol (2PC)". Each section should + explain: + - what the section delivers and why it satisfies the rubric, + - how to run and observe it in the demo, + - which log lines prove it works. +- **[docs/](docs/):** add a short design note, for example + `docs/consistency-redesign.md` and `docs/commitment-protocol.md`, similar + in style to the existing [docs/vector-clock-redesign.md](docs/vector-clock-redesign.md). + Keep them short: one page each is enough. +- **In-code comments and docstrings.** The Checkpoint 3 guide explicitly + asks us to document the crucial code sections. Short comments or + docstrings are required in, at a minimum: + - the 2PC coordinator block in + [order_executor/src/app.py](order_executor/src/app.py) (one block + comment explaining the phase sequence and what happens on timeout), + - the `Prepare` / `Commit` / `Abort` handlers in the new + `books_database` service (one docstring each, explaining how the + pending buffer works and when replication happens), + - the `Prepare` / `Commit` / `Abort` handlers in the new + `payment_service`, + - any retry or recovery helper used by the coordinator or participants. + + These comments are small but they are a named deliverable of the 1-point + "project organization, documentation, collaboration" rubric item, + alongside the README and the design notes. + +--- + +## 10. Verification and demo support + +The Checkpoint 2 demo script is +[scripts/checkpoint2-checks.ps1](scripts/checkpoint2-checks.ps1). We should +create `scripts/checkpoint3-checks.ps1` that: + +1. runs `docker compose up --build -d` and confirms all containers are up + (now including 3 database replicas and 1 payment service), +2. submits a valid checkout and confirms the order is committed in the + database (stock decremented on all 3 replicas), +3. submits a checkout that would oversell a book and confirms the protocol + aborts cleanly (stock unchanged on all replicas, payment service logged + Abort), +4. kills the database primary and confirms a new primary takes over and + the system still commits a new order, +5. **read-consistency convergence check** — after a successful commit, + reads issued directly against each of the 3 database replicas return + the **same** final stock value. This is the single most direct proof + that the consistency protocol actually works and that all replicas + converge, +6. (bonus) forces the database participant to fail after voting commit and + checks that the coordinator retries until the participant recovers. + +New test payload files should live next to the Checkpoint 2 files: +- `test_checkout_oversold.json` for the abort case, +- `test_checkout_concurrent_a.json` and `test_checkout_concurrent_b.json` + for the concurrent-writes bonus. + +--- + +## 11. Release steps (the day before the demo) + +1. Merge all work into `master`. +2. Run `scripts/checkpoint3-checks.ps1` end to end. All checks must pass. +3. Commit the final state and create the git tag: + ```bash + git tag checkpoint-3 + git push origin checkpoint-3 + ``` +4. Book the evaluation slot in the Google Sheet linked from + . +5. Prepare one laptop with: + - Docker Desktop running, + - the repo at tag `checkpoint-3`, + - both diagrams open as images, + - the README open at the "How to demonstrate" section. + +--- + +## 12. Checklist (to track progress) + +- [ ] Order data model carries `items` (title + quantity) end to end +- [ ] Orchestrator stops collapsing the order to `item_count` +- [ ] Queue proto extended with `repeated OrderItem items` and stubs regenerated +- [ ] `books_database` service with gRPC Read/Write +- [ ] 3 database replicas in `docker-compose.yaml` +- [ ] Consistency protocol implemented (primary-backup recommended) +- [ ] Primary failover reuses bully election pattern +- [ ] `payment_service` with Prepare/Commit/Abort +- [ ] 2PC coordinator logic inside `order_executor` +- [ ] Database participant supports Prepare/Commit/Abort +- [ ] `CP3_EXECUTION_ONLY` dev-time flag in orchestrator +- [ ] Logs updated for replication, consistency, payment, 2PC +- [ ] In-code docstrings added to 2PC coordinator, DB participant, payment participant, recovery helpers +- [ ] `docs/diagrams/consistency-protocol.svg` added +- [ ] `docs/diagrams/commitment-protocol.svg` added +- [ ] README updated to Checkpoint 3 +- [ ] `docs/consistency-redesign.md` and `docs/commitment-protocol.md` added +- [ ] `scripts/checkpoint3-checks.ps1` passes end to end (including replica-convergence check) +- [ ] New test payload files added +- [ ] Git tag `checkpoint-3` created and pushed +- [ ] Bonus: concurrent-writes strategy documented +- [ ] Bonus: participant persists staged transaction to disk before voting commit +- [ ] Bonus: participant-failure recovery demonstrated +- [ ] Bonus: coordinator-failure analysis written +- [ ] Evaluation slot booked in the Google Sheet + +--- + +## 13. Team plan for Checkpoint 3 (who does what) + +The work in this document will be delivered in three steps by two roles: +this branch's author (me) and the team lead (another team member). + +### Step 1 — Implementation on this branch (me) + +On the current feature branch `individual-sten-qy-li`, implement every +Checkpoint 3 requirement described in §§3–10 of this document, **except** +creating the `checkpoint-3` git tag. Concretely, Step 1 covers: + +- §3 order data model prerequisite, +- §4 replicated `books_database` service and consistency protocol, +- §5 `payment_service` and 2PC coordinator logic, +- §6 wiring options (Option A for the demo, Option C dev flag during work), +- §7 logging additions, +- §8 diagrams, +- §9 documentation and in-code docstrings, +- §10 verification script, +- the three bonus items from §4.4 and §5.4. + +### Step 2 — Push to GitHub (me) + +Once all checklist items from §12 are ticked (except "Git tag `checkpoint-3` +created and pushed", which is owned by Step 3), commit the final state on +`individual-sten-qy-li` and push the branch to the GitHub remote: + +```bash +git push origin individual-sten-qy-li +``` + +No `master` merge and no tag are created in this step. The branch is left +in a "ready for review" state. + +### Step 3 — Merge and tag (team lead, not me) + +The team lead will: + +1. merge `individual-sten-qy-li` into `master` (typically via a reviewed + pull request), +2. create the `checkpoint-3` git tag on **the merge commit on `master`**, + not on any commit that lives only on the feature branch, +3. push the tag: + + ```bash + git tag checkpoint-3 + git push origin checkpoint-3 + ``` + +This is the hand-off point. After Step 3, the repository is officially at +Checkpoint 3 state. + +### How this supersedes §11 + +§11 step 3 (run `git tag checkpoint-3` and push) is intentionally **not** +done on this branch. For our team, that step belongs to the team lead on +`master` after the merge. The rest of §11 (final local checks, booking the +evaluation slot, preparing the demo laptop) is still valid and is shared +between me and the team lead as applicable. + +--- + +## 14. Execution phases (implementation order for Step 1 of §13) + +§§3–12 describe **what** to deliver. §13 Step 1 is the big "do all of it" +bucket. This section breaks Step 1 into **13 ordered phases** so progress +is trackable across sessions and the team lead can see at a glance where +we are. Each phase is chosen so the repository ends the phase in a +runnable state (the stack still starts and the previous features still +work), which keeps the blast radius of any single mistake small. + +### Phase table + +| # | Phase | Covers | Ends when | +| --- | --- | --- | --- | +| 1 | Order data model prerequisite | §3 | `docker compose up` still works; a checkout carries real `items` (title + quantity) end to end, visible in both orchestrator and executor logs | +| 2 | `books_database` service (core) | §4.1–4.3 | 3 replicas running; direct gRPC `Read`/`Write` against the primary works; pre-seeded stock is visible | +| 3 | Concurrent-writes bonus | §4.4 | Two simultaneous writes on the same book serialize cleanly (per-key lock on primary, or versioned OCC) | +| 4 | Payment service + DB participant RPCs | §5.1, §5.3 steps 1–3 | `payment_service` container up; both payment and DB answer direct `Prepare` / `Commit` / `Abort` calls | +| 5 | 2PC coordinator in executor | §5.2, §5.3 steps 4–6 | End-to-end successful order decrements stock on all 3 database replicas; `decision=COMMIT` log line present | +| 6 | Participant-failure bonus (on-disk staged txn) | §5.4 item 1 | Forced DB-participant crash after vote recovers on restart; coordinator retries succeed | +| 7 | Coordinator-failure analysis | §5.4 item 2 | Short analysis in `docs/commitment-protocol.md` covering 2PC blocking + mitigations | +| 8 | Wiring + `CP3_EXECUTION_ONLY` dev flag | §6 | Both Option A (full CP2+CP3 pipeline) and Option C (flag on) run end to end | +| 9 | Logging sweep | §7 | `docker compose logs` shows the full 2PC trace using the existing key=value style | +| 10 | Diagrams | §8 | `docs/diagrams/consistency-protocol.svg` and `docs/diagrams/commitment-protocol.svg` present, referenced from README | +| 11 | Documentation | §9 | README renamed to CP3; `docs/consistency-redesign.md`, `docs/commitment-protocol.md`, and the in-code docstrings added | +| 12 | Verification script | §10 | `scripts/checkpoint3-checks.ps1` passes end to end, including the replica-convergence check | +| 13 | Final commit + push | §13 Step 2 | Clean commit on `individual-sten-qy-li` pushed to `origin`; no tag created (that is §13 Step 3, team lead's) | + +### Phase progress log + +This log is updated at the end of each phase. Keep entries short so the +section stays scannable. + +- [x] Phase 1 completed. End-to-end proof: orchestrator log + `received_checkout ... item_count=2 items=[Book Ax3,Book Bx1]` and + executor log `executing order=... items=[Book Ax3,Book Bx1]`. Four + protos extended with `OrderItem` + `repeated OrderItem items = 9;`, + stubs regenerated, orchestrator propagates items, executor logs them, + frontend updated to send `title`. CP2 vector-clock pipeline still + reaches `final_status=APPROVED`. +- [x] Phase 2 completed. Three replicas up with bully election (DB-3 primary; + DB-1 and DB-2 acknowledge). Client-facing `Read` and `Write` on the + primary work; pre-seeded stock visible. `Write(Book A, 7)` committed with + `seq=1`, replicated to DB-1 and DB-2 (both logged `replicate_applied` + old=10 new=7). Non-primary `Write` correctly rejected with + `not primary; primary=3`. New proto `books_database.proto`, new + `books_database/` service folder, three replicas added to + `docker-compose.yaml` on host ports 50058–50060. +- [x] Phase 3 completed. Per-key locks in `books_database/src/app.py` + (`kv_state_lock` meta-lock + `key_locks` dict + `get_key_lock(title)` + + dedicated `seq_lock` for the monotonic sequence). Test A (10 concurrent + writes on `Book A`, quantities 100..109): final=104 (one of the attempted + values), 10 distinct seqs 1..10 on primary with each `old` matching the + previous `new`, both DB-1 and DB-2 applied 1..10 in order. Test B + (5 concurrent writes on 5 different titles): wall-clock 0.04s vs 0.07s + for 5 same-key writes — different keys fan out to backups in parallel + while same-key serializes. No torn state. +- [x] Phase 4 completed. New `payment_service/` runtime (single-instance gRPC + server on port 50061, always votes commit, idempotent on retries). New + `utils/pb/payment_service/payment.proto` with `Prepare` / `Commit` / + `Abort`. `books_database.proto` extended with the same three RPCs plus a + `ReadLocal` debug RPC for the replica-convergence check. Primary-side + `Prepare` stages reservations in `pending_orders` and rejects votes when + stock (minus existing reservations) is insufficient; `Commit` applies each + decrement through the existing synchronous-replication path; `Abort` + drops the reservation. `docker-compose.yaml` now starts `payment_service` + and the executor `depends_on` it and the three DB replicas. Smoke tests + pass: payment round-trip, DB prepare+commit with cross-replica + convergence (Book A 10→8 and Book B 6→5 on DB-1/2/3), abort leaves stock + unchanged, huge request vote-aborts with reason, overlapping prepares + observe prior reservations, prepare is idempotent. +- [ ] Phase 5 — 2PC coordinator in executor +- [ ] Phase 6 — participant-failure bonus (on-disk staged txn) +- [ ] Phase 7 — coordinator-failure analysis +- [ ] Phase 8 — wiring + `CP3_EXECUTION_ONLY` dev flag +- [ ] Phase 9 — logging sweep +- [ ] Phase 10 — diagrams +- [ ] Phase 11 — documentation +- [ ] Phase 12 — verification script +- [ ] Phase 13 — final commit + push + +### Risk notes + +- Phases 5 and 6 are the riskiest (new distributed logic). Expect iteration. +- Phases 1 and 4 involve regenerating Python gRPC stubs. The local + environment must have `grpcio-tools` installed, or the regeneration must + be done inside a container that already has it. +- Phase 12 needs a running Docker Desktop to really pass. + diff --git a/books_database/Dockerfile b/books_database/Dockerfile new file mode 100644 index 000000000..b2ec714c7 --- /dev/null +++ b/books_database/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.11 +WORKDIR /app +COPY ./books_database/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +CMD python utils/other/hotreload.py "books_database/src/app.py" diff --git a/books_database/requirements.txt b/books_database/requirements.txt new file mode 100644 index 000000000..c267def1b --- /dev/null +++ b/books_database/requirements.txt @@ -0,0 +1,3 @@ +grpcio==1.78.0 +grpcio-tools==1.78.0 +watchdog==6.0.0 diff --git a/books_database/src/app.py b/books_database/src/app.py new file mode 100644 index 000000000..7c73e52e4 --- /dev/null +++ b/books_database/src/app.py @@ -0,0 +1,599 @@ +import os +import sys +import time +import threading +from concurrent import futures + +import grpc + +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") + +db_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/books_database") +) +sys.path.insert(0, db_grpc_path) + +import books_database_pb2 as db_pb2 +import books_database_pb2_grpc as db_grpc + + +REPLICA_ID = int(os.getenv("REPLICA_ID", "1")) +REPLICA_PORT = os.getenv("REPLICA_PORT", "50058") +HEARTBEAT_INTERVAL = 2.0 +LEADER_TIMEOUT = 5.0 +REPLICATE_TIMEOUT = 2.0 + +SEED_STOCK = { + "Book A": 10, + "Book B": 6, + "Book C": 20, + "Distributed Systems Basics": 5, + "Designing Data-Intensive Applications": 3, +} + + +def parse_peers(): + peers = [] + raw = os.getenv("PEERS", "") + for item in raw.split(","): + item = item.strip() + if not item: + continue + peer_id, peer_addr = item.split("@", 1) + peers.append((int(peer_id), peer_addr)) + return peers + + +PEERS = parse_peers() + + +# --- Bully election state --- + +state_lock = threading.Lock() +leader_id = None +last_heartbeat = time.time() +is_leader = False +election_in_progress = False + + +# --- KV store state --- +# +# We use fine-grained per-key locks so two writes against *different* books +# can run in parallel while two writes against the *same* book are +# serialized. This gives us the concurrent-writes bonus (§4.4) without +# breaking read-validate-write atomicity. kv_state_lock is a short meta-lock +# that only covers lookups in key_locks and kv_store; it is never held while +# we fan out to backups. + +kv_state_lock = threading.Lock() +kv_store = dict(SEED_STOCK) +key_locks = {} # title -> threading.Lock + +seq_lock = threading.Lock() +seq_counter = 0 + +# --- 2PC participant state --- +# +# pending_orders[order_id] = list of (title, quantity) reservations. +# A Prepare inserts the reservation here and we "hold" stock against it when +# evaluating subsequent Prepares. A Commit reads the reservation, applies +# the decrement to kv_store (and replicates to backups), then drops the +# pending entry. An Abort just drops. All three handlers take pending_lock +# so concurrent 2PC ops on the same or different orders serialize cleanly. +pending_lock = threading.Lock() +pending_orders = {} + + +def get_key_lock(title): + with kv_state_lock: + lock = key_locks.get(title) + if lock is None: + lock = threading.Lock() + key_locks[title] = lock + return lock + + +def peer_addr_for(pid): + for p, addr in PEERS: + if p == pid: + return addr + return "" + + +def has_fresh_leader_locked(): + if leader_id is None: + return False + if is_leader and leader_id == REPLICA_ID: + return True + return (time.time() - last_heartbeat) <= LEADER_TIMEOUT + + +def send_rpc(addr, fn): + try: + with grpc.insecure_channel(addr) as channel: + stub = db_grpc.BooksDatabaseServiceStub(channel) + return fn(stub) + except Exception: + return None + + +def announce_coordinator(): + for pid, addr in PEERS: + if pid == REPLICA_ID: + continue + send_rpc( + addr, + lambda stub: stub.Coordinator( + db_pb2.CoordinatorRequest(leader_id=REPLICA_ID), + timeout=2.0, + ), + ) + + +def start_election(): + global election_in_progress, leader_id + + with state_lock: + if election_in_progress: + return + if has_fresh_leader_locked(): + return + election_in_progress = True + + print(f"[DB-{REPLICA_ID}] starting election") + + higher_peers = [(pid, addr) for pid, addr in PEERS if pid > REPLICA_ID] + got_answer = False + + for _pid, addr in higher_peers: + response = send_rpc( + addr, + lambda stub: stub.Election( + db_pb2.ElectionRequest(candidate_id=REPLICA_ID), + timeout=2.0, + ), + ) + if response and response.alive: + got_answer = True + + if not got_answer: + become_leader() + return + + time.sleep(LEADER_TIMEOUT) + + with state_lock: + fresh_leader = has_fresh_leader_locked() + election_in_progress = False + + if not fresh_leader: + with state_lock: + leader_id = None + start_election() + + +def become_leader(): + global leader_id, is_leader, election_in_progress, last_heartbeat + with state_lock: + leader_id = REPLICA_ID + is_leader = True + election_in_progress = False + last_heartbeat = time.time() + + print(f"[DB-{REPLICA_ID}] became primary") + announce_coordinator() + + +def heartbeat_loop(): + while True: + time.sleep(HEARTBEAT_INTERVAL) + with state_lock: + leader_now = is_leader + if not leader_now: + continue + for pid, addr in PEERS: + if pid == REPLICA_ID: + continue + send_rpc( + addr, + lambda stub: stub.Heartbeat( + db_pb2.HeartbeatRequest(leader_id=REPLICA_ID), + timeout=2.0, + ), + ) + + +def timeout_loop(): + global leader_id + while True: + time.sleep(1.0) + with state_lock: + if is_leader or election_in_progress: + continue + if leader_id is None: + continue + expired = (time.time() - last_heartbeat) > LEADER_TIMEOUT + if expired: + print(f"[DB-{REPLICA_ID}] primary timeout detected") + with state_lock: + leader_id = None + start_election() + + +# --- Replication helper (called by the primary on Write) --- + +def replicate_to_backups(title, quantity, seq): + targets = [(pid, addr) for pid, addr in PEERS if pid != REPLICA_ID] + results = {} + + def do_one(pid, addr): + resp = send_rpc( + addr, + lambda stub: stub.ReplicateWrite( + db_pb2.ReplicateWriteRequest( + title=title, + quantity=quantity, + seq=seq, + from_replica=REPLICA_ID, + ), + timeout=REPLICATE_TIMEOUT, + ), + ) + results[pid] = resp + + threads = [ + threading.Thread(target=do_one, args=(pid, addr)) + for pid, addr in targets + ] + for t in threads: + t.start() + for t in threads: + t.join(timeout=REPLICATE_TIMEOUT + 1.0) + + acked = [pid for pid, r in results.items() if r is not None and r.success] + missing = [pid for pid, _ in targets if pid not in acked] + return acked, missing + + +# --- gRPC service --- + +class BooksDatabaseService(db_grpc.BooksDatabaseServiceServicer): + + # Client-facing RPCs (Phase 2: primary-only for strong consistency). + + def Read(self, request, context): + with state_lock: + if not is_leader: + msg = f"not primary; primary={leader_id}" + print(f"[DB-{REPLICA_ID}] read_rejected title={request.title} reason={msg}") + return db_pb2.ReadResponse(success=False, quantity=0, message=msg) + + key_lock = get_key_lock(request.title) + with key_lock: + with kv_state_lock: + value = kv_store.get(request.title) + + if value is None: + print(f"[DB-{REPLICA_ID}] read_miss title=\"{request.title}\"") + return db_pb2.ReadResponse( + success=False, quantity=0, message="unknown title" + ) + + print( + f"[DB-{REPLICA_ID}] read_ok title=\"{request.title}\" value={value}" + ) + return db_pb2.ReadResponse(success=True, quantity=value, message="ok") + + def ReadLocal(self, request, context): + """Debug/ops read. Returns whatever this replica currently holds, + regardless of leader status. Used only by the convergence check.""" + key_lock = get_key_lock(request.title) + with key_lock: + with kv_state_lock: + value = kv_store.get(request.title) + + if value is None: + return db_pb2.ReadResponse( + success=False, quantity=0, message="unknown title" + ) + return db_pb2.ReadResponse(success=True, quantity=value, message="ok") + + def Write(self, request, context): + global seq_counter + + with state_lock: + if not is_leader: + msg = f"not primary; primary={leader_id}" + print( + f"[DB-{REPLICA_ID}] write_rejected " + f"title=\"{request.title}\" reason={msg}" + ) + return db_pb2.WriteResponse(success=False, message=msg) + + # Per-key lock: concurrent writes on the *same* title serialize here + # while concurrent writes on *different* titles run in parallel. + key_lock = get_key_lock(request.title) + with key_lock: + with kv_state_lock: + old = kv_store.get(request.title) + + with seq_lock: + seq_counter += 1 + seq = seq_counter + + acked, missing = replicate_to_backups( + request.title, request.quantity, seq + ) + + if missing: + print( + f"[DB-{REPLICA_ID}] write_failed " + f"title=\"{request.title}\" seq={seq} " + f"old={old} new={request.quantity} " + f"acked={acked} missing={missing}" + ) + return db_pb2.WriteResponse( + success=False, + message=f"replication incomplete; missing backups {missing}", + ) + + with kv_state_lock: + kv_store[request.title] = request.quantity + + print( + f"[DB-{REPLICA_ID}] write_committed primary={REPLICA_ID} " + f"title=\"{request.title}\" seq={seq} " + f"old={old} new={request.quantity} backups_acked={acked}" + ) + return db_pb2.WriteResponse(success=True, message="ok") + + # Internal RPCs. + + def ReplicateWrite(self, request, context): + global seq_counter + + # Per-key lock on the backup too: defensive — the primary already + # serializes replicates for the same key, but this guards against + # any future path that might not. + key_lock = get_key_lock(request.title) + with key_lock: + with kv_state_lock: + old = kv_store.get(request.title) + kv_store[request.title] = request.quantity + with seq_lock: + if request.seq > seq_counter: + seq_counter = request.seq + + print( + f"[DB-{REPLICA_ID}] replicate_applied " + f"from_primary={request.from_replica} " + f"title=\"{request.title}\" seq={request.seq} " + f"old={old} new={request.quantity}" + ) + return db_pb2.ReplicateWriteResponse(success=True, message="ok") + + def WhoIsPrimary(self, request, context): + with state_lock: + current = leader_id if leader_id is not None else 0 + addr = peer_addr_for(current) if current else "" + return db_pb2.WhoIsPrimaryResponse(leader_id=current, leader_addr=addr) + + # Bully election RPCs. + + def Election(self, request, context): + global election_in_progress + + if REPLICA_ID <= request.candidate_id: + return db_pb2.ElectionResponse(alive=False) + + print(f"[DB-{REPLICA_ID}] received election from {request.candidate_id}") + + with state_lock: + already_leader = is_leader + election_running = election_in_progress + + if already_leader: + threading.Thread(target=announce_coordinator, daemon=True).start() + elif not election_running: + threading.Thread(target=start_election, daemon=True).start() + + return db_pb2.ElectionResponse(alive=True) + + def Coordinator(self, request, context): + global leader_id, is_leader, election_in_progress, last_heartbeat + with state_lock: + leader_id = request.leader_id + is_leader = leader_id == REPLICA_ID + election_in_progress = False + last_heartbeat = time.time() + + print(f"[DB-{REPLICA_ID}] new primary is {leader_id}") + return db_pb2.Ack(ok=True) + + def Heartbeat(self, request, context): + global leader_id, is_leader, last_heartbeat + with state_lock: + leader_id = request.leader_id + is_leader = leader_id == REPLICA_ID + last_heartbeat = time.time() + return db_pb2.Ack(ok=True) + + # --- 2PC participant RPCs --- + + def Prepare(self, request, context): + """Phase 1 of 2PC. Check that each requested item has enough stock + once existing reservations are subtracted, then stage the order in + pending_orders and return vote_commit=True. If any item is short, + return vote_commit=False and stage nothing.""" + with state_lock: + if not is_leader: + msg = f"not primary; primary={leader_id}" + print( + f"[DB-{REPLICA_ID}] prepare_rejected " + f"order={request.order_id} reason={msg}" + ) + return db_pb2.PrepareResponse(vote_commit=False, message=msg) + + order_id = request.order_id + items = [(it.title, it.quantity) for it in request.items] + + with pending_lock: + if order_id in pending_orders: + print( + f"[DB-{REPLICA_ID}] prepare_idempotent order={order_id} " + f"(already prepared)" + ) + return db_pb2.PrepareResponse( + vote_commit=True, message="already prepared" + ) + + with kv_state_lock: + stock_snapshot = {t: kv_store.get(t) for t, _ in items} + + reserved = {} + for staged in pending_orders.values(): + for t, q in staged: + reserved[t] = reserved.get(t, 0) + q + + insufficient = [] + for title, qty in items: + current = stock_snapshot.get(title) + if current is None: + insufficient.append(f"{title}(unknown)") + continue + available = current - reserved.get(title, 0) + if available < qty: + insufficient.append( + f"{title}(want={qty},avail={available})" + ) + + if insufficient: + print( + f"[DB-{REPLICA_ID}] prepare_vote_abort " + f"order={order_id} reasons={insufficient}" + ) + return db_pb2.PrepareResponse( + vote_commit=False, + message=f"insufficient stock: {insufficient}", + ) + + pending_orders[order_id] = items + + items_repr = ",".join(f"{t}x{q}" for t, q in items) + print( + f"[DB-{REPLICA_ID}] prepare_vote_commit " + f"order={order_id} items=[{items_repr}]" + ) + return db_pb2.PrepareResponse(vote_commit=True, message="ok") + + def Commit(self, request, context): + """Phase 2 of 2PC. Apply the staged decrements to kv_store, replicate + each one to the backups synchronously, then drop the pending entry. + If any backup fails to ack, leave pending in place and report failure + so the coordinator can retry.""" + global seq_counter + + with state_lock: + if not is_leader: + msg = f"not primary; primary={leader_id}" + print( + f"[DB-{REPLICA_ID}] commit_rejected " + f"order={request.order_id} reason={msg}" + ) + return db_pb2.CommitResponse(success=False, message=msg) + + order_id = request.order_id + + with pending_lock: + items = pending_orders.get(order_id) + if items is None: + # Either never prepared or already committed. Treat as no-op. + print( + f"[DB-{REPLICA_ID}] commit_noop order={order_id} " + f"reason=no-pending" + ) + return db_pb2.CommitResponse( + success=True, message="no pending (already committed?)" + ) + + applied = [] + for title, qty in items: + with kv_state_lock: + old = kv_store.get(title, 0) + new_value = old - qty + with seq_lock: + seq_counter += 1 + seq = seq_counter + + acked, missing = replicate_to_backups(title, new_value, seq) + if missing: + print( + f"[DB-{REPLICA_ID}] commit_replicate_failed " + f"order={order_id} title=\"{title}\" seq={seq} " + f"missing={missing}" + ) + return db_pb2.CommitResponse( + success=False, + message=f"replication failed; missing {missing}", + ) + + with kv_state_lock: + kv_store[title] = new_value + applied.append((title, old, new_value, seq, acked)) + + del pending_orders[order_id] + + for title, old, new_value, seq, acked in applied: + print( + f"[DB-{REPLICA_ID}] commit_applied order={order_id} " + f"title=\"{title}\" seq={seq} old={old} new={new_value} " + f"backups_acked={acked}" + ) + return db_pb2.CommitResponse(success=True, message="ok") + + def Abort(self, request, context): + """Drop the staged reservation for this order. Idempotent: aborting + an order that was never prepared (or already committed/aborted) is + a successful no-op.""" + order_id = request.order_id + with pending_lock: + items = pending_orders.pop(order_id, None) + + if items is None: + print(f"[DB-{REPLICA_ID}] abort_noop order={order_id}") + return db_pb2.AbortResponse(success=True, message="no pending") + + items_repr = ",".join(f"{t}x{q}" for t, q in items) + print( + f"[DB-{REPLICA_ID}] abort_ok order={order_id} " + f"dropped=[{items_repr}]" + ) + return db_pb2.AbortResponse(success=True, message="ok") + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + db_grpc.add_BooksDatabaseServiceServicer_to_server( + BooksDatabaseService(), server + ) + server.add_insecure_port("[::]:" + REPLICA_PORT) + server.start() + print( + f"[DB-{REPLICA_ID}] listening on port {REPLICA_PORT} " + f"seeded_titles={list(SEED_STOCK.keys())}" + ) + + threading.Thread(target=heartbeat_loop, daemon=True).start() + threading.Thread(target=timeout_loop, daemon=True).start() + + time.sleep(1.0) + with state_lock: + should_start = (leader_id is None) and (not election_in_progress) + if should_start: + start_election() + + server.wait_for_termination() + + +if __name__ == "__main__": + serve() diff --git a/books_database/tests/test_concurrent_writes.py b/books_database/tests/test_concurrent_writes.py new file mode 100644 index 000000000..5dd675684 --- /dev/null +++ b/books_database/tests/test_concurrent_writes.py @@ -0,0 +1,132 @@ +"""Phase 3 verification: per-key locks allow concurrent writes on different +keys to proceed in parallel, while concurrent writes on the same key +serialize cleanly. + +Run from host: + python books_database/tests/test_concurrent_writes.py +""" + +import os +import sys +import time +import threading + +import grpc + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.abspath(os.path.join(HERE, "../../utils/pb/books_database"))) + +import books_database_pb2 as db_pb2 +import books_database_pb2_grpc as db_grpc + +# Host ports for the three replicas (see docker-compose.yaml). +PRIMARY_CANDIDATES = [ + ("127.0.0.1:50058", 1), + ("127.0.0.1:50059", 2), + ("127.0.0.1:50060", 3), +] + + +def find_primary(): + """Ask any replica who the primary is and return its host-side address.""" + # Map replica_id -> host_addr + id_to_host = {rid: addr for addr, rid in PRIMARY_CANDIDATES} + for addr, _ in PRIMARY_CANDIDATES: + try: + with grpc.insecure_channel(addr) as ch: + stub = db_grpc.BooksDatabaseServiceStub(ch) + r = stub.WhoIsPrimary(db_pb2.WhoIsPrimaryRequest(), timeout=2.0) + if r.leader_id: + return id_to_host[r.leader_id], r.leader_id + except Exception: + continue + raise RuntimeError("no primary found") + + +def write_one(addr, title, quantity, results, idx, barrier): + barrier.wait() + t0 = time.time() + try: + with grpc.insecure_channel(addr) as ch: + stub = db_grpc.BooksDatabaseServiceStub(ch) + r = stub.Write( + db_pb2.WriteRequest(title=title, quantity=quantity), + timeout=10.0, + ) + ok, msg = r.success, r.message + except Exception as exc: + ok, msg = False, f"rpc_error={exc}" + results[idx] = (title, quantity, ok, msg, t0, time.time()) + + +def read_one(addr, title): + with grpc.insecure_channel(addr) as ch: + stub = db_grpc.BooksDatabaseServiceStub(ch) + return stub.Read(db_pb2.ReadRequest(title=title), timeout=3.0) + + +def run_concurrent(addr, plan, label): + """plan = list of (title, quantity) tuples, all fired at once.""" + n = len(plan) + results = [None] * n + barrier = threading.Barrier(n) + threads = [ + threading.Thread( + target=write_one, + args=(addr, title, qty, results, i, barrier), + ) + for i, (title, qty) in enumerate(plan) + ] + start = time.time() + for t in threads: + t.start() + for t in threads: + t.join(timeout=30.0) + elapsed = time.time() - start + + all_ok = all(r is not None and r[2] for r in results) + print(f"\n== {label}: {n} concurrent writes -> elapsed={elapsed:.2f}s all_ok={all_ok}") + for r in results: + title, qty, ok, msg, t0, t1 = r + print(f" title=\"{title}\" qty={qty} ok={ok} latency={(t1-t0):.2f}s msg={msg!r}") + return elapsed, all_ok + + +def main(): + addr, pid = find_primary() + print(f"primary = DB-{pid} @ {addr}") + + # Test B: 5 concurrent writes on 5 DIFFERENT keys. With per-key locks + # these should fan out to backups in parallel — wall-clock well under + # 5 * (single-write latency). + different_plan = [ + ("Book A", 201), + ("Book B", 202), + ("Book C", 203), + ("Distributed Systems Basics", 204), + ("Designing Data-Intensive Applications", 205), + ] + elapsed_diff, ok_diff = run_concurrent(addr, different_plan, "TEST B (different keys)") + + # Re-confirm per-key serialization baseline: 5 concurrent writes on SAME key. + same_plan = [("Book A", 300 + i) for i in range(5)] + elapsed_same, ok_same = run_concurrent(addr, same_plan, "TEST A' (same key, smaller)") + + # Verify final value is one of the attempted values (no torn state). + r_a = read_one(addr, "Book A") + consistent_a = r_a.success and (r_a.quantity in {300, 301, 302, 303, 304}) + print(f"\nfinal Book A = {r_a.quantity} consistent={consistent_a}") + + for title, qty in different_plan: + r = read_one(addr, title) + print(f" {title} = {r.quantity} (expected {qty}) match={r.quantity == qty}") + + print() + print(f"elapsed(different keys) = {elapsed_diff:.2f}s") + print(f"elapsed(same key) = {elapsed_same:.2f}s") + print(" -> different-keys should be roughly same as one write's latency,") + print(" same-key should be ~N * single-write latency (serialized).") + + +if __name__ == "__main__": + main() diff --git a/docker-compose.yaml b/docker-compose.yaml index b8ae862d0..8971fac37 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -99,6 +99,10 @@ services: - ./order_executor/src:/app/order_executor/src depends_on: - order_queue + - payment_service + - books_database_1 + - books_database_2 + - books_database_3 order_executor_2: build: @@ -117,6 +121,10 @@ services: - ./order_executor/src:/app/order_executor/src depends_on: - order_queue + - payment_service + - books_database_1 + - books_database_2 + - books_database_3 order_executor_3: build: @@ -134,4 +142,74 @@ services: - ./utils:/app/utils - ./order_executor/src:/app/order_executor/src depends_on: - - order_queue \ No newline at end of file + - order_queue + - payment_service + - books_database_1 + - books_database_2 + - books_database_3 + + payment_service: + build: + context: ./ + dockerfile: ./payment_service/Dockerfile + ports: + - "50061:50061" + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/payment_service/src/app.py + - PYTHONPATH=/app + - PAYMENT_PORT=50061 + volumes: + - ./utils:/app/utils + - ./payment_service/src:/app/payment_service/src + + books_database_1: + build: + context: ./ + dockerfile: ./books_database/Dockerfile + ports: + - "50058:50058" + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/books_database/src/app.py + - PYTHONPATH=/app + - REPLICA_ID=1 + - REPLICA_PORT=50058 + - PEERS=1@books_database_1:50058,2@books_database_2:50058,3@books_database_3:50058 + volumes: + - ./utils:/app/utils + - ./books_database/src:/app/books_database/src + + books_database_2: + build: + context: ./ + dockerfile: ./books_database/Dockerfile + ports: + - "50059:50058" + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/books_database/src/app.py + - PYTHONPATH=/app + - REPLICA_ID=2 + - REPLICA_PORT=50058 + - PEERS=1@books_database_1:50058,2@books_database_2:50058,3@books_database_3:50058 + volumes: + - ./utils:/app/utils + - ./books_database/src:/app/books_database/src + + books_database_3: + build: + context: ./ + dockerfile: ./books_database/Dockerfile + ports: + - "50060:50058" + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/books_database/src/app.py + - PYTHONPATH=/app + - REPLICA_ID=3 + - REPLICA_PORT=50058 + - PEERS=1@books_database_1:50058,2@books_database_2:50058,3@books_database_3:50058 + volumes: + - ./utils:/app/utils + - ./books_database/src:/app/books_database/src \ No newline at end of file diff --git a/frontend/src/index.html b/frontend/src/index.html index 2ace91b7d..a1785e871 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -86,14 +86,14 @@

      Items

    MZ+YuN-Q*EVQxYoSYCFeL>S0 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 10: + if request.item_count > 20: is_fraud = True message = "Too many items in order." + + # Treat any non-16-digit card number as invalid + elif len(card_digits) != 16: + is_fraud = True + message = "Invalid card number." + + elif request.card_number.startswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif request.card_number.endswith("0000"): is_fraud = True message = "Suspicious card number pattern." + elif "fraud" in request.user_name.lower(): is_fraud = True message = "Suspicious user name." @@ -49,6 +64,13 @@ def CheckFraud(self, request, context): return response +def extract_card_digits(card: str) -> str: + """ + Return only the digit characters from the given card number. + """ + return ''.join(c for c in str(card) if c.isdigit()) + + def serve(): server = grpc.server(futures.ThreadPoolExecutor()) fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) @@ -59,6 +81,10 @@ def serve(): print("Fraud detection server started. Listening on port 50051.") server.wait_for_termination() +def mask_fixed(card: str) -> str: + digits = ''.join(c for c in str(card) if c.isdigit()) + masked = '*' * 12 + digits[-4:].rjust(4, '*') + return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) if __name__ == '__main__': serve() \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 64341eb19..5e1cae6cf 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -116,19 +116,15 @@ def checkout(): credit_card = user.get("creditCard", {}) card_number = credit_card.get("number") + masked_card_number = mask_fixed(card_number) expiration_date = credit_card.get("expirationDate") cvv = credit_card.get("cvv") + print( + "Received a request for checkout of user : {} for card number : {}".format( + user_name, masked_card_number + ) + ) - print("FULL REQUEST DATA:", request_data) - print("USER NAME:", user_name) - print("USER CONTACT:", user_contact) - print("USER COMMENT:", user_comment) - print("ITEMS:", items) - print("SHIPPING METHOD:", shipping_method) - print("TERMS ACCEPTED:", terms_accepted) - print("CARD NUMBER:", card_number) - print("EXPIRATION DATE:", expiration_date) - print("CVV:", cvv) # Keep these simple bad-request checks locally if not user_name: @@ -264,6 +260,10 @@ def suggestions_worker(): "suggestedBooks": suggested_books }, 200 +def mask_fixed(card: str) -> str: + digits = ''.join(c for c in str(card) if c.isdigit()) + masked = '*' * 12 + digits[-4:].rjust(4, '*') + return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) if __name__ == '__main__': app.run(host='0.0.0.0') \ No newline at end of file diff --git a/system_diagram.png b/system_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ba1b6c564c798b71c51af2daa4c56314fe7684 GIT binary patch literal 239025 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%0A&rArdDgY4=sHqlycKb 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�ZAp@{y5O~KU2aWZqySpG9 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 $QQM5^ez81cj%B#SzGnUpY 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 U9=&TqAuD)FQT2F|{Ds^91)wa+k^lez literal 0 HcmV?d00001 diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index 36f592052..b1087e51f 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -20,9 +20,13 @@ def VerifyTransaction(self, request, context): print("Received transaction verification request") print("user_name:", request.user_name) print("user_contact:", request.user_contact) + masked_card_number = mask_fixed(request.card_number) + print("card_number:", masked_card_number) print("item_count:", request.item_count) print("terms_accepted:", request.terms_accepted) - + # Compute length based on digits only to avoid counting spaces or other characters + card_digits = extract_card_digits(request.card_number) + print("card length (digits only):", len(card_digits)) is_valid = True message = "Transaction is valid." @@ -42,6 +46,11 @@ def VerifyTransaction(self, request, context): is_valid = False message = "Missing credit card information." + # Treat any non-16-digit card number as invalid + elif len(card_digits) != 16: + is_valid = False + message = "Invalid card number." + response = transaction_verification.TransactionVerificationResponse() response.is_valid = is_valid response.message = message @@ -50,6 +59,13 @@ def VerifyTransaction(self, request, context): return response +def extract_card_digits(card: str) -> str: + """ + Return only the digit characters from the given card number. + """ + return ''.join(c for c in str(card) if c.isdigit()) + + def serve(): server = grpc.server(futures.ThreadPoolExecutor()) transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( @@ -62,6 +78,10 @@ def serve(): print("Transaction verification server started. Listening on port 50052.") server.wait_for_termination() +def mask_fixed(card: str) -> str: + digits = ''.join(c for c in str(card) if c.isdigit()) + masked = '*' * 12 + digits[-4:].rjust(4, '*') + return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) if __name__ == '__main__': serve() \ No newline at end of file From 5ad3d74fad39b1a5b331b79080c1d0333b2330fa Mon Sep 17 00:00:00 2001 From: anupkumar Date: Mon, 2 Mar 2026 16:55:05 +0200 Subject: [PATCH 18/58] incremental changes --- README.md | 4 ++-- fraud_detection/src/app.py | 5 ++--- orchestrator/src/app.py | 30 +++++++++++++++--------------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 009c93749..e34340b5a 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ The following diagrams give a high-level view of the system and how the services **System overview** -![System diagram](../system_diagram.png) +![System diagram](./system_diagram.png) **Service architecture** -![Service architecture diagram](../architecture_diagram.png) +![Service architecture diagram](./architecture_diagram.png) ### Running the code with Docker Compose [recommended] diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index 10c6b4264..ecee0fad5 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -44,14 +44,13 @@ def CheckFraud(self, request, context): is_fraud = True message = "Invalid card number." - elif request.card_number.startswith("0000"): + elif card_digits.startswith("0000"): is_fraud = True message = "Suspicious card number pattern." - elif request.card_number.endswith("0000"): + elif card_digits.endswith("0000"): is_fraud = True message = "Suspicious card number pattern." - elif "fraud" in request.user_name.lower(): is_fraud = True message = "Suspicious user name." diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 5e1cae6cf..590815ff5 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -49,11 +49,11 @@ def detect_fraud(user_name, card_number, item_count): user_name=user_name or "", card_number=card_number or "", item_count=item_count - ) + ), + timeout=5.0 # seconds ) return response - def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): with grpc.insecure_channel('transaction_verification:50052') as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) @@ -110,15 +110,16 @@ def checkout(): shipping_method = request_data.get("shippingMethod") terms_accepted = request_data.get("termsAndConditionsAccepted", False) - user_name = user.get("name") - user_contact = user.get("contact") + # Normalize user fields and enforce that user_name is not empty or just spaces + user_name = (user.get("name") or "").strip() + user_contact = (user.get("contact") or "").strip() user_comment = user.get("userComment", "") credit_card = user.get("creditCard", {}) - card_number = credit_card.get("number") + card_number = credit_card.get("number").strip() masked_card_number = mask_fixed(card_number) - expiration_date = credit_card.get("expirationDate") - cvv = credit_card.get("cvv") + expiration_date = credit_card.get("expirationDate").strip() + cvv = credit_card.get("cvv").strip() print( "Received a request for checkout of user : {} for card number : {}".format( user_name, masked_card_number @@ -127,19 +128,20 @@ def checkout(): # Keep these simple bad-request checks locally - if not user_name: + # user_name must not be empty or contain spaces + if not user_name or "" == user_name: return { "error": { "code": "BAD_REQUEST", - "message": "User name is required." + "message": "User name is required and must not contain spaces." } }, 400 - if not user_contact: + if not user_contact or "" == user_contact: return { "error": { "code": "BAD_REQUEST", - "message": "User contact is required." + "message": "User contact is required and must not contain spaces." } }, 400 @@ -166,10 +168,8 @@ def fraud_worker(): results["fraud"].message ) except Exception as e: - error_msg = f"fraud_detection failed: {e}" - print(error_msg) - results["errors"].append(error_msg) - + print(f"fraud_detection failed: {e}") # Full detail for server logs + results["errors"].append("fraud_detection service unavailable") # Sanitized for client def verification_worker(): try: print("Calling transaction_verification service...") From 10d90111ec0c87c9081c547a201af9c5f6c554e2 Mon Sep 17 00:00:00 2001 From: Qun Yan Li Date: Wed, 11 Mar 2026 11:48:01 +0200 Subject: [PATCH 19/58] Implement Seminar 5 event ordering with vector clocks --- fraud_detection/requirements.txt | 7 +- fraud_detection/src/app.py | 209 +++++-- orchestrator/requirements.txt | 7 +- orchestrator/src/app.py | 532 ++++++++++++------ suggestions/requirements.txt | 7 +- suggestions/src/app.py | 202 ++++++- transaction_verification/requirements.txt | 7 +- transaction_verification/src/app.py | 238 ++++++-- .../pb/fraud_detection/fraud_detection.proto | 53 +- .../pb/fraud_detection/fraud_detection_pb2.py | 42 +- .../fraud_detection/fraud_detection_pb2.pyi | 69 ++- .../fraud_detection_pb2_grpc.py | 212 +++++-- utils/pb/suggestions/suggestions.proto | 50 +- utils/pb/suggestions/suggestions_pb2.py | 44 +- utils/pb/suggestions/suggestions_pb2.pyi | 68 ++- utils/pb/suggestions/suggestions_pb2_grpc.py | 192 ++++++- .../transaction_verification.proto | 48 +- .../transaction_verification_pb2.py | 38 +- .../transaction_verification_pb2.pyi | 51 +- .../transaction_verification_pb2_grpc.py | 235 +++++++- 20 files changed, 1820 insertions(+), 491 deletions(-) diff --git a/fraud_detection/requirements.txt b/fraud_detection/requirements.txt index a80eedef7..52b5881e3 100644 --- a/fraud_detection/requirements.txt +++ b/fraud_detection/requirements.txt @@ -1,4 +1,5 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 + + watchdog==6.0.0 diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index ecee0fad5..28f4c1328 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -1,11 +1,11 @@ -import sys import os +import sys +import threading from concurrent import futures -# This set of lines are needed to import the gRPC stubs. -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") fraud_detection_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/fraud_detection') + os.path.join(FILE, "../../../utils/pb/fraud_detection") ) sys.path.insert(0, fraud_detection_grpc_path) @@ -14,76 +14,167 @@ import fraud_detection_pb2_grpc as fraud_detection_grpc -class HelloService(fraud_detection_grpc.HelloServiceServicer): - def SayHello(self, request, context): - response = fraud_detection.HelloResponse() - response.greeting = "Hello, " + request.name - print(response.greeting) - return response - - def CheckFraud(self, request, context): - print("Received fraud check request") - print("user_name:", request.user_name) - masked_card_number = mask_fixed(request.card_number) - print("card_number:", masked_card_number) - print("item_count:", request.item_count) - - # Compute length based on digits only to avoid counting spaces or other characters - card_digits = extract_card_digits(request.card_number) - print("card length (digits only):", len(card_digits)) - is_fraud = False - message = "No fraud detected." - - # Very simple dummy rules for now - if request.item_count > 20: - is_fraud = True - message = "Too many items in order." - - # Treat any non-16-digit card number as invalid - elif len(card_digits) != 16: - is_fraud = True - message = "Invalid card number." +SERVICE_INDEX = 1 # [transaction_verification, fraud_detection, suggestions] - elif card_digits.startswith("0000"): - is_fraud = True - message = "Suspicious card number pattern." +orders = {} +orders_lock = threading.Lock() - elif card_digits.endswith("0000"): - is_fraud = True - message = "Suspicious card number pattern." - elif "fraud" in request.user_name.lower(): - is_fraud = True - message = "Suspicious user name." - response = fraud_detection.FraudCheckResponse() - response.is_fraud = is_fraud - response.message = message +def merge_vc(local_vc, incoming_vc): + return [max(a, b) for a, b in zip(local_vc, incoming_vc)] - print("Returning fraud result:", response.is_fraud, response.message) - return response + +def tick(vc, idx): + vc[idx] += 1 + return vc def extract_card_digits(card: str) -> str: - """ - Return only the digit characters from the given card number. - """ - return ''.join(c for c in str(card) if c.isdigit()) + return "".join(c for c in str(card) if c.isdigit()) + + +def get_order_state(order_id: str): + with orders_lock: + return orders.get(order_id) + + +class FraudDetectionService(fraud_detection_grpc.FraudDetectionServiceServicer): + def InitOrder(self, request, context): + order = request.order + + with orders_lock: + orders[order.order_id] = { + "order": order, + "vc": [0, 0, 0], + } + + print(f"[FD] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") + + return fraud_detection.EventResponse( + success=True, + message="Fraud service initialized order.", + vc=fraud_detection.VectorClock(values=[0, 0, 0]), + ) + + def CheckUserFraud(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return fraud_detection.EventResponse( + success=False, + message="Order not found in fraud service.", + vc=fraud_detection.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + user_name = state["order"].user_name + success = "fraud" not in user_name.lower() + message = "User fraud check passed." if success else "Suspicious user name." + + print( + f"[FD] order={request.order_id} event=CheckUserFraud " + f"vc={vc} success={success}" + ) + + return fraud_detection.EventResponse( + success=success, + message=message, + vc=fraud_detection.VectorClock(values=vc), + ) + + def CheckCardFraud(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return fraud_detection.EventResponse( + success=False, + message="Order not found in fraud service.", + vc=fraud_detection.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + card_digits = extract_card_digits(state["order"].card_number) + + success = True + message = "Card fraud check passed." + + if len(card_digits) != 16: + success = False + message = "Invalid card number." + elif card_digits.startswith("0000") or card_digits.endswith("0000"): + success = False + message = "Suspicious card number pattern." + + print( + f"[FD] order={request.order_id} event=CheckCardFraud " + f"vc={vc} success={success}" + ) + + return fraud_detection.EventResponse( + success=success, + message=message, + vc=fraud_detection.VectorClock(values=vc), + ) + + def ClearOrder(self, request, context): + order_id = request.order_id + final_vc = list(request.final_vc.values) + + with orders_lock: + state = orders.get(order_id) + + if state is None: + return fraud_detection.EventResponse( + success=False, + message="Order not found in fraud service.", + vc=fraud_detection.VectorClock(values=[0, 0, 0]), + ) + + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + + if can_clear: + del orders[order_id] + + success = can_clear + message = ( + "Order cleared from fraud service." + if success + else "Cannot clear order: local VC is ahead of final VC." + ) + + print( + f"[FD] order={order_id} event=ClearOrder " + f"local_vc={local_vc} final_vc={final_vc} success={success}" + ) + + return fraud_detection.EventResponse( + success=success, + message=message, + vc=fraud_detection.VectorClock(values=final_vc), + ) def serve(): - server = grpc.server(futures.ThreadPoolExecutor()) - fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + fraud_detection_grpc.add_FraudDetectionServiceServicer_to_server( + FraudDetectionService(), server + ) port = "50051" server.add_insecure_port("[::]:" + port) server.start() - print("Fraud detection server started. Listening on port 50051.") + print(f"Fraud detection server started. Listening on port {port}.") server.wait_for_termination() -def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) -if __name__ == '__main__': +if __name__ == "__main__": serve() \ No newline at end of file diff --git a/orchestrator/requirements.txt b/orchestrator/requirements.txt index 5ba8e254b..2016d74ab 100644 --- a/orchestrator/requirements.txt +++ b/orchestrator/requirements.txt @@ -1,12 +1,13 @@ blinker==1.7.0 click==8.1.7 Flask==3.0.0 -grpcio==1.60.0 -grpcio-tools==1.60.0 +grpcio==1.78.0 +grpcio-tools==1.78.0 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.3 -protobuf==4.25.2 + + Werkzeug==3.0.1 Flask-CORS==4.0.0 watchdog==6.0.0 \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 590815ff5..f1aaec6f5 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,102 +1,202 @@ -import sys import os +import sys import threading +import uuid + +import grpc +from flask import Flask, request +from flask_cors import CORS -# This set of lines are needed to import the gRPC stubs. -# The path of the stubs is relative to the current file, or absolute inside the container. -# Change these lines only if strictly needed. -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +# Import gRPC stubs +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") fraud_detection_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/fraud_detection') + os.path.join(FILE, "../../../utils/pb/fraud_detection") ) sys.path.insert(0, fraud_detection_grpc_path) import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc transaction_verification_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/transaction_verification') + os.path.join(FILE, "../../../utils/pb/transaction_verification") ) sys.path.insert(0, transaction_verification_grpc_path) import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc suggestions_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/suggestions') + os.path.join(FILE, "../../../utils/pb/suggestions") ) sys.path.insert(0, suggestions_grpc_path) import suggestions_pb2 as suggestions import suggestions_pb2_grpc as suggestions_grpc -import grpc -from flask import Flask, request -from flask_cors import CORS +app = Flask(__name__) +CORS(app, resources={r"/*": {"origins": "*"}}) -def greet(name='you'): - with grpc.insecure_channel('fraud_detection:50051') as channel: - stub = fraud_detection_grpc.HelloServiceStub(channel) - response = stub.SayHello(fraud_detection.HelloRequest(name=name)) - return response.greeting +def mask_fixed(card: str) -> str: + digits = "".join(c for c in str(card) if c.isdigit()) + masked = "*" * 12 + digits[-4:].rjust(4, "*") + return " ".join(masked[i:i + 4] for i in range(0, 16, 4)) -def detect_fraud(user_name, card_number, item_count): - with grpc.insecure_channel('fraud_detection:50051') as channel: - stub = fraud_detection_grpc.HelloServiceStub(channel) - response = stub.CheckFraud( - fraud_detection.FraudCheckRequest( - user_name=user_name or "", - card_number=card_number or "", - item_count=item_count - ), - timeout=5.0 # seconds +def merge_vcs(*vectors): + result = [0, 0, 0] + for vc in vectors: + for i in range(3): + result[i] = max(result[i], vc[i]) + return result + + +def build_order_kwargs(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): + return { + "user_name": user_name, + "user_contact": user_contact, + "card_number": card_number, + "expiration_date": expiration_date, + "cvv": cvv, + "item_count": item_count, + "terms_accepted": terms_accepted, + } + + +def init_fraud_service(order_id, order_kwargs): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + request = fraud_detection.InitOrderRequest( + order=fraud_detection.OrderData(order_id=order_id, **order_kwargs) ) - return response + return stub.InitOrder(request, timeout=5.0) + -def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): - with grpc.insecure_channel('transaction_verification:50052') as channel: +def init_transaction_service(order_id, order_kwargs): + with grpc.insecure_channel("transaction_verification:50052") as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) - response = stub.VerifyTransaction( - transaction_verification.TransactionVerificationRequest( - user_name=user_name or "", - user_contact=user_contact or "", - card_number=card_number or "", - expiration_date=expiration_date or "", - cvv=cvv or "", - item_count=item_count, - terms_accepted=terms_accepted - ) + request = transaction_verification.InitOrderRequest( + order=transaction_verification.OrderData(order_id=order_id, **order_kwargs) ) - return response + return stub.InitOrder(request, timeout=5.0) -def get_suggestions(user_name, item_count): - with grpc.insecure_channel('suggestions:50053') as channel: +def init_suggestions_service(order_id, order_kwargs): + with grpc.insecure_channel("suggestions:50053") as channel: stub = suggestions_grpc.SuggestionsServiceStub(channel) - response = stub.GetSuggestions( - suggestions.SuggestionsRequest( - user_name=user_name or "", - item_count=item_count - ) + request = suggestions.InitOrderRequest( + order=suggestions.OrderData(order_id=order_id, **order_kwargs) ) - return response + return stub.InitOrder(request, timeout=5.0) -app = Flask(__name__) -CORS(app, resources={r'/*': {'origins': '*'}}) +def tv_validate_items(order_id, vc): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + request = transaction_verification.EventRequest( + order_id=order_id, + vc=transaction_verification.VectorClock(values=vc), + ) + return stub.ValidateItems(request, timeout=5.0) -@app.route('/', methods=['GET']) +def tv_validate_user_data(order_id, vc): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + request = transaction_verification.EventRequest( + order_id=order_id, + vc=transaction_verification.VectorClock(values=vc), + ) + return stub.ValidateUserData(request, timeout=5.0) + + +def tv_validate_card_format(order_id, vc): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + request = transaction_verification.EventRequest( + order_id=order_id, + vc=transaction_verification.VectorClock(values=vc), + ) + return stub.ValidateCardFormat(request, timeout=5.0) + + +def fd_check_user_fraud(order_id, vc): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + request = fraud_detection.EventRequest( + order_id=order_id, + vc=fraud_detection.VectorClock(values=vc), + ) + return stub.CheckUserFraud(request, timeout=5.0) + + +def fd_check_card_fraud(order_id, vc): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + request = fraud_detection.EventRequest( + order_id=order_id, + vc=fraud_detection.VectorClock(values=vc), + ) + return stub.CheckCardFraud(request, timeout=5.0) + + +def sug_precompute(order_id, vc): + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + request = suggestions.EventRequest( + order_id=order_id, + vc=suggestions.VectorClock(values=vc), + ) + return stub.PrecomputeSuggestions(request, timeout=5.0) + + +def sug_finalize(order_id, vc): + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + request = suggestions.EventRequest( + order_id=order_id, + vc=suggestions.VectorClock(values=vc), + ) + return stub.FinalizeSuggestions(request, timeout=5.0) + + +def clear_fraud_service(order_id, final_vc): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + request = fraud_detection.ClearOrderRequest( + order_id=order_id, + final_vc=fraud_detection.VectorClock(values=final_vc), + ) + return stub.ClearOrder(request, timeout=5.0) + + +def clear_transaction_service(order_id, final_vc): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + request = transaction_verification.ClearOrderRequest( + order_id=order_id, + final_vc=transaction_verification.VectorClock(values=final_vc), + ) + return stub.ClearOrder(request, timeout=5.0) + + +def clear_suggestions_service(order_id, final_vc): + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + request = suggestions.ClearOrderRequest( + order_id=order_id, + final_vc=suggestions.VectorClock(values=final_vc), + ) + return stub.ClearOrder(request, timeout=5.0) + + +@app.route("/", methods=["GET"]) def index(): - response = greet(name='orchestrator') - return response + return {"message": "Orchestrator is running."}, 200 -@app.route('/checkout', methods=['POST']) +@app.route("/checkout", methods=["POST"]) def checkout(): request_data = request.get_json(silent=True) - if request_data is None: return { "error": { @@ -105,165 +205,251 @@ def checkout(): } }, 400 - user = request_data.get("user", {}) - items = request_data.get("items", []) - shipping_method = request_data.get("shippingMethod") - terms_accepted = request_data.get("termsAndConditionsAccepted", False) + user = request_data.get("user", {}) or {} + items = request_data.get("items", []) or [] + terms_accepted = bool(request_data.get("termsAndConditionsAccepted", False)) - # Normalize user fields and enforce that user_name is not empty or just spaces user_name = (user.get("name") or "").strip() user_contact = (user.get("contact") or "").strip() - user_comment = user.get("userComment", "") - - credit_card = user.get("creditCard", {}) - card_number = credit_card.get("number").strip() - masked_card_number = mask_fixed(card_number) - expiration_date = credit_card.get("expirationDate").strip() - cvv = credit_card.get("cvv").strip() - print( - "Received a request for checkout of user : {} for card number : {}".format( - user_name, masked_card_number - ) - ) + credit_card = (user.get("creditCard") or {}) + card_number = (credit_card.get("number") or "").strip() + expiration_date = (credit_card.get("expirationDate") or "").strip() + cvv = (credit_card.get("cvv") or "").strip() - # Keep these simple bad-request checks locally - # user_name must not be empty or contain spaces - if not user_name or "" == user_name: + if not user_name: return { "error": { "code": "BAD_REQUEST", - "message": "User name is required and must not contain spaces." + "message": "User name is required." } }, 400 - if not user_contact or "" == user_contact: + if not user_contact: return { "error": { "code": "BAD_REQUEST", - "message": "User contact is required and must not contain spaces." + "message": "User contact is required." } }, 400 item_count = len(items) + order_id = str(uuid.uuid4()) - results = { - "fraud": None, - "verification": None, - "suggestions": None, - "errors": [] + print( + f"[ORCH] order={order_id} received_checkout " + f"user={user_name} card={mask_fixed(card_number)} item_count={item_count}" + ) + + order_kwargs = build_order_kwargs( + user_name=user_name, + user_contact=user_contact, + card_number=card_number, + expiration_date=expiration_date, + cvv=cvv, + item_count=item_count, + terms_accepted=terms_accepted, + ) + + # Initialization phase + try: + init_tv = init_transaction_service(order_id, order_kwargs) + init_fd = init_fraud_service(order_id, order_kwargs) + init_sug = init_suggestions_service(order_id, order_kwargs) + except Exception as e: + print(f"[ORCH] order={order_id} initialization_error={e}") + return { + "error": { + "code": "INTERNAL_ERROR", + "message": "Failed to initialize backend services." + } + }, 500 + + for name, response in [ + ("InitTransactionVerification", init_tv), + ("InitFraudDetection", init_fd), + ("InitSuggestions", init_sug), + ]: + if not response.success: + print(f"[ORCH] order={order_id} step={name} success=False message={response.message}") + return { + "orderId": order_id, + "status": "Order Rejected", + "suggestedBooks": [], + "reason": response.message, + }, 200 + + print(f"[ORCH] order={order_id} initialization_complete") + + # Event-flow state + cancelled = threading.Event() + + done = { + "a": threading.Event(), # ValidateItems + "b": threading.Event(), # ValidateUserData + "c": threading.Event(), # ValidateCardFormat + "d": threading.Event(), # CheckUserFraud + "e": threading.Event(), # CheckCardFraud + "f": threading.Event(), # PrecomputeSuggestions + "g": threading.Event(), # FinalizeSuggestions } - def fraud_worker(): - try: - print("Calling fraud_detection service...") - results["fraud"] = detect_fraud( - user_name=user_name, - card_number=card_number, - item_count=item_count - ) - print( - "fraud_detection result:", - results["fraud"].is_fraud, - results["fraud"].message - ) - except Exception as e: - print(f"fraud_detection failed: {e}") # Full detail for server logs - results["errors"].append("fraud_detection service unavailable") # Sanitized for client - def verification_worker(): - try: - print("Calling transaction_verification service...") - results["verification"] = verify_transaction( - user_name=user_name, - user_contact=user_contact, - card_number=card_number, - expiration_date=expiration_date, - cvv=cvv, - item_count=item_count, - terms_accepted=terms_accepted - ) - print( - "transaction_verification result:", - results["verification"].is_valid, - results["verification"].message - ) - except Exception as e: - error_msg = f"transaction_verification failed: {e}" - print(error_msg) - results["errors"].append(error_msg) + state = { + "event_vcs": {}, + "final_vc": [0, 0, 0], + "books": [], + "failure_kind": None, # "event_failure" or "internal_error" + "failed_step": None, + "failure_message": None, + } + lock = threading.Lock() + + def store_event_result(step, response): + vc = list(response.vc.values) + with lock: + state["event_vcs"][step] = vc + state["final_vc"] = vc + + def merged_from(*steps): + with lock: + vcs = [state["event_vcs"][step] for step in steps if step in state["event_vcs"]] + if not vcs: + return [0, 0, 0] + return merge_vcs(*vcs) + + def record_event_failure(step, response): + with lock: + if state["failure_kind"] is None: + state["failure_kind"] = "event_failure" + state["failed_step"] = step + state["failure_message"] = response.message + state["final_vc"] = list(response.vc.values) + cancelled.set() + print(f"[ORCH] order={order_id} step={step} success=False message={response.message}") + + def record_internal_failure(step, message, fallback_vc): + with lock: + if state["failure_kind"] is None: + state["failure_kind"] = "internal_error" + state["failed_step"] = step + state["failure_message"] = message + state["final_vc"] = fallback_vc + cancelled.set() + print(f"[ORCH] order={order_id} step={step} internal_error={message}") + + def run_event(step, prereqs, input_steps, rpc_func): + for prereq in prereqs: + done[prereq].wait() + + if cancelled.is_set(): + done[step].set() + return + + request_vc = merged_from(*input_steps) - def suggestions_worker(): try: - print("Calling suggestions service...") - results["suggestions"] = get_suggestions( - user_name=user_name, - item_count=item_count - ) - print( - "suggestions returned:", - len(results["suggestions"].books), - "books" - ) + response = rpc_func(order_id, request_vc) + store_event_result(step, response) + + if not response.success: + record_event_failure(step, response) except Exception as e: - error_msg = f"suggestions failed: {e}" - print(error_msg) - results["errors"].append(error_msg) + record_internal_failure(step, str(e), request_vc) + finally: + done[step].set() - fraud_thread = threading.Thread(target=fraud_worker) - verification_thread = threading.Thread(target=verification_worker) - suggestions_thread = threading.Thread(target=suggestions_worker) + def run_finalize(): + for prereq in ["e", "f"]: + done[prereq].wait() - print("Starting worker threads...") - fraud_thread.start() - verification_thread.start() - suggestions_thread.start() + if cancelled.is_set(): + done["g"].set() + return - fraud_thread.join() - verification_thread.join() - suggestions_thread.join() - print("All worker threads finished.") + request_vc = merged_from("e", "f") - if results["errors"]: + try: + response = sug_finalize(order_id, request_vc) + store_event_result("g", response) + + if response.success: + books = [] + for book in response.books: + books.append({ + "bookId": book.bookId, + "title": book.title, + "author": book.author, + }) + with lock: + state["books"] = books + else: + record_event_failure("g", response) + except Exception as e: + record_internal_failure("g", str(e), request_vc) + finally: + done["g"].set() + + # Example partial order: + # a || b + # c after a + # d after b + # e after c and d + # f after a + # g after e and f + workers = [ + threading.Thread(target=run_event, args=("a", [], [], tv_validate_items)), + threading.Thread(target=run_event, args=("b", [], [], tv_validate_user_data)), + threading.Thread(target=run_event, args=("c", ["a"], ["a"], tv_validate_card_format)), + threading.Thread(target=run_event, args=("d", ["b"], ["b"], fd_check_user_fraud)), + threading.Thread(target=run_event, args=("e", ["c", "d"], ["c", "d"], fd_check_card_fraud)), + threading.Thread(target=run_event, args=("f", ["a"], ["a"], sug_precompute)), + threading.Thread(target=run_finalize), + ] + + print(f"[ORCH] order={order_id} starting_event_flow") + for worker in workers: + worker.start() + + for worker in workers: + worker.join() + + print(f"[ORCH] order={order_id} all_worker_threads_finished") + + final_vc = state["final_vc"] + + # Bonus cleanup / useful for repeat testing + try: + clear_transaction_service(order_id, final_vc) + clear_fraud_service(order_id, final_vc) + clear_suggestions_service(order_id, final_vc) + print(f"[ORCH] order={order_id} clear_broadcast_sent final_vc={final_vc}") + except Exception as e: + print(f"[ORCH] order={order_id} clear_broadcast_warning={e}") + + if state["failure_kind"] == "internal_error": return { "error": { "code": "INTERNAL_ERROR", - "message": "; ".join(results["errors"]) + "message": state["failure_message"], } }, 500 - if results["fraud"] and results["fraud"].is_fraud: + if state["failure_kind"] == "event_failure": return { - "orderId": "12345", + "orderId": order_id, "status": "Order Rejected", - "suggestedBooks": [] + "suggestedBooks": [], + "reason": state["failure_message"], }, 200 - if results["verification"] and not results["verification"].is_valid: - return { - "orderId": "12345", - "status": "Order Rejected", - "suggestedBooks": [] - }, 200 - - suggested_books = [] - if results["suggestions"]: - for book in results["suggestions"].books: - suggested_books.append({ - "bookId": book.bookId, - "title": book.title, - "author": book.author - }) + print(f"[ORCH] order={order_id} final_status=APPROVED final_vc={final_vc}") return { - "orderId": "12345", + "orderId": order_id, "status": "Order Approved", - "suggestedBooks": suggested_books + "suggestedBooks": state["books"], }, 200 -def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) -if __name__ == '__main__': - app.run(host='0.0.0.0') \ No newline at end of file +if __name__ == "__main__": + app.run(host="0.0.0.0") \ No newline at end of file diff --git a/suggestions/requirements.txt b/suggestions/requirements.txt index 43fdaf26f..718548bc2 100644 --- a/suggestions/requirements.txt +++ b/suggestions/requirements.txt @@ -1,4 +1,5 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 + + watchdog==6.0.0 \ No newline at end of file diff --git a/suggestions/src/app.py b/suggestions/src/app.py index 258c2fb0c..ca95bf0c3 100644 --- a/suggestions/src/app.py +++ b/suggestions/src/app.py @@ -1,10 +1,11 @@ -import sys import os +import sys +import threading from concurrent import futures -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") suggestions_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/suggestions') + os.path.join(FILE, "../../../utils/pb/suggestions") ) sys.path.insert(0, suggestions_grpc_path) @@ -13,38 +14,189 @@ import suggestions_pb2_grpc as suggestions_grpc +SERVICE_INDEX = 2 # [transaction_verification, fraud_detection, suggestions] + +orders = {} +orders_lock = threading.Lock() + +STATIC_BOOKS = [ + { + "bookId": "101", + "title": "Distributed Systems Basics", + "author": "A. Author", + }, + { + "bookId": "102", + "title": "Designing Data-Intensive Applications", + "author": "Martin Kleppmann", + }, + { + "bookId": "103", + "title": "Clean Code", + "author": "Robert C. Martin", + }, + { + "bookId": "104", + "title": "The Pragmatic Programmer", + "author": "Andrew Hunt", + }, +] + + +def merge_vc(local_vc, incoming_vc): + return [max(a, b) for a, b in zip(local_vc, incoming_vc)] + + +def tick(vc, idx): + vc[idx] += 1 + return vc + + +def get_order_state(order_id: str): + with orders_lock: + return orders.get(order_id) + + class SuggestionsService(suggestions_grpc.SuggestionsServiceServicer): - def GetSuggestions(self, request, context): - print("Received suggestions request") - print("user_name:", request.user_name) - print("item_count:", request.item_count) - - static_books = [ - {"bookId": "101", "title": "Distributed Systems Basics", "author": "A. Author"}, - {"bookId": "102", "title": "Designing Data-Intensive Applications", "author": "Martin Kleppmann"}, - {"bookId": "103", "title": "Clean Code", "author": "Robert C. Martin"}, - {"bookId": "104", "title": "The Pragmatic Programmer", "author": "Andrew Hunt"}, - ] - - response = suggestions.SuggestionsResponse() - - if request.item_count > 0: - chosen = static_books[:2] + def InitOrder(self, request, context): + order = request.order + + with orders_lock: + orders[order.order_id] = { + "order": order, + "vc": [0, 0, 0], + "books": [], + } + + print(f"[SUG] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") + + return suggestions.EventResponse( + success=True, + message="Suggestions service initialized order.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + ) + + def PrecomputeSuggestions(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return suggestions.EventResponse( + success=False, + message="Order not found in suggestions service.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + item_count = state["order"].item_count + + if item_count > 0: + state["books"] = STATIC_BOOKS[:2] + success = True + message = "Suggestions prepared." else: - chosen = [] + state["books"] = [] + success = False + message = "Cannot prepare suggestions for empty order." + + print( + f"[SUG] order={request.order_id} event=PrecomputeSuggestions " + f"vc={vc} success={success} prepared_books={len(state['books'])}" + ) + + return suggestions.EventResponse( + success=success, + message=message, + vc=suggestions.VectorClock(values=vc), + ) + + def FinalizeSuggestions(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return suggestions.SuggestionsEventResponse( + success=False, + message="Order not found in suggestions service.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + books=[], + ) - for book in chosen: + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + prepared_books = state["books"] + success = len(prepared_books) > 0 + message = ( + "Suggestions finalized." + if success + else "No prepared suggestions available." + ) + + response = suggestions.SuggestionsEventResponse( + success=success, + message=message, + vc=suggestions.VectorClock(values=vc), + ) + + for book in prepared_books: b = response.books.add() b.bookId = book["bookId"] b.title = book["title"] b.author = book["author"] - print("Returning", len(response.books), "suggested books") + print( + f"[SUG] order={request.order_id} event=FinalizeSuggestions " + f"vc={vc} success={success} returned_books={len(prepared_books)}" + ) + return response + def ClearOrder(self, request, context): + order_id = request.order_id + final_vc = list(request.final_vc.values) + + with orders_lock: + state = orders.get(order_id) + + if state is None: + return suggestions.EventResponse( + success=False, + message="Order not found in suggestions service.", + vc=suggestions.VectorClock(values=[0, 0, 0]), + ) + + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + + if can_clear: + del orders[order_id] + + success = can_clear + message = ( + "Order cleared from suggestions service." + if success + else "Cannot clear order: local VC is ahead of final VC." + ) + + print( + f"[SUG] order={order_id} event=ClearOrder " + f"local_vc={local_vc} final_vc={final_vc} success={success}" + ) + + return suggestions.EventResponse( + success=success, + message=message, + vc=suggestions.VectorClock(values=final_vc), + ) + def serve(): - server = grpc.server(futures.ThreadPoolExecutor()) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) suggestions_grpc.add_SuggestionsServiceServicer_to_server( SuggestionsService(), server ) @@ -52,9 +204,9 @@ def serve(): port = "50053" server.add_insecure_port("[::]:" + port) server.start() - print("Suggestions server started. Listening on port 50053.") + print(f"Suggestions server started. Listening on port {port}.") server.wait_for_termination() -if __name__ == '__main__': +if __name__ == "__main__": serve() \ No newline at end of file diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt index 43fdaf26f..718548bc2 100644 --- a/transaction_verification/requirements.txt +++ b/transaction_verification/requirements.txt @@ -1,4 +1,5 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 + + watchdog==6.0.0 \ No newline at end of file diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index b1087e51f..7e5264c4a 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -1,10 +1,11 @@ -import sys import os +import sys +import threading from concurrent import futures -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") transaction_verification_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/transaction_verification') + os.path.join(FILE, "../../../utils/pb/transaction_verification") ) sys.path.insert(0, transaction_verification_grpc_path) @@ -13,61 +14,206 @@ import transaction_verification_pb2_grpc as transaction_verification_grpc +SERVICE_INDEX = 0 # [transaction_verification, fraud_detection, suggestions] + +orders = {} +orders_lock = threading.Lock() + + +def merge_vc(local_vc, incoming_vc): + return [max(a, b) for a, b in zip(local_vc, incoming_vc)] + + +def tick(vc, idx): + vc[idx] += 1 + return vc + + +def extract_card_digits(card: str) -> str: + return "".join(c for c in str(card) if c.isdigit()) + + +def mask_fixed(card: str) -> str: + digits = extract_card_digits(card) + masked = "*" * 12 + digits[-4:].rjust(4, "*") + return " ".join(masked[i:i + 4] for i in range(0, 16, 4)) + + +def get_order_state(order_id: str): + with orders_lock: + return orders.get(order_id) + + class TransactionVerificationService( transaction_verification_grpc.TransactionVerificationServiceServicer ): - def VerifyTransaction(self, request, context): - print("Received transaction verification request") - print("user_name:", request.user_name) - print("user_contact:", request.user_contact) - masked_card_number = mask_fixed(request.card_number) - print("card_number:", masked_card_number) - print("item_count:", request.item_count) - print("terms_accepted:", request.terms_accepted) - # Compute length based on digits only to avoid counting spaces or other characters - card_digits = extract_card_digits(request.card_number) - print("card length (digits only):", len(card_digits)) - is_valid = True - message = "Transaction is valid." - - if not request.user_name: - is_valid = False + def InitOrder(self, request, context): + order = request.order + + with orders_lock: + orders[order.order_id] = { + "order": order, + "vc": [0, 0, 0], + } + + print(f"[TV] order={order.order_id} event=InitOrder vc={[0, 0, 0]} success=True") + + return transaction_verification.EventResponse( + success=True, + message="Transaction verification service initialized order.", + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + def ValidateItems(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return transaction_verification.EventResponse( + success=False, + message="Order not found in transaction verification service.", + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + item_count = state["order"].item_count + success = item_count > 0 + message = "Items check passed." if success else "No items in order." + + print( + f"[TV] order={request.order_id} event=ValidateItems " + f"vc={vc} success={success} item_count={item_count}" + ) + + return transaction_verification.EventResponse( + success=success, + message=message, + vc=transaction_verification.VectorClock(values=vc), + ) + + def ValidateUserData(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return transaction_verification.EventResponse( + success=False, + message="Order not found in transaction verification service.", + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + order = state["order"] + success = True + message = "User data check passed." + + if not order.user_name: + success = False message = "Missing user name." - elif not request.user_contact: - is_valid = False + elif not order.user_contact: + success = False message = "Missing user contact." - elif request.item_count <= 0: - is_valid = False - message = "No items in order." - elif not request.terms_accepted: - is_valid = False + elif not order.terms_accepted: + success = False message = "Terms and conditions not accepted." - elif not request.card_number or not request.expiration_date or not request.cvv: - is_valid = False - message = "Missing credit card information." - # Treat any non-16-digit card number as invalid + print( + f"[TV] order={request.order_id} event=ValidateUserData " + f"vc={vc} success={success}" + ) + + return transaction_verification.EventResponse( + success=success, + message=message, + vc=transaction_verification.VectorClock(values=vc), + ) + + def ValidateCardFormat(self, request, context): + state = get_order_state(request.order_id) + if state is None: + return transaction_verification.EventResponse( + success=False, + message="Order not found in transaction verification service.", + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + incoming_vc = list(request.vc.values) + local_vc = state["vc"] + vc = merge_vc(local_vc, incoming_vc) + vc = tick(vc, SERVICE_INDEX) + state["vc"] = vc + + order = state["order"] + card_digits = extract_card_digits(order.card_number) + + success = True + message = "Card format check passed." + + if not order.card_number or not order.expiration_date or not order.cvv: + success = False + message = "Missing credit card information." elif len(card_digits) != 16: - is_valid = False + success = False message = "Invalid card number." - response = transaction_verification.TransactionVerificationResponse() - response.is_valid = is_valid - response.message = message + print( + f"[TV] order={request.order_id} event=ValidateCardFormat " + f"vc={vc} success={success} masked_card={mask_fixed(order.card_number)}" + ) - print("Returning verification result:", response.is_valid, response.message) - return response + return transaction_verification.EventResponse( + success=success, + message=message, + vc=transaction_verification.VectorClock(values=vc), + ) + def ClearOrder(self, request, context): + order_id = request.order_id + final_vc = list(request.final_vc.values) -def extract_card_digits(card: str) -> str: - """ - Return only the digit characters from the given card number. - """ - return ''.join(c for c in str(card) if c.isdigit()) + with orders_lock: + state = orders.get(order_id) + + if state is None: + return transaction_verification.EventResponse( + success=False, + message="Order not found in transaction verification service.", + vc=transaction_verification.VectorClock(values=[0, 0, 0]), + ) + + local_vc = state["vc"] + can_clear = all(a <= b for a, b in zip(local_vc, final_vc)) + + if can_clear: + del orders[order_id] + + success = can_clear + message = ( + "Order cleared from transaction verification service." + if success + else "Cannot clear order: local VC is ahead of final VC." + ) + + print( + f"[TV] order={order_id} event=ClearOrder " + f"local_vc={local_vc} final_vc={final_vc} success={success}" + ) + + return transaction_verification.EventResponse( + success=success, + message=message, + vc=transaction_verification.VectorClock(values=final_vc), + ) def serve(): - server = grpc.server(futures.ThreadPoolExecutor()) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) transaction_verification_grpc.add_TransactionVerificationServiceServicer_to_server( TransactionVerificationService(), server ) @@ -75,13 +221,9 @@ def serve(): port = "50052" server.add_insecure_port("[::]:" + port) server.start() - print("Transaction verification server started. Listening on port 50052.") + print(f"Transaction verification server started. Listening on port {port}.") server.wait_for_termination() -def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) -if __name__ == '__main__': +if __name__ == "__main__": serve() \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index d575366b1..fe4d514a0 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -1,27 +1,52 @@ syntax = "proto3"; -package hello; +package fraud_detection; -service HelloService { - rpc SayHello (HelloRequest) returns (HelloResponse); - rpc CheckFraud (FraudCheckRequest) returns (FraudCheckResponse); +service FraudDetectionService { + // Required for Seminar 5: + // Orchestrator sends the order once, service stores it locally and + // initializes its per-order vector clock. + rpc InitOrder (InitOrderRequest) returns (EventResponse); + + // Example Seminar 5 fraud-related events: + rpc CheckUserFraud (EventRequest) returns (EventResponse); + rpc CheckCardFraud (EventRequest) returns (EventResponse); + + // Optional bonus event from Seminar 5: + rpc ClearOrder (ClearOrderRequest) returns (EventResponse); +} + +message VectorClock { + repeated int32 values = 1; } -message HelloRequest { - string name = 1; +message OrderData { + string order_id = 1; + string user_name = 2; + string user_contact = 3; + string card_number = 4; + string expiration_date = 5; + string cvv = 6; + int32 item_count = 7; + bool terms_accepted = 8; } -message HelloResponse { - string greeting = 1; +message InitOrderRequest { + OrderData order = 1; } -message FraudCheckRequest { - string user_name = 1; - string card_number = 2; - int32 item_count = 3; +message EventRequest { + string order_id = 1; + VectorClock vc = 2; } -message FraudCheckResponse { - bool is_fraud = 1; +message EventResponse { + bool success = 1; string message = 2; + VectorClock vc = 3; +} + +message ClearOrderRequest { + string order_id = 1; + VectorClock final_vc = 2; } \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index d405d9cf5..6e0970015 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: fraud_detection.proto -# Protobuf Python Version: 4.25.0 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'fraud_detection.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,21 +24,25 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t\"O\n\x11\x46raudCheckRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x02 \x01(\t\x12\x12\n\nitem_count\x18\x03 \x01(\x05\"7\n\x12\x46raudCheckResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\x88\x01\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponse\x12\x41\n\nCheckFraud\x12\x18.hello.FraudCheckRequest\x1a\x19.hello.FraudCheckResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x0f\x66raud_detection\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"=\n\x10InitOrderRequest\x12)\n\x05order\x18\x01 \x01(\x0b\x32\x1a.fraud_detection.OrderData\"J\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12(\n\x02vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"[\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12(\n\x02vc\x18\x03 \x01(\x0b\x32\x1c.fraud_detection.VectorClock\"U\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12.\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x1c.fraud_detection.VectorClock2\xdb\x02\n\x15\x46raudDetectionService\x12N\n\tInitOrder\x12!.fraud_detection.InitOrderRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckUserFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12O\n\x0e\x43heckCardFraud\x12\x1d.fraud_detection.EventRequest\x1a\x1e.fraud_detection.EventResponse\x12P\n\nClearOrder\x12\".fraud_detection.ClearOrderRequest\x1a\x1e.fraud_detection.EventResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'fraud_detection_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_HELLOREQUEST']._serialized_start=32 - _globals['_HELLOREQUEST']._serialized_end=60 - _globals['_HELLORESPONSE']._serialized_start=62 - _globals['_HELLORESPONSE']._serialized_end=95 - _globals['_FRAUDCHECKREQUEST']._serialized_start=97 - _globals['_FRAUDCHECKREQUEST']._serialized_end=176 - _globals['_FRAUDCHECKRESPONSE']._serialized_start=178 - _globals['_FRAUDCHECKRESPONSE']._serialized_end=233 - _globals['_HELLOSERVICE']._serialized_start=236 - _globals['_HELLOSERVICE']._serialized_end=372 +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_VECTORCLOCK']._serialized_start=42 + _globals['_VECTORCLOCK']._serialized_end=71 + _globals['_ORDERDATA']._serialized_start=74 + _globals['_ORDERDATA']._serialized_end=247 + _globals['_INITORDERREQUEST']._serialized_start=249 + _globals['_INITORDERREQUEST']._serialized_end=310 + _globals['_EVENTREQUEST']._serialized_start=312 + _globals['_EVENTREQUEST']._serialized_end=386 + _globals['_EVENTRESPONSE']._serialized_start=388 + _globals['_EVENTRESPONSE']._serialized_end=479 + _globals['_CLEARORDERREQUEST']._serialized_start=481 + _globals['_CLEARORDERREQUEST']._serialized_end=566 + _globals['_FRAUDDETECTIONSERVICE']._serialized_start=569 + _globals['_FRAUDDETECTIONSERVICE']._serialized_end=916 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index fe3863b06..fe8cb8ab1 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -1,35 +1,64 @@ +from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Optional as _Optional +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor -class HelloRequest(_message.Message): - __slots__ = ("name",) - NAME_FIELD_NUMBER: _ClassVar[int] - name: str - def __init__(self, name: _Optional[str] = ...) -> None: ... +class VectorClock(_message.Message): + __slots__ = ("values",) + VALUES_FIELD_NUMBER: _ClassVar[int] + values: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, values: _Optional[_Iterable[int]] = ...) -> None: ... -class HelloResponse(_message.Message): - __slots__ = ("greeting",) - GREETING_FIELD_NUMBER: _ClassVar[int] - greeting: str - def __init__(self, greeting: _Optional[str] = ...) -> None: ... - -class FraudCheckRequest(_message.Message): - __slots__ = ("user_name", "card_number", "item_count") +class OrderData(_message.Message): + __slots__ = ("order_id", "user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] USER_NAME_FIELD_NUMBER: _ClassVar[int] + USER_CONTACT_FIELD_NUMBER: _ClassVar[int] CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_DATE_FIELD_NUMBER: _ClassVar[int] + CVV_FIELD_NUMBER: _ClassVar[int] ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] + order_id: str user_name: str + user_contact: str card_number: str + expiration_date: str + cvv: str item_count: int - def __init__(self, user_name: _Optional[str] = ..., card_number: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... + terms_accepted: bool + def __init__(self, order_id: _Optional[str] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + +class InitOrderRequest(_message.Message): + __slots__ = ("order",) + ORDER_FIELD_NUMBER: _ClassVar[int] + order: OrderData + def __init__(self, order: _Optional[_Union[OrderData, _Mapping]] = ...) -> None: ... -class FraudCheckResponse(_message.Message): - __slots__ = ("is_fraud", "message") - IS_FRAUD_FIELD_NUMBER: _ClassVar[int] +class EventRequest(_message.Message): + __slots__ = ("order_id", "vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... + +class EventResponse(_message.Message): + __slots__ = ("success", "message", "vc") + SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] - is_fraud: bool + VC_FIELD_NUMBER: _ClassVar[int] + success: bool message: str - def __init__(self, is_fraud: bool = ..., message: _Optional[str] = ...) -> None: ... + vc: VectorClock + def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... + +class ClearOrderRequest(_message.Message): + __slots__ = ("order_id", "final_vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + final_vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., final_vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 3ada2e86e..85179fcb1 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -1,11 +1,31 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings import fraud_detection_pb2 as fraud__detection__pb2 +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False -class HelloServiceStub(object): +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in fraud_detection_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class FraudDetectionServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,58 +34,96 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.SayHello = channel.unary_unary( - '/hello.HelloService/SayHello', - request_serializer=fraud__detection__pb2.HelloRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.HelloResponse.FromString, - ) - self.CheckFraud = channel.unary_unary( - '/hello.HelloService/CheckFraud', - request_serializer=fraud__detection__pb2.FraudCheckRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.FraudCheckResponse.FromString, - ) - - -class HelloServiceServicer(object): + self.InitOrder = channel.unary_unary( + '/fraud_detection.FraudDetectionService/InitOrder', + request_serializer=fraud__detection__pb2.InitOrderRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.EventResponse.FromString, + _registered_method=True) + self.CheckUserFraud = channel.unary_unary( + '/fraud_detection.FraudDetectionService/CheckUserFraud', + request_serializer=fraud__detection__pb2.EventRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.EventResponse.FromString, + _registered_method=True) + self.CheckCardFraud = channel.unary_unary( + '/fraud_detection.FraudDetectionService/CheckCardFraud', + request_serializer=fraud__detection__pb2.EventRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.EventResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/fraud_detection.FraudDetectionService/ClearOrder', + request_serializer=fraud__detection__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=fraud__detection__pb2.EventResponse.FromString, + _registered_method=True) + + +class FraudDetectionServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def SayHello(self, request, context): - """Missing associated documentation comment in .proto file.""" + def InitOrder(self, request, context): + """Required for Seminar 5: + Orchestrator sends the order once, service stores it locally and + initializes its per-order vector clock. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CheckUserFraud(self, request, context): + """Example Seminar 5 fraud-related events: + """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def CheckFraud(self, request, context): + def CheckCardFraud(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ClearOrder(self, request, context): + """Optional bonus event from Seminar 5: + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + -def add_HelloServiceServicer_to_server(servicer, server): +def add_FraudDetectionServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'SayHello': grpc.unary_unary_rpc_method_handler( - servicer.SayHello, - request_deserializer=fraud__detection__pb2.HelloRequest.FromString, - response_serializer=fraud__detection__pb2.HelloResponse.SerializeToString, + 'InitOrder': grpc.unary_unary_rpc_method_handler( + servicer.InitOrder, + request_deserializer=fraud__detection__pb2.InitOrderRequest.FromString, + response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, + ), + 'CheckUserFraud': grpc.unary_unary_rpc_method_handler( + servicer.CheckUserFraud, + request_deserializer=fraud__detection__pb2.EventRequest.FromString, + response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, ), - 'CheckFraud': grpc.unary_unary_rpc_method_handler( - servicer.CheckFraud, - request_deserializer=fraud__detection__pb2.FraudCheckRequest.FromString, - response_serializer=fraud__detection__pb2.FraudCheckResponse.SerializeToString, + 'CheckCardFraud': grpc.unary_unary_rpc_method_handler( + servicer.CheckCardFraud, + request_deserializer=fraud__detection__pb2.EventRequest.FromString, + response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=fraud__detection__pb2.ClearOrderRequest.FromString, + response_serializer=fraud__detection__pb2.EventResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'hello.HelloService', rpc_method_handlers) + 'fraud_detection.FraudDetectionService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('fraud_detection.FraudDetectionService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. -class HelloService(object): +class FraudDetectionService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def SayHello(request, + def InitOrder(request, target, options=(), channel_credentials=None, @@ -75,14 +133,24 @@ def SayHello(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/hello.HelloService/SayHello', - fraud__detection__pb2.HelloRequest.SerializeToString, - fraud__detection__pb2.HelloResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/InitOrder', + fraud__detection__pb2.InitOrderRequest.SerializeToString, + fraud__detection__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod - def CheckFraud(request, + def CheckUserFraud(request, target, options=(), channel_credentials=None, @@ -92,8 +160,72 @@ def CheckFraud(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/hello.HelloService/CheckFraud', - fraud__detection__pb2.FraudCheckRequest.SerializeToString, - fraud__detection__pb2.FraudCheckResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/CheckUserFraud', + fraud__detection__pb2.EventRequest.SerializeToString, + fraud__detection__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CheckCardFraud(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/CheckCardFraud', + fraud__detection__pb2.EventRequest.SerializeToString, + fraud__detection__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/ClearOrder', + fraud__detection__pb2.ClearOrderRequest.SerializeToString, + fraud__detection__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto index 12f15f4cb..60820ebda 100644 --- a/utils/pb/suggestions/suggestions.proto +++ b/utils/pb/suggestions/suggestions.proto @@ -3,12 +3,42 @@ syntax = "proto3"; package suggestions; service SuggestionsService { - rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); + rpc InitOrder (InitOrderRequest) returns (EventResponse); + + rpc PrecomputeSuggestions (EventRequest) returns (EventResponse); + rpc FinalizeSuggestions (EventRequest) returns (SuggestionsEventResponse); + + rpc ClearOrder (ClearOrderRequest) returns (EventResponse); +} + +message VectorClock { + repeated int32 values = 1; +} + +message OrderData { + string order_id = 1; + string user_name = 2; + string user_contact = 3; + string card_number = 4; + string expiration_date = 5; + string cvv = 6; + int32 item_count = 7; + bool terms_accepted = 8; } -message SuggestionsRequest { - string user_name = 1; - int32 item_count = 2; +message InitOrderRequest { + OrderData order = 1; +} + +message EventRequest { + string order_id = 1; + VectorClock vc = 2; +} + +message EventResponse { + bool success = 1; + string message = 2; + VectorClock vc = 3; } message SuggestedBook { @@ -17,6 +47,14 @@ message SuggestedBook { string author = 3; } -message SuggestionsResponse { - repeated SuggestedBook books = 1; +message SuggestionsEventResponse { + bool success = 1; + string message = 2; + VectorClock vc = 3; + repeated SuggestedBook books = 4; +} + +message ClearOrderRequest { + string order_id = 1; + VectorClock final_vc = 2; } \ No newline at end of file diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py index 550a4d48e..cab75ecee 100644 --- a/utils/pb/suggestions/suggestions_pb2.py +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: suggestions.proto -# Protobuf Python Version: 4.25.0 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'suggestions.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,19 +24,29 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\";\n\x12SuggestionsRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x12\n\nitem_count\x18\x02 \x01(\x05\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"@\n\x13SuggestionsResponse\x12)\n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x1a.suggestions.SuggestedBook2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"9\n\x10InitOrderRequest\x12%\n\x05order\x18\x01 \x01(\x0b\x32\x16.suggestions.OrderData\"F\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12$\n\x02vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock\"W\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"\x8d\x01\n\x18SuggestionsEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12$\n\x02vc\x18\x03 \x01(\x0b\x32\x18.suggestions.VectorClock\x12)\n\x05\x62ooks\x18\x04 \x03(\x0b\x32\x1a.suggestions.SuggestedBook\"Q\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12*\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32\x18.suggestions.VectorClock2\xcf\x02\n\x12SuggestionsService\x12\x46\n\tInitOrder\x12\x1d.suggestions.InitOrderRequest\x1a\x1a.suggestions.EventResponse\x12N\n\x15PrecomputeSuggestions\x12\x19.suggestions.EventRequest\x1a\x1a.suggestions.EventResponse\x12W\n\x13\x46inalizeSuggestions\x12\x19.suggestions.EventRequest\x1a%.suggestions.SuggestionsEventResponse\x12H\n\nClearOrder\x12\x1e.suggestions.ClearOrderRequest\x1a\x1a.suggestions.EventResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'suggestions_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_SUGGESTIONSREQUEST']._serialized_start=34 - _globals['_SUGGESTIONSREQUEST']._serialized_end=93 - _globals['_SUGGESTEDBOOK']._serialized_start=95 - _globals['_SUGGESTEDBOOK']._serialized_end=157 - _globals['_SUGGESTIONSRESPONSE']._serialized_start=159 - _globals['_SUGGESTIONSRESPONSE']._serialized_end=223 - _globals['_SUGGESTIONSSERVICE']._serialized_start=225 - _globals['_SUGGESTIONSSERVICE']._serialized_end=330 +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_VECTORCLOCK']._serialized_start=34 + _globals['_VECTORCLOCK']._serialized_end=63 + _globals['_ORDERDATA']._serialized_start=66 + _globals['_ORDERDATA']._serialized_end=239 + _globals['_INITORDERREQUEST']._serialized_start=241 + _globals['_INITORDERREQUEST']._serialized_end=298 + _globals['_EVENTREQUEST']._serialized_start=300 + _globals['_EVENTREQUEST']._serialized_end=370 + _globals['_EVENTRESPONSE']._serialized_start=372 + _globals['_EVENTRESPONSE']._serialized_end=459 + _globals['_SUGGESTEDBOOK']._serialized_start=461 + _globals['_SUGGESTEDBOOK']._serialized_end=523 + _globals['_SUGGESTIONSEVENTRESPONSE']._serialized_start=526 + _globals['_SUGGESTIONSEVENTRESPONSE']._serialized_end=667 + _globals['_CLEARORDERREQUEST']._serialized_start=669 + _globals['_CLEARORDERREQUEST']._serialized_end=750 + _globals['_SUGGESTIONSSERVICE']._serialized_start=753 + _globals['_SUGGESTIONSSERVICE']._serialized_end=1088 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/suggestions/suggestions_pb2.pyi b/utils/pb/suggestions/suggestions_pb2.pyi index d3d31c343..3e893c77f 100644 --- a/utils/pb/suggestions/suggestions_pb2.pyi +++ b/utils/pb/suggestions/suggestions_pb2.pyi @@ -5,13 +5,55 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor -class SuggestionsRequest(_message.Message): - __slots__ = ("user_name", "item_count") +class VectorClock(_message.Message): + __slots__ = ("values",) + VALUES_FIELD_NUMBER: _ClassVar[int] + values: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, values: _Optional[_Iterable[int]] = ...) -> None: ... + +class OrderData(_message.Message): + __slots__ = ("order_id", "user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] USER_NAME_FIELD_NUMBER: _ClassVar[int] + USER_CONTACT_FIELD_NUMBER: _ClassVar[int] + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_DATE_FIELD_NUMBER: _ClassVar[int] + CVV_FIELD_NUMBER: _ClassVar[int] ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] + order_id: str user_name: str + user_contact: str + card_number: str + expiration_date: str + cvv: str item_count: int - def __init__(self, user_name: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... + terms_accepted: bool + def __init__(self, order_id: _Optional[str] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + +class InitOrderRequest(_message.Message): + __slots__ = ("order",) + ORDER_FIELD_NUMBER: _ClassVar[int] + order: OrderData + def __init__(self, order: _Optional[_Union[OrderData, _Mapping]] = ...) -> None: ... + +class EventRequest(_message.Message): + __slots__ = ("order_id", "vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... + +class EventResponse(_message.Message): + __slots__ = ("success", "message", "vc") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + vc: VectorClock + def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... class SuggestedBook(_message.Message): __slots__ = ("bookId", "title", "author") @@ -23,8 +65,22 @@ class SuggestedBook(_message.Message): author: str def __init__(self, bookId: _Optional[str] = ..., title: _Optional[str] = ..., author: _Optional[str] = ...) -> None: ... -class SuggestionsResponse(_message.Message): - __slots__ = ("books",) +class SuggestionsEventResponse(_message.Message): + __slots__ = ("success", "message", "vc", "books") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] BOOKS_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + vc: VectorClock books: _containers.RepeatedCompositeFieldContainer[SuggestedBook] - def __init__(self, books: _Optional[_Iterable[_Union[SuggestedBook, _Mapping]]] = ...) -> None: ... + def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ..., books: _Optional[_Iterable[_Union[SuggestedBook, _Mapping]]] = ...) -> None: ... + +class ClearOrderRequest(_message.Message): + __slots__ = ("order_id", "final_vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + final_vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., final_vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... diff --git a/utils/pb/suggestions/suggestions_pb2_grpc.py b/utils/pb/suggestions/suggestions_pb2_grpc.py index ca1154a0b..7199eb0ff 100644 --- a/utils/pb/suggestions/suggestions_pb2_grpc.py +++ b/utils/pb/suggestions/suggestions_pb2_grpc.py @@ -1,9 +1,29 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings import suggestions_pb2 as suggestions__pb2 +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in suggestions_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class SuggestionsServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -14,17 +34,50 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.GetSuggestions = channel.unary_unary( - '/suggestions.SuggestionsService/GetSuggestions', - request_serializer=suggestions__pb2.SuggestionsRequest.SerializeToString, - response_deserializer=suggestions__pb2.SuggestionsResponse.FromString, - ) + self.InitOrder = channel.unary_unary( + '/suggestions.SuggestionsService/InitOrder', + request_serializer=suggestions__pb2.InitOrderRequest.SerializeToString, + response_deserializer=suggestions__pb2.EventResponse.FromString, + _registered_method=True) + self.PrecomputeSuggestions = channel.unary_unary( + '/suggestions.SuggestionsService/PrecomputeSuggestions', + request_serializer=suggestions__pb2.EventRequest.SerializeToString, + response_deserializer=suggestions__pb2.EventResponse.FromString, + _registered_method=True) + self.FinalizeSuggestions = channel.unary_unary( + '/suggestions.SuggestionsService/FinalizeSuggestions', + request_serializer=suggestions__pb2.EventRequest.SerializeToString, + response_deserializer=suggestions__pb2.SuggestionsEventResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/suggestions.SuggestionsService/ClearOrder', + request_serializer=suggestions__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=suggestions__pb2.EventResponse.FromString, + _registered_method=True) class SuggestionsServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def GetSuggestions(self, request, context): + def InitOrder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def PrecomputeSuggestions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def FinalizeSuggestions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClearOrder(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -33,15 +86,31 @@ def GetSuggestions(self, request, context): def add_SuggestionsServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'GetSuggestions': grpc.unary_unary_rpc_method_handler( - servicer.GetSuggestions, - request_deserializer=suggestions__pb2.SuggestionsRequest.FromString, - response_serializer=suggestions__pb2.SuggestionsResponse.SerializeToString, + 'InitOrder': grpc.unary_unary_rpc_method_handler( + servicer.InitOrder, + request_deserializer=suggestions__pb2.InitOrderRequest.FromString, + response_serializer=suggestions__pb2.EventResponse.SerializeToString, + ), + 'PrecomputeSuggestions': grpc.unary_unary_rpc_method_handler( + servicer.PrecomputeSuggestions, + request_deserializer=suggestions__pb2.EventRequest.FromString, + response_serializer=suggestions__pb2.EventResponse.SerializeToString, + ), + 'FinalizeSuggestions': grpc.unary_unary_rpc_method_handler( + servicer.FinalizeSuggestions, + request_deserializer=suggestions__pb2.EventRequest.FromString, + response_serializer=suggestions__pb2.SuggestionsEventResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=suggestions__pb2.ClearOrderRequest.FromString, + response_serializer=suggestions__pb2.EventResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'suggestions.SuggestionsService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('suggestions.SuggestionsService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -49,7 +118,34 @@ class SuggestionsService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def GetSuggestions(request, + def InitOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/InitOrder', + suggestions__pb2.InitOrderRequest.SerializeToString, + suggestions__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def PrecomputeSuggestions(request, target, options=(), channel_credentials=None, @@ -59,8 +155,72 @@ def GetSuggestions(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/suggestions.SuggestionsService/GetSuggestions', - suggestions__pb2.SuggestionsRequest.SerializeToString, - suggestions__pb2.SuggestionsResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/PrecomputeSuggestions', + suggestions__pb2.EventRequest.SerializeToString, + suggestions__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def FinalizeSuggestions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/FinalizeSuggestions', + suggestions__pb2.EventRequest.SerializeToString, + suggestions__pb2.SuggestionsEventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/ClearOrder', + suggestions__pb2.ClearOrderRequest.SerializeToString, + suggestions__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/utils/pb/transaction_verification/transaction_verification.proto b/utils/pb/transaction_verification/transaction_verification.proto index 44ba9cec7..540261840 100644 --- a/utils/pb/transaction_verification/transaction_verification.proto +++ b/utils/pb/transaction_verification/transaction_verification.proto @@ -3,20 +3,46 @@ syntax = "proto3"; package transaction_verification; service TransactionVerificationService { - rpc VerifyTransaction (TransactionVerificationRequest) returns (TransactionVerificationResponse); + rpc InitOrder (InitOrderRequest) returns (EventResponse); + + rpc ValidateItems (EventRequest) returns (EventResponse); + rpc ValidateUserData (EventRequest) returns (EventResponse); + rpc ValidateCardFormat (EventRequest) returns (EventResponse); + + rpc ClearOrder (ClearOrderRequest) returns (EventResponse); +} + +message VectorClock { + repeated int32 values = 1; +} + +message OrderData { + string order_id = 1; + string user_name = 2; + string user_contact = 3; + string card_number = 4; + string expiration_date = 5; + string cvv = 6; + int32 item_count = 7; + bool terms_accepted = 8; +} + +message InitOrderRequest { + OrderData order = 1; } -message TransactionVerificationRequest { - string user_name = 1; - string user_contact = 2; - string card_number = 3; - string expiration_date = 4; - string cvv = 5; - int32 item_count = 6; - bool terms_accepted = 7; +message EventRequest { + string order_id = 1; + VectorClock vc = 2; } -message TransactionVerificationResponse { - bool is_valid = 1; +message EventResponse { + bool success = 1; string message = 2; + VectorClock vc = 3; +} + +message ClearOrderRequest { + string order_id = 1; + VectorClock final_vc = 2; } \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py index 447731158..0aa59cc2e 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: transaction_verification.proto -# Protobuf Python Version: 4.25.0 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'transaction_verification.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,17 +24,25 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xb0\x01\n\x1eTransactionVerificationRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x14\n\x0cuser_contact\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x03 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x04 \x01(\t\x12\x0b\n\x03\x63vv\x18\x05 \x01(\t\x12\x12\n\nitem_count\x18\x06 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x07 \x01(\x08\"D\n\x1fTransactionVerificationResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xab\x01\n\x1eTransactionVerificationService\x12\x88\x01\n\x11VerifyTransaction\x12\x38.transaction_verification.TransactionVerificationRequest\x1a\x39.transaction_verification.TransactionVerificationResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\x1d\n\x0bVectorClock\x12\x0e\n\x06values\x18\x01 \x03(\x05\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"F\n\x10InitOrderRequest\x12\x32\n\x05order\x18\x01 \x01(\x0b\x32#.transaction_verification.OrderData\"S\n\x0c\x45ventRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x31\n\x02vc\x18\x02 \x01(\x0b\x32%.transaction_verification.VectorClock\"d\n\rEventResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x31\n\x02vc\x18\x03 \x01(\x0b\x32%.transaction_verification.VectorClock\"^\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x37\n\x08\x66inal_vc\x18\x02 \x01(\x0b\x32%.transaction_verification.VectorClock2\x94\x04\n\x1eTransactionVerificationService\x12`\n\tInitOrder\x12*.transaction_verification.InitOrderRequest\x1a\'.transaction_verification.EventResponse\x12`\n\rValidateItems\x12&.transaction_verification.EventRequest\x1a\'.transaction_verification.EventResponse\x12\x63\n\x10ValidateUserData\x12&.transaction_verification.EventRequest\x1a\'.transaction_verification.EventResponse\x12\x65\n\x12ValidateCardFormat\x12&.transaction_verification.EventRequest\x1a\'.transaction_verification.EventResponse\x12\x62\n\nClearOrder\x12+.transaction_verification.ClearOrderRequest\x1a\'.transaction_verification.EventResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_start=61 - _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_end=237 - _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_start=239 - _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_end=307 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=310 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=481 +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_VECTORCLOCK']._serialized_start=60 + _globals['_VECTORCLOCK']._serialized_end=89 + _globals['_ORDERDATA']._serialized_start=92 + _globals['_ORDERDATA']._serialized_end=265 + _globals['_INITORDERREQUEST']._serialized_start=267 + _globals['_INITORDERREQUEST']._serialized_end=337 + _globals['_EVENTREQUEST']._serialized_start=339 + _globals['_EVENTREQUEST']._serialized_end=422 + _globals['_EVENTRESPONSE']._serialized_start=424 + _globals['_EVENTRESPONSE']._serialized_end=524 + _globals['_CLEARORDERREQUEST']._serialized_start=526 + _globals['_CLEARORDERREQUEST']._serialized_end=620 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=623 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=1155 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.pyi b/utils/pb/transaction_verification/transaction_verification_pb2.pyi index 5bb457074..fe8cb8ab1 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.pyi +++ b/utils/pb/transaction_verification/transaction_verification_pb2.pyi @@ -1,11 +1,19 @@ +from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Optional as _Optional +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor -class TransactionVerificationRequest(_message.Message): - __slots__ = ("user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") +class VectorClock(_message.Message): + __slots__ = ("values",) + VALUES_FIELD_NUMBER: _ClassVar[int] + values: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, values: _Optional[_Iterable[int]] = ...) -> None: ... + +class OrderData(_message.Message): + __slots__ = ("order_id", "user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] USER_NAME_FIELD_NUMBER: _ClassVar[int] USER_CONTACT_FIELD_NUMBER: _ClassVar[int] CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] @@ -13,6 +21,7 @@ class TransactionVerificationRequest(_message.Message): CVV_FIELD_NUMBER: _ClassVar[int] ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] + order_id: str user_name: str user_contact: str card_number: str @@ -20,12 +29,36 @@ class TransactionVerificationRequest(_message.Message): cvv: str item_count: int terms_accepted: bool - def __init__(self, user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + def __init__(self, order_id: _Optional[str] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + +class InitOrderRequest(_message.Message): + __slots__ = ("order",) + ORDER_FIELD_NUMBER: _ClassVar[int] + order: OrderData + def __init__(self, order: _Optional[_Union[OrderData, _Mapping]] = ...) -> None: ... -class TransactionVerificationResponse(_message.Message): - __slots__ = ("is_valid", "message") - IS_VALID_FIELD_NUMBER: _ClassVar[int] +class EventRequest(_message.Message): + __slots__ = ("order_id", "vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... + +class EventResponse(_message.Message): + __slots__ = ("success", "message", "vc") + SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] - is_valid: bool + VC_FIELD_NUMBER: _ClassVar[int] + success: bool message: str - def __init__(self, is_valid: bool = ..., message: _Optional[str] = ...) -> None: ... + vc: VectorClock + def __init__(self, success: bool = ..., message: _Optional[str] = ..., vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... + +class ClearOrderRequest(_message.Message): + __slots__ = ("order_id", "final_vc") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FIELD_NUMBER: _ClassVar[int] + order_id: str + final_vc: VectorClock + def __init__(self, order_id: _Optional[str] = ..., final_vc: _Optional[_Union[VectorClock, _Mapping]] = ...) -> None: ... diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py index 32d7e091c..aa188d87d 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -1,9 +1,29 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings import transaction_verification_pb2 as transaction__verification__pb2 +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in transaction_verification_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class TransactionVerificationServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -14,17 +34,61 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.VerifyTransaction = channel.unary_unary( - '/transaction_verification.TransactionVerificationService/VerifyTransaction', - request_serializer=transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, - response_deserializer=transaction__verification__pb2.TransactionVerificationResponse.FromString, - ) + self.InitOrder = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/InitOrder', + request_serializer=transaction__verification__pb2.InitOrderRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.EventResponse.FromString, + _registered_method=True) + self.ValidateItems = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/ValidateItems', + request_serializer=transaction__verification__pb2.EventRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.EventResponse.FromString, + _registered_method=True) + self.ValidateUserData = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/ValidateUserData', + request_serializer=transaction__verification__pb2.EventRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.EventResponse.FromString, + _registered_method=True) + self.ValidateCardFormat = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/ValidateCardFormat', + request_serializer=transaction__verification__pb2.EventRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.EventResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/ClearOrder', + request_serializer=transaction__verification__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=transaction__verification__pb2.EventResponse.FromString, + _registered_method=True) class TransactionVerificationServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def VerifyTransaction(self, request, context): + def InitOrder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ValidateItems(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ValidateUserData(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ValidateCardFormat(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClearOrder(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -33,15 +97,36 @@ def VerifyTransaction(self, request, context): def add_TransactionVerificationServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'VerifyTransaction': grpc.unary_unary_rpc_method_handler( - servicer.VerifyTransaction, - request_deserializer=transaction__verification__pb2.TransactionVerificationRequest.FromString, - response_serializer=transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, + 'InitOrder': grpc.unary_unary_rpc_method_handler( + servicer.InitOrder, + request_deserializer=transaction__verification__pb2.InitOrderRequest.FromString, + response_serializer=transaction__verification__pb2.EventResponse.SerializeToString, + ), + 'ValidateItems': grpc.unary_unary_rpc_method_handler( + servicer.ValidateItems, + request_deserializer=transaction__verification__pb2.EventRequest.FromString, + response_serializer=transaction__verification__pb2.EventResponse.SerializeToString, + ), + 'ValidateUserData': grpc.unary_unary_rpc_method_handler( + servicer.ValidateUserData, + request_deserializer=transaction__verification__pb2.EventRequest.FromString, + response_serializer=transaction__verification__pb2.EventResponse.SerializeToString, + ), + 'ValidateCardFormat': grpc.unary_unary_rpc_method_handler( + servicer.ValidateCardFormat, + request_deserializer=transaction__verification__pb2.EventRequest.FromString, + response_serializer=transaction__verification__pb2.EventResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=transaction__verification__pb2.ClearOrderRequest.FromString, + response_serializer=transaction__verification__pb2.EventResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'transaction_verification.TransactionVerificationService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('transaction_verification.TransactionVerificationService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -49,7 +134,88 @@ class TransactionVerificationService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def VerifyTransaction(request, + def InitOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/InitOrder', + transaction__verification__pb2.InitOrderRequest.SerializeToString, + transaction__verification__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ValidateItems(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/ValidateItems', + transaction__verification__pb2.EventRequest.SerializeToString, + transaction__verification__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ValidateUserData(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/ValidateUserData', + transaction__verification__pb2.EventRequest.SerializeToString, + transaction__verification__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ValidateCardFormat(request, target, options=(), channel_credentials=None, @@ -59,8 +225,45 @@ def VerifyTransaction(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/transaction_verification.TransactionVerificationService/VerifyTransaction', - transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, - transaction__verification__pb2.TransactionVerificationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/ValidateCardFormat', + transaction__verification__pb2.EventRequest.SerializeToString, + transaction__verification__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/ClearOrder', + transaction__verification__pb2.ClearOrderRequest.SerializeToString, + transaction__verification__pb2.EventResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) From 2b2d913e1e9e9780e3bb692c7c5924bf381365a3 Mon Sep 17 00:00:00 2001 From: anupkumar Date: Fri, 13 Mar 2026 13:22:14 +0200 Subject: [PATCH 20/58] code refactoring and adding vector clock implementation --- docker-compose.yaml | 4 + fraud_detection/requirements.txt | 6 +- fraud_detection/src/app.py | 341 ++++++++-- orchestrator/requirements.txt | 6 +- orchestrator/src/app.py | 583 +++++++++++++++--- suggestions/requirements.txt | 6 +- suggestions/src/app.py | 199 +++++- transaction_verification/requirements.txt | 6 +- transaction_verification/src/app.py | 345 +++++++++-- .../pb/fraud_detection/fraud_detection.proto | 60 +- .../pb/fraud_detection/fraud_detection_pb2.py | 48 +- .../fraud_detection/fraud_detection_pb2.pyi | 76 ++- .../fraud_detection_pb2_grpc.py | 199 ++++-- utils/pb/suggestions/suggestions.proto | 46 +- utils/pb/suggestions/suggestions_pb2.py | 48 +- utils/pb/suggestions/suggestions_pb2_grpc.py | 182 +++++- .../transaction_verification.proto | 52 +- .../transaction_verification_pb2.py | 44 +- .../transaction_verification_pb2_grpc.py | 182 +++++- 19 files changed, 2094 insertions(+), 339 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index e4818a9c8..6eabdff43 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -31,6 +31,7 @@ services: # The PYTHONFILE environment variable specifies the absolute entry point of the application # Check app.py in the orchestrator directory to see how this is used - PYTHONFILE=/app/orchestrator/src/app.py + - PYTHONPATH=/app volumes: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils @@ -54,6 +55,7 @@ services: # The PYTHONFILE environment variable specifies the absolute entry point of the application # Check app.py in the fraud_detection directory to see how this is used - PYTHONFILE=/app/fraud_detection/src/app.py + - PYTHONPATH=/app volumes: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils @@ -74,6 +76,7 @@ services: - PYTHONUNBUFFERED=TRUE # The PYTHONFILE environment variable specifies the absolute entry point of the application - PYTHONFILE=/app/transaction_verification/src/app.py + - PYTHONPATH=/app volumes: # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils @@ -89,6 +92,7 @@ services: environment: - PYTHONUNBUFFERED=TRUE - PYTHONFILE=/app/suggestions/src/app.py + - PYTHONPATH=/app volumes: - ./utils:/app/utils - ./suggestions/src:/app/suggestions/src \ No newline at end of file diff --git a/fraud_detection/requirements.txt b/fraud_detection/requirements.txt index a80eedef7..cdb30b264 100644 --- a/fraud_detection/requirements.txt +++ b/fraud_detection/requirements.txt @@ -1,4 +1,4 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 +protobuf==6.31.1 watchdog==6.0.0 diff --git a/fraud_detection/src/app.py b/fraud_detection/src/app.py index ecee0fad5..74d48c6e0 100644 --- a/fraud_detection/src/app.py +++ b/fraud_detection/src/app.py @@ -1,11 +1,12 @@ import sys import os +import threading +import time from concurrent import futures -# This set of lines are needed to import the gRPC stubs. -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") fraud_detection_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/fraud_detection') + os.path.join(FILE, "../../../utils/pb/fraud_detection") ) sys.path.insert(0, fraud_detection_grpc_path) @@ -13,66 +14,302 @@ import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc +order_cache = {} +vector_clocks = {} +cache_lock = threading.Lock() -class HelloService(fraud_detection_grpc.HelloServiceServicer): - def SayHello(self, request, context): - response = fraud_detection.HelloResponse() - response.greeting = "Hello, " + request.name - print(response.greeting) - return response +def initialize_vector_clock(order_id): + return { + "fraud_detection": 0, + "transaction_verification": 0, + "suggestions": 0, + "tv_event_a": 0, + "tv_event_b": 0, + "tv_event_c": 0, + "fd_event_d": 0, + "fd_event_e": 0, + } + + +def check_event_dependencies(vc, required_events): + for event in required_events: + if vc.get(event, 0) == 0: + return False, f"Required event {event} not completed" + return True, "OK" + + +def execute_event_d(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["fd_event_d"] += 1 + + user_name = order_data.get("user_name", "") + is_fraud = "fraud" in user_name.lower() + message = ( + "User data fraud check passed" if not is_fraud else "Suspicious user name." + ) + + print(f"Event d (check_user_fraud) for order {order_id}: {is_fraud}, {message}") + return {"is_fraud": is_fraud, "message": message} + + +def execute_event_e(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["fd_event_e"] += 1 + + card_number = order_data.get("card_number", "") + card_digits = extract_card_digits(card_number) + item_count = order_data.get("item_count", 0) + + is_fraud = False + message = "Credit card fraud check passed" + + if item_count > 20: + is_fraud = True + message = "Too many items in order." + elif len(card_digits) != 16: + is_fraud = True + message = "Invalid card number." + elif card_digits.startswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif card_digits.endswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + + print(f"Event e (check_card_fraud) for order {order_id}: {is_fraud}, {message}") + return {"is_fraud": is_fraud, "message": message} + + +def process_order(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["fraud_detection"] += 1 + + card_digits = extract_card_digits(order_data["card_number"]) + is_fraud = False + message = "No fraud detected." + + if order_data["item_count"] > 20: + is_fraud = True + message = "Too many items in order." + elif len(card_digits) != 16: + is_fraud = True + message = "Invalid card number." + elif card_digits.startswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif card_digits.endswith("0000"): + is_fraud = True + message = "Suspicious card number pattern." + elif "fraud" in order_data["user_name"].lower(): + is_fraud = True + message = "Suspicious user name." + + result = { + "is_fraud": is_fraud, + "message": message, + "vector_clock": vector_clocks[order_id].copy(), + } + + print(f"Fraud detection processed for order {order_id}: {is_fraud}, {message}") + return result + + +class FraudDetectionService(fraud_detection_grpc.FraudDetectionServiceServicer): def CheckFraud(self, request, context): - print("Received fraud check request") - print("user_name:", request.user_name) - masked_card_number = mask_fixed(request.card_number) - print("card_number:", masked_card_number) - print("item_count:", request.item_count) - - # Compute length based on digits only to avoid counting spaces or other characters - card_digits = extract_card_digits(request.card_number) - print("card length (digits only):", len(card_digits)) - is_fraud = False - message = "No fraud detected." - - # Very simple dummy rules for now - if request.item_count > 20: - is_fraud = True - message = "Too many items in order." - - # Treat any non-16-digit card number as invalid - elif len(card_digits) != 16: - is_fraud = True - message = "Invalid card number." - - elif card_digits.startswith("0000"): - is_fraud = True - message = "Suspicious card number pattern." - - elif card_digits.endswith("0000"): - is_fraud = True - message = "Suspicious card number pattern." - elif "fraud" in request.user_name.lower(): - is_fraud = True - message = "Suspicious user name." + order_id = request.order_id if hasattr(request, "order_id") else "" + print(f"Received fraud check request for order: {order_id}") + + with cache_lock: + order_cache[order_id] = { + "user_name": request.user_name, + "card_number": request.card_number, + "item_count": request.item_count, + } + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vector_clocks[order_id]["fraud_detection"] = 0 + + print( + f"Order {order_id} cached in fraud_detection. Vector clock: {vector_clocks.get(order_id)}" + ) response = fraud_detection.FraudCheckResponse() - response.is_fraud = is_fraud - response.message = message + response.is_fraud = False + response.message = "Order cached. Waiting for execution trigger." - print("Returning fraud result:", response.is_fraud, response.message) return response + def TriggerFraudCheck(self, request, context): + order_id = request.order_id + event_type = request.event_type if hasattr(request, "event_type") else "all" + print(f"Triggering fraud check for order {order_id}, event: {event_type}") + + with cache_lock: + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vc = vector_clocks.get(order_id, {}) + + if event_type == "all": + result = process_order(order_id) + if result: + with cache_lock: + if order_id in order_cache: + del order_cache[order_id] + response = fraud_detection.FraudCheckResponse() + response.is_fraud = result["is_fraud"] + response.message = result["message"] + response.failed = result["is_fraud"] + print( + f"[VC] Fraud detection for order {order_id}: VC={vector_clocks.get(order_id, {})}" + ) + return response + elif event_type == "event_d": + # Event D (check_user_fraud) should first be cleared by transaction-verification service (Event B) + can_proceed, msg = check_event_dependencies(vc, ["tv_event_b"]) + if not can_proceed: + # Update current VC state from other services to be sure + # In a real system we would use the passed VC or fetch it. + # For this task, we assume the orchestrator will only call this after tv_event_b is done. + # But to be robust, we can return a failure that orchestrator handles. + response = fraud_detection.FraudCheckResponse() + response.is_fraud = False + response.message = f"Cannot execute event_d: {msg}" + response.failed = True + return response + result = execute_event_d(order_id) + if result: + response = fraud_detection.FraudCheckResponse() + response.is_fraud = result["is_fraud"] + response.message = result["message"] + response.failed = result["is_fraud"] + print( + f"[VC] Event d for order {order_id}: VC fd_event_d={vector_clocks.get(order_id, {}).get('fd_event_d', 0)}" + ) + return response + elif event_type == "event_e": + # Event E (check_card_fraud) should first be cleared by transaction-verification (Event C) and fraud-detection (Event D) + can_proceed, msg = check_event_dependencies( + vc, ["tv_event_c", "fd_event_d"] + ) + if not can_proceed: + response = fraud_detection.FraudCheckResponse() + response.is_fraud = False + response.message = f"Cannot execute event_e: {msg}" + response.failed = True + return response + result = execute_event_e(order_id) + if result: + response = fraud_detection.FraudCheckResponse() + response.is_fraud = result["is_fraud"] + response.message = result["message"] + response.failed = result["is_fraud"] + print( + f"[VC] Event e for order {order_id}: VC fd_event_e={vector_clocks.get(order_id, {}).get('fd_event_e', 0)}" + ) + return response + + response = fraud_detection.FraudCheckResponse() + response.is_fraud = True + response.message = "Order not found in cache or invalid event type." + response.failed = True + return response + + def GetVectorClock(self, request, context): + order_id = request.order_id + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + response = fraud_detection.VectorClockResponse() + response.fraud_detection = vc.get("fraud_detection", 0) + response.fd_event_d = vc.get("fd_event_d", 0) + response.fd_event_e = vc.get("fd_event_e", 0) + response.transaction_verification = vc.get("transaction_verification", 0) + response.tv_event_a = vc.get("tv_event_a", 0) + response.tv_event_b = vc.get("tv_event_b", 0) + response.tv_event_c = vc.get("tv_event_c", 0) + response.suggestions = vc.get("suggestions", 0) + return response + + def ClearOrder(self, request, context): + order_id = request.order_id + print(f"Received ClearOrder request for order: {order_id}") + + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + local_vc_fd = vc.get("fraud_detection", 0) + local_vc_d = vc.get("fd_event_d", 0) + local_vc_e = vc.get("fd_event_e", 0) + local_vc_tv = vc.get("transaction_verification", 0) + local_vc_tv_a = vc.get("tv_event_a", 0) + local_vc_tv_b = vc.get("tv_event_b", 0) + local_vc_tv_c = vc.get("tv_event_c", 0) + local_vc_suggestions = vc.get("suggestions", 0) + + expected_vc_fd = request.final_vc_fraud_detection + expected_vc_d = request.final_vc_fd_event_d + expected_vc_e = request.final_vc_fd_event_e + expected_vc_tv = request.final_vc_transaction_verification + expected_vc_tv_a = request.final_vc_tv_event_a + expected_vc_tv_b = request.final_vc_tv_event_b + expected_vc_tv_c = request.final_vc_tv_event_c + expected_vc_suggestions = request.final_vc_suggestions + + if ( + local_vc_fd <= expected_vc_fd + and local_vc_d <= expected_vc_d + and local_vc_e <= expected_vc_e + and local_vc_tv <= expected_vc_tv + and local_vc_tv_a <= expected_vc_tv_a + and local_vc_tv_b <= expected_vc_tv_b + and local_vc_tv_c <= expected_vc_tv_c + and local_vc_suggestions <= expected_vc_suggestions + ): + if order_id in order_cache: + del order_cache[order_id] + if order_id in vector_clocks: + del vector_clocks[order_id] + print(f"Order {order_id} cleared successfully in fraud_detection") + response = fraud_detection.ClearOrderResponse() + response.success = True + response.message = "Order cleared successfully" + return response + else: + print(f"Vector clock mismatch for order {order_id}. Local VC: {vc}") + response = fraud_detection.ClearOrderResponse() + response.success = False + response.message = ( + f"Vector clock mismatch. Local: fd={local_vc_fd}, d={local_vc_d}, e={local_vc_e}, tv={local_vc_tv}, tv_a={local_vc_tv_a}, tv_b={local_vc_tv_b}, tv_c={local_vc_tv_c}, suggestions={local_vc_suggestions}. " + f"Expected: fd={expected_vc_fd}, d={expected_vc_d}, e={expected_vc_e}, tv={expected_vc_tv}, tv_a={expected_vc_tv_a}, tv_b={expected_vc_tv_b}, tv_c={expected_vc_tv_c}, suggestions={expected_vc_suggestions}" + ) + return response + def extract_card_digits(card: str) -> str: """ Return only the digit characters from the given card number. """ - return ''.join(c for c in str(card) if c.isdigit()) + return "".join(c for c in str(card) if c.isdigit()) def serve(): server = grpc.server(futures.ThreadPoolExecutor()) - fraud_detection_grpc.add_HelloServiceServicer_to_server(HelloService(), server) + fraud_detection_grpc.add_FraudDetectionServiceServicer_to_server( + FraudDetectionService(), server + ) port = "50051" server.add_insecure_port("[::]:" + port) @@ -80,10 +317,12 @@ def serve(): print("Fraud detection server started. Listening on port 50051.") server.wait_for_termination() + def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) + digits = "".join(c for c in str(card) if c.isdigit()) + masked = "*" * 12 + digits[-4:].rjust(4, "*") + return " ".join(masked[i : i + 4] for i in range(0, 16, 4)) + -if __name__ == '__main__': - serve() \ No newline at end of file +if __name__ == "__main__": + serve() diff --git a/orchestrator/requirements.txt b/orchestrator/requirements.txt index 5ba8e254b..626611aaf 100644 --- a/orchestrator/requirements.txt +++ b/orchestrator/requirements.txt @@ -1,12 +1,12 @@ blinker==1.7.0 click==8.1.7 Flask==3.0.0 -grpcio==1.60.0 -grpcio-tools==1.60.0 +grpcio==1.78.0 +grpcio-tools==1.78.0 itsdangerous==2.1.2 Jinja2==3.1.3 MarkupSafe==2.1.3 -protobuf==4.25.2 +protobuf==6.31.1 Werkzeug==3.0.1 Flask-CORS==4.0.0 watchdog==6.0.0 \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 590815ff5..38fe8ed58 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -1,28 +1,29 @@ import sys import os import threading +import uuid # This set of lines are needed to import the gRPC stubs. # The path of the stubs is relative to the current file, or absolute inside the container. # Change these lines only if strictly needed. -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") fraud_detection_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/fraud_detection') + os.path.join(FILE, "../../../utils/pb/fraud_detection") ) sys.path.insert(0, fraud_detection_grpc_path) import fraud_detection_pb2 as fraud_detection import fraud_detection_pb2_grpc as fraud_detection_grpc transaction_verification_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/transaction_verification') + os.path.join(FILE, "../../../utils/pb/transaction_verification") ) sys.path.insert(0, transaction_verification_grpc_path) import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc suggestions_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/suggestions') + os.path.join(FILE, "../../../utils/pb/suggestions") ) sys.path.insert(0, suggestions_grpc_path) import suggestions_pb2 as suggestions @@ -34,66 +35,226 @@ from flask_cors import CORS -def greet(name='you'): - with grpc.insecure_channel('fraud_detection:50051') as channel: +def greet(name="you"): + with grpc.insecure_channel("fraud_detection:50051") as channel: stub = fraud_detection_grpc.HelloServiceStub(channel) response = stub.SayHello(fraud_detection.HelloRequest(name=name)) return response.greeting -def detect_fraud(user_name, card_number, item_count): - with grpc.insecure_channel('fraud_detection:50051') as channel: - stub = fraud_detection_grpc.HelloServiceStub(channel) +def detect_fraud(order_id, user_name, card_number, item_count): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) response = stub.CheckFraud( fraud_detection.FraudCheckRequest( + order_id=order_id, user_name=user_name or "", card_number=card_number or "", - item_count=item_count + item_count=item_count, + ), + timeout=5.0, + ) + return response + + +def trigger_fraud_check(order_id, event_type="all"): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + response = stub.TriggerFraudCheck( + fraud_detection.TriggerRequest(order_id=order_id, event_type=event_type), + timeout=5.0, + ) + return response + + +def get_fraud_vector_clock(order_id): + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + response = stub.GetVectorClock( + fraud_detection.VectorClockRequest(order_id=order_id), + timeout=5.0, + ) + return response + + +def trigger_verification(order_id, event_type="all"): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + response = stub.TriggerVerification( + transaction_verification.TriggerRequest( + order_id=order_id, event_type=event_type ), - timeout=5.0 # seconds + timeout=5.0, + ) + return response + + +def get_verification_vector_clock(order_id): + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) + response = stub.GetVectorClock( + transaction_verification.VectorClockRequest(order_id=order_id), + timeout=5.0, + ) + return response + + +def trigger_suggestions(order_id, event_type="all"): + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + response = stub.TriggerSuggestions( + suggestions.TriggerRequest(order_id=order_id, event_type=event_type), + timeout=5.0, ) return response -def verify_transaction(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): - with grpc.insecure_channel('transaction_verification:50052') as channel: + +def get_suggestions_vector_clock(order_id): + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + response = stub.GetVectorClock( + suggestions.VectorClockRequest(order_id=order_id), + timeout=5.0, + ) + return response + + +def broadcast_clear_order(order_id, final_vc): + broadcast_results = {} + + try: + with grpc.insecure_channel("fraud_detection:50051") as channel: + stub = fraud_detection_grpc.FraudDetectionServiceStub(channel) + response = stub.ClearOrder( + fraud_detection.ClearOrderRequest( + order_id=order_id, + final_vc_fraud_detection=final_vc.get("fraud_detection", 0), + final_vc_fd_event_d=final_vc.get("fd_event_d", 0), + final_vc_fd_event_e=final_vc.get("fd_event_e", 0), + final_vc_transaction_verification=final_vc.get( + "transaction_verification", 0 + ), + final_vc_tv_event_a=final_vc.get("tv_event_a", 0), + final_vc_tv_event_b=final_vc.get("tv_event_b", 0), + final_vc_tv_event_c=final_vc.get("tv_event_c", 0), + final_vc_suggestions=final_vc.get("suggestions", 0), + ), + timeout=5.0, + ) + broadcast_results["fraud_detection"] = { + "success": response.success, + "message": response.message, + } + except Exception as e: + broadcast_results["fraud_detection"] = {"success": False, "message": str(e)} + + try: + with grpc.insecure_channel("transaction_verification:50052") as channel: + stub = transaction_verification_grpc.TransactionVerificationServiceStub( + channel + ) + response = stub.ClearOrder( + transaction_verification.ClearOrderRequest( + order_id=order_id, + final_vc_transaction_verification=final_vc.get( + "transaction_verification", 0 + ), + final_vc_tv_event_a=final_vc.get("tv_event_a", 0), + final_vc_tv_event_b=final_vc.get("tv_event_b", 0), + final_vc_tv_event_c=final_vc.get("tv_event_c", 0), + final_vc_fraud_detection=final_vc.get("fraud_detection", 0), + final_vc_suggestions=final_vc.get("suggestions", 0), + ), + timeout=5.0, + ) + broadcast_results["transaction_verification"] = { + "success": response.success, + "message": response.message, + } + except Exception as e: + broadcast_results["transaction_verification"] = { + "success": False, + "message": str(e), + } + + try: + with grpc.insecure_channel("suggestions:50053") as channel: + stub = suggestions_grpc.SuggestionsServiceStub(channel) + response = stub.ClearOrder( + suggestions.ClearOrderRequest( + order_id=order_id, + final_vc_suggestions=final_vc.get("suggestions", 0), + final_vc_fraud_detection=final_vc.get("fraud_detection", 0), + final_vc_fd_event_d=final_vc.get("fd_event_d", 0), + final_vc_fd_event_e=final_vc.get("fd_event_e", 0), + final_vc_transaction_verification=final_vc.get( + "transaction_verification", 0 + ), + final_vc_tv_event_a=final_vc.get("tv_event_a", 0), + final_vc_tv_event_b=final_vc.get("tv_event_b", 0), + final_vc_tv_event_c=final_vc.get("tv_event_c", 0), + ), + timeout=5.0, + ) + broadcast_results["suggestions"] = { + "success": response.success, + "message": response.message, + } + except Exception as e: + broadcast_results["suggestions"] = {"success": False, "message": str(e)} + + return broadcast_results + + +def verify_transaction( + order_id, + user_name, + user_contact, + card_number, + expiration_date, + cvv, + item_count, + terms_accepted, +): + with grpc.insecure_channel("transaction_verification:50052") as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) response = stub.VerifyTransaction( transaction_verification.TransactionVerificationRequest( + order_id=order_id, user_name=user_name or "", user_contact=user_contact or "", card_number=card_number or "", expiration_date=expiration_date or "", cvv=cvv or "", item_count=item_count, - terms_accepted=terms_accepted + terms_accepted=terms_accepted, ) ) return response -def get_suggestions(user_name, item_count): - with grpc.insecure_channel('suggestions:50053') as channel: +def get_suggestions(order_id, user_name, item_count): + with grpc.insecure_channel("suggestions:50053") as channel: stub = suggestions_grpc.SuggestionsServiceStub(channel) response = stub.GetSuggestions( suggestions.SuggestionsRequest( - user_name=user_name or "", - item_count=item_count + order_id=order_id, user_name=user_name or "", item_count=item_count ) ) return response app = Flask(__name__) -CORS(app, resources={r'/*': {'origins': '*'}}) +CORS(app, resources={r"/*": {"origins": "*"}}) -@app.route('/', methods=['GET']) +@app.route("/", methods=["GET"]) def index(): - response = greet(name='orchestrator') + response = greet(name="orchestrator") return response -@app.route('/checkout', methods=['POST']) +@app.route("/checkout", methods=["POST"]) def checkout(): request_data = request.get_json(silent=True) @@ -101,7 +262,7 @@ def checkout(): return { "error": { "code": "BAD_REQUEST", - "message": "Request body must be valid JSON." + "message": "Request body must be valid JSON.", } }, 400 @@ -126,14 +287,13 @@ def checkout(): ) ) - # Keep these simple bad-request checks locally # user_name must not be empty or contain spaces if not user_name or "" == user_name: return { "error": { "code": "BAD_REQUEST", - "message": "User name is required and must not contain spaces." + "message": "User name is required and must not contain spaces.", } }, 400 @@ -141,71 +301,59 @@ def checkout(): return { "error": { "code": "BAD_REQUEST", - "message": "User contact is required and must not contain spaces." + "message": "User contact is required and must not contain spaces.", } }, 400 item_count = len(items) + order_id = str(uuid.uuid4()) - results = { - "fraud": None, - "verification": None, - "suggestions": None, - "errors": [] - } + results = {"fraud": None, "verification": None, "suggestions": None, "errors": []} def fraud_worker(): try: - print("Calling fraud_detection service...") - results["fraud"] = detect_fraud( + print(f"Calling fraud_detection service to cache order {order_id}...") + detect_fraud( + order_id=order_id, user_name=user_name, card_number=card_number, - item_count=item_count - ) - print( - "fraud_detection result:", - results["fraud"].is_fraud, - results["fraud"].message + item_count=item_count, ) + print(f"Order {order_id} cached in fraud_detection") except Exception as e: - print(f"fraud_detection failed: {e}") # Full detail for server logs - results["errors"].append("fraud_detection service unavailable") # Sanitized for client + print(f"fraud_detection caching failed: {e}") + results["errors"].append("fraud_detection service unavailable") + def verification_worker(): try: - print("Calling transaction_verification service...") - results["verification"] = verify_transaction( + print( + f"Calling transaction_verification service to cache order {order_id}..." + ) + verify_transaction( + order_id=order_id, user_name=user_name, user_contact=user_contact, card_number=card_number, expiration_date=expiration_date, cvv=cvv, item_count=item_count, - terms_accepted=terms_accepted - ) - print( - "transaction_verification result:", - results["verification"].is_valid, - results["verification"].message + terms_accepted=terms_accepted, ) + print(f"Order {order_id} cached in transaction_verification") except Exception as e: - error_msg = f"transaction_verification failed: {e}" + error_msg = f"transaction_verification caching failed: {e}" print(error_msg) results["errors"].append(error_msg) def suggestions_worker(): try: - print("Calling suggestions service...") - results["suggestions"] = get_suggestions( - user_name=user_name, - item_count=item_count - ) - print( - "suggestions returned:", - len(results["suggestions"].books), - "books" + print(f"Calling suggestions service to cache order {order_id}...") + get_suggestions( + order_id=order_id, user_name=user_name, item_count=item_count ) + print(f"Order {order_id} cached in suggestions") except Exception as e: - error_msg = f"suggestions failed: {e}" + error_msg = f"suggestions caching failed: {e}" print(error_msg) results["errors"].append(error_msg) @@ -213,57 +361,326 @@ def suggestions_worker(): verification_thread = threading.Thread(target=verification_worker) suggestions_thread = threading.Thread(target=suggestions_worker) - print("Starting worker threads...") + print("Starting worker threads to cache orders...") fraud_thread.start() verification_thread.start() suggestions_thread.start() + # The requirement says: "some of the threads created by the orchestrator may finish right away, + # and only one thread may wait for the end of the execution flow". + # We will let fraud and suggestions threads finish initialization quickly. + # Verification thread will be the one we join to ensure basic caching is done before triggers. + # Actually, to be safe, we join all to ensure all services have the data before we start triggering. + # But for the task, let's just join them as before, they are "initialization" threads. fraud_thread.join() verification_thread.join() suggestions_thread.join() - print("All worker threads finished.") + print("All caching threads finished. Starting event-based execution...") + + event_results = { + "event_a": None, + "event_b": None, + "event_c": None, + "event_d": None, + "event_e": None, + "event_f": None, + } + failed = False + failure_message = "" + + def trigger_event_a(): + try: + print( + f"Triggering event_a (verify_items_not_empty) for order {order_id}..." + ) + event_results["event_a"] = trigger_verification(order_id, "event_a") + vc = get_verification_vector_clock(order_id) + print( + f"event_a result: {event_results['event_a'].is_valid}, {event_results['event_a'].message}, VC: a={vc.tv_event_a}" + ) + if not event_results["event_a"].is_valid: + return True, event_results["event_a"].message + except Exception as e: + print(f"event_a failed: {e}") + return True, str(e) + return False, "" + + def trigger_event_b(): + try: + print(f"Triggering event_b (verify_user_data) for order {order_id}...") + event_results["event_b"] = trigger_verification(order_id, "event_b") + vc = get_verification_vector_clock(order_id) + print( + f"event_b result: {event_results['event_b'].is_valid}, {event_results['event_b'].message}, VC: b={vc.tv_event_b}" + ) + if not event_results["event_b"].is_valid: + return True, event_results["event_b"].message + except Exception as e: + print(f"event_b failed: {e}") + return True, str(e) + return False, "" + + thread_a = threading.Thread(target=lambda: None) + thread_b = threading.Thread(target=lambda: None) + + def run_a(): + nonlocal failed, failure_message + f, m = trigger_event_a() + if f: + failed = True + failure_message = m + + def run_b(): + nonlocal failed, failure_message + f, m = trigger_event_b() + if f: + failed = True + failure_message = m + + thread_a = threading.Thread(target=run_a) + thread_b = threading.Thread(target=run_b) + + thread_a.start() + thread_b.start() + thread_a.join() + thread_b.join() + + if failed: + print(f"Early failure detected in events a/b: {failure_message}") + vc_fraud = get_fraud_vector_clock(order_id) + vc_verif = get_verification_vector_clock(order_id) + final_vc = { + "fraud_detection": vc_fraud.fraud_detection, + "fd_event_d": vc_fraud.fd_event_d, + "fd_event_e": vc_fraud.fd_event_e, + "transaction_verification": vc_verif.transaction_verification, + "tv_event_a": vc_verif.tv_event_a, + "tv_event_b": vc_verif.tv_event_b, + "tv_event_c": vc_verif.tv_event_c, + "suggestions": 0, + } + print(f"Broadcasting clear order with final VC: {final_vc}") + broadcast_results = broadcast_clear_order(order_id, final_vc) + print(f"Broadcast results: {broadcast_results}") + return { + "orderId": order_id, + "status": "Order Rejected", + "suggestedBooks": [], + "failure": failure_message, + }, 200 + + print("Events a and b completed. Vector clocks updated.") + + def trigger_event_c(): + try: + print(f"Triggering event_c (verify_card_format) for order {order_id}...") + event_results["event_c"] = trigger_verification(order_id, "event_c") + vc = get_verification_vector_clock(order_id) + print( + f"event_c result: {event_results['event_c'].is_valid}, {event_results['event_c'].message}, VC: c={vc.tv_event_c}" + ) + if not event_results["event_c"].is_valid: + return True, event_results["event_c"].message + except Exception as e: + print(f"event_c failed: {e}") + return True, str(e) + return False, "" + + def trigger_event_d(): + try: + print(f"Triggering event_d (check_user_fraud) for order {order_id}...") + event_results["event_d"] = trigger_fraud_check(order_id, "event_d") + vc = get_fraud_vector_clock(order_id) + print( + f"event_d result: {event_results['event_d'].is_fraud}, {event_results['event_d'].message}, VC: d={vc.fd_event_d}" + ) + if event_results["event_d"].is_fraud: + return True, event_results["event_d"].message + except Exception as e: + print(f"event_d failed: {e}") + return True, str(e) + return False, "" + + thread_c = threading.Thread(target=lambda: None) + thread_d = threading.Thread(target=lambda: None) + + def run_c(): + nonlocal failed, failure_message + f, m = trigger_event_c() + if f: + failed = True + failure_message = m + + def run_d(): + nonlocal failed, failure_message + f, m = trigger_event_d() + if f: + failed = True + failure_message = m + + thread_c = threading.Thread(target=run_c) + thread_d = threading.Thread(target=run_d) + + thread_c.start() + thread_d.start() + thread_c.join() + thread_d.join() + + if failed: + print(f"Early failure detected in events c/d: {failure_message}") + vc_fraud = get_fraud_vector_clock(order_id) + vc_verif = get_verification_vector_clock(order_id) + final_vc = { + "fraud_detection": vc_fraud.fraud_detection, + "fd_event_d": vc_fraud.fd_event_d, + "fd_event_e": vc_fraud.fd_event_e, + "transaction_verification": vc_verif.transaction_verification, + "tv_event_a": vc_verif.tv_event_a, + "tv_event_b": vc_verif.tv_event_b, + "tv_event_c": vc_verif.tv_event_c, + "suggestions": 0, + } + print(f"Broadcasting clear order with final VC: {final_vc}") + broadcast_results = broadcast_clear_order(order_id, final_vc) + print(f"Broadcast results: {broadcast_results}") + return { + "orderId": order_id, + "status": "Order Rejected", + "suggestedBooks": [], + "failure": failure_message, + }, 200 + + print("Events c and d completed.") + + def trigger_event_e(): + try: + print(f"Triggering event_e (check_card_fraud) for order {order_id}...") + event_results["event_e"] = trigger_fraud_check(order_id, "event_e") + vc = get_fraud_vector_clock(order_id) + print( + f"event_e result: {event_results['event_e'].is_fraud}, {event_results['event_e'].message}, VC: e={vc.fd_event_e}" + ) + if event_results["event_e"].is_fraud: + return True, event_results["event_e"].message + except Exception as e: + print(f"event_e failed: {e}") + return True, str(e) + return False, "" + + thread_e = threading.Thread(target=lambda: None) + + def run_e(): + nonlocal failed, failure_message + f, m = trigger_event_e() + if f: + failed = True + failure_message = m + + thread_e = threading.Thread(target=run_e) + thread_e.start() + thread_e.join() + + if failed: + print(f"Early failure detected in event e: {failure_message}") + vc_fraud = get_fraud_vector_clock(order_id) + vc_verif = get_verification_vector_clock(order_id) + final_vc = { + "fraud_detection": vc_fraud.fraud_detection, + "fd_event_d": vc_fraud.fd_event_d, + "fd_event_e": vc_fraud.fd_event_e, + "transaction_verification": vc_verif.transaction_verification, + "tv_event_a": vc_verif.tv_event_a, + "tv_event_b": vc_verif.tv_event_b, + "tv_event_c": vc_verif.tv_event_c, + "suggestions": 0, + } + print(f"Broadcasting clear order with final VC: {final_vc}") + broadcast_results = broadcast_clear_order(order_id, final_vc) + print(f"Broadcast results: {broadcast_results}") + return { + "orderId": order_id, + "status": "Order Rejected", + "suggestedBooks": [], + "failure": failure_message, + }, 200 + + print("Event e completed.") + + def trigger_event_f(): + try: + print(f"Triggering event_f (generate_suggestions) for order {order_id}...") + event_results["event_f"] = trigger_suggestions(order_id, "event_f") + print(f"event_f returned {len(event_results['event_f'].books)} books") + except Exception as e: + print(f"event_f failed: {e}") + + thread_f = threading.Thread(target=trigger_event_f) + thread_f.start() + thread_f.join() + print("Event f completed. All events finished.") + + results["fraud"] = event_results["event_e"] + results["verification"] = event_results["event_c"] + results["suggestions"] = event_results["event_f"] + + vc_fraud = get_fraud_vector_clock(order_id) + vc_verif = get_verification_vector_clock(order_id) + vc_suggestions = get_suggestions_vector_clock(order_id) + + final_vc = { + "fraud_detection": vc_fraud.fraud_detection, + "fd_event_d": vc_fraud.fd_event_d, + "fd_event_e": vc_fraud.fd_event_e, + "transaction_verification": vc_verif.transaction_verification, + "tv_event_a": vc_verif.tv_event_a, + "tv_event_b": vc_verif.tv_event_b, + "tv_event_c": vc_verif.tv_event_c, + "suggestions": vc_suggestions.suggestions, + } + print(f"Final vector clock: {final_vc}") + + print(f"Broadcasting clear order with final VC: {final_vc}") + broadcast_results = broadcast_clear_order(order_id, final_vc) + print(f"Broadcast results: {broadcast_results}") if results["errors"]: return { - "error": { - "code": "INTERNAL_ERROR", - "message": "; ".join(results["errors"]) - } + "error": {"code": "INTERNAL_ERROR", "message": "; ".join(results["errors"])} }, 500 if results["fraud"] and results["fraud"].is_fraud: return { - "orderId": "12345", + "orderId": order_id, "status": "Order Rejected", - "suggestedBooks": [] + "suggestedBooks": [], }, 200 if results["verification"] and not results["verification"].is_valid: return { - "orderId": "12345", + "orderId": order_id, "status": "Order Rejected", - "suggestedBooks": [] + "suggestedBooks": [], }, 200 suggested_books = [] if results["suggestions"]: for book in results["suggestions"].books: - suggested_books.append({ - "bookId": book.bookId, - "title": book.title, - "author": book.author - }) + suggested_books.append( + {"bookId": book.bookId, "title": book.title, "author": book.author} + ) return { - "orderId": "12345", + "orderId": order_id, "status": "Order Approved", - "suggestedBooks": suggested_books + "suggestedBooks": suggested_books, }, 200 + def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) + digits = "".join(c for c in str(card) if c.isdigit()) + masked = "*" * 12 + digits[-4:].rjust(4, "*") + return " ".join(masked[i : i + 4] for i in range(0, 16, 4)) + -if __name__ == '__main__': - app.run(host='0.0.0.0') \ No newline at end of file +if __name__ == "__main__": + app.run(host="0.0.0.0") diff --git a/suggestions/requirements.txt b/suggestions/requirements.txt index 43fdaf26f..8a7d109a8 100644 --- a/suggestions/requirements.txt +++ b/suggestions/requirements.txt @@ -1,4 +1,4 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 +protobuf==6.31.1 watchdog==6.0.0 \ No newline at end of file diff --git a/suggestions/src/app.py b/suggestions/src/app.py index 258c2fb0c..7674ab1d8 100644 --- a/suggestions/src/app.py +++ b/suggestions/src/app.py @@ -1,10 +1,11 @@ import sys import os +import threading from concurrent import futures -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") suggestions_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/suggestions') + os.path.join(FILE, "../../../utils/pb/suggestions") ) sys.path.insert(0, suggestions_grpc_path) @@ -12,36 +13,188 @@ import suggestions_pb2 as suggestions import suggestions_pb2_grpc as suggestions_grpc +order_cache = {} +vector_clocks = {} +cache_lock = threading.Lock() + + +def initialize_vector_clock(order_id): + return { + "fraud_detection": 0, + "transaction_verification": 0, + "suggestions": 0, + "tv_event_a": 0, + "tv_event_b": 0, + "tv_event_c": 0, + "fd_event_d": 0, + "fd_event_e": 0, + } + + +def check_event_dependencies(vc, required_events): + for event in required_events: + if vc.get(event, 0) == 0: + return False, f"Required event {event} not completed" + return True, "OK" + + +static_books = [ + {"bookId": "101", "title": "Distributed Systems Basics", "author": "A. Author"}, + { + "bookId": "102", + "title": "Designing Data-Intensive Applications", + "author": "Martin Kleppmann", + }, + {"bookId": "103", "title": "Clean Code", "author": "Robert C. Martin"}, + {"bookId": "104", "title": "The Pragmatic Programmer", "author": "Andrew Hunt"}, +] + + +def execute_event_f(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["suggestions"] += 1 + + item_count = order_data.get("item_count", 0) + chosen = static_books[:2] if item_count > 0 else [] + + print(f"Event f (generate_suggestions) for order {order_id}: {len(chosen)} books") + return {"books": chosen} + + +def process_order(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["suggestions"] += 1 + + chosen = static_books[:2] if order_data["item_count"] > 0 else [] + + result = {"books": chosen, "vector_clock": vector_clocks[order_id].copy()} + + print(f"Suggestions processed for order {order_id}: {len(chosen)} books") + return result + class SuggestionsService(suggestions_grpc.SuggestionsServiceServicer): def GetSuggestions(self, request, context): - print("Received suggestions request") - print("user_name:", request.user_name) - print("item_count:", request.item_count) + order_id = request.order_id if hasattr(request, "order_id") else "" + print(f"Received suggestions request for order: {order_id}") - static_books = [ - {"bookId": "101", "title": "Distributed Systems Basics", "author": "A. Author"}, - {"bookId": "102", "title": "Designing Data-Intensive Applications", "author": "Martin Kleppmann"}, - {"bookId": "103", "title": "Clean Code", "author": "Robert C. Martin"}, - {"bookId": "104", "title": "The Pragmatic Programmer", "author": "Andrew Hunt"}, - ] + with cache_lock: + order_cache[order_id] = { + "user_name": request.user_name, + "item_count": request.item_count, + } + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vector_clocks[order_id]["suggestions"] = 0 + + print( + f"Order {order_id} cached in suggestions. Vector clock: {vector_clocks.get(order_id)}" + ) response = suggestions.SuggestionsResponse() + return response + + def TriggerSuggestions(self, request, context): + order_id = request.order_id + event_type = request.event_type if hasattr(request, "event_type") else "all" + print(f"Triggering suggestions for order {order_id}, event: {event_type}") - if request.item_count > 0: - chosen = static_books[:2] - else: - chosen = [] + with cache_lock: + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vc = vector_clocks.get(order_id, {}) - for book in chosen: - b = response.books.add() - b.bookId = book["bookId"] - b.title = book["title"] - b.author = book["author"] + if event_type == "all": + result = process_order(order_id) + response = suggestions.SuggestionsResponse() + if result: + with cache_lock: + if order_id in order_cache: + del order_cache[order_id] + for book in result["books"]: + b = response.books.add() + b.bookId = book["bookId"] + b.title = book["title"] + b.author = book["author"] + print( + f"[VC] Suggestions for order {order_id}: VC={vector_clocks.get(order_id, {})}" + ) + return response + elif event_type == "event_f": + # Event F depends on Event E (check_card_fraud) + can_proceed, msg = check_event_dependencies(vc, ["fd_event_e"]) + if not can_proceed: + response = suggestions.SuggestionsResponse() + response.failed = True + return response + result = execute_event_f(order_id) + response = suggestions.SuggestionsResponse() + if result: + with cache_lock: + if order_id in order_cache: + del order_cache[order_id] + for book in result["books"]: + b = response.books.add() + b.bookId = book["bookId"] + b.title = book["title"] + b.author = book["author"] + print( + f"[VC] Event f for order {order_id}: VC suggestions={vector_clocks.get(order_id, {}).get('suggestions', 0)}" + ) + return response - print("Returning", len(response.books), "suggested books") + def GetVectorClock(self, request, context): + order_id = request.order_id + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + response = suggestions.VectorClockResponse() + response.suggestions = vc.get("suggestions", 0) + response.fraud_detection = vc.get("fraud_detection", 0) + response.fd_event_d = vc.get("fd_event_d", 0) + response.fd_event_e = vc.get("fd_event_e", 0) + response.transaction_verification = vc.get("transaction_verification", 0) + response.tv_event_a = vc.get("tv_event_a", 0) + response.tv_event_b = vc.get("tv_event_b", 0) + response.tv_event_c = vc.get("tv_event_c", 0) return response + def ClearOrder(self, request, context): + order_id = request.order_id + print(f"Received ClearOrder request for order: {order_id}") + + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + local_vc_s = vc.get("suggestions", 0) + + expected_vc_s = request.final_vc_suggestions + + if local_vc_s <= expected_vc_s: + if order_id in order_cache: + del order_cache[order_id] + if order_id in vector_clocks: + del vector_clocks[order_id] + print(f"Order {order_id} cleared successfully in suggestions") + response = suggestions.ClearOrderResponse() + response.success = True + response.message = "Order cleared successfully" + return response + else: + print(f"Vector clock mismatch for order {order_id}. Local VC: {vc}") + response = suggestions.ClearOrderResponse() + response.success = False + response.message = f"Vector clock mismatch. Local: s={local_vc_s}. Expected: s={expected_vc_s}" + return response + def serve(): server = grpc.server(futures.ThreadPoolExecutor()) @@ -56,5 +209,5 @@ def serve(): server.wait_for_termination() -if __name__ == '__main__': - serve() \ No newline at end of file +if __name__ == "__main__": + serve() diff --git a/transaction_verification/requirements.txt b/transaction_verification/requirements.txt index 43fdaf26f..8a7d109a8 100644 --- a/transaction_verification/requirements.txt +++ b/transaction_verification/requirements.txt @@ -1,4 +1,4 @@ -grpcio==1.60.0 -grpcio-tools==1.60.0 -protobuf==4.25.2 +grpcio==1.78.0 +grpcio-tools==1.78.0 +protobuf==6.31.1 watchdog==6.0.0 \ No newline at end of file diff --git a/transaction_verification/src/app.py b/transaction_verification/src/app.py index b1087e51f..4940d0d45 100644 --- a/transaction_verification/src/app.py +++ b/transaction_verification/src/app.py @@ -1,10 +1,12 @@ import sys import os +import threading +import time from concurrent import futures -FILE = __file__ if '__file__' in globals() else os.getenv("PYTHONFILE", "") +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") transaction_verification_grpc_path = os.path.abspath( - os.path.join(FILE, '../../../utils/pb/transaction_verification') + os.path.join(FILE, "../../../utils/pb/transaction_verification") ) sys.path.insert(0, transaction_verification_grpc_path) @@ -12,58 +14,311 @@ import transaction_verification_pb2 as transaction_verification import transaction_verification_pb2_grpc as transaction_verification_grpc +order_cache = {} +vector_clocks = {} +cache_lock = threading.Lock() + +EVENTS = { + "a": "verify_items_not_empty", + "b": "verify_user_data", + "c": "verify_card_format", +} + + +def initialize_vector_clock(order_id): + return { + "fraud_detection": 0, + "transaction_verification": 0, + "suggestions": 0, + "tv_event_a": 0, + "tv_event_b": 0, + "tv_event_c": 0, + } + + +def check_event_dependencies(vc, required_events): + for event in required_events: + if vc.get(event, 0) == 0: + return False, f"Required event {event} not completed" + return True, "OK" + + +def execute_event_a(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["tv_event_a"] += 1 + + item_count = order_data["item_count"] + is_valid = item_count > 0 + message = "Items verified: not empty" if is_valid else "No items in order." + + print(f"Event a (verify_items_not_empty) for order {order_id}: {is_valid}") + return {"is_valid": is_valid, "message": message} + + +def execute_event_b(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["tv_event_b"] += 1 + + user_name = order_data.get("user_name", "") + user_contact = order_data.get("user_contact", "") + is_valid = bool(user_name and user_contact) + message = "User data verified" if is_valid else "Missing user name or contact." + + print(f"Event b (verify_user_data) for order {order_id}: {is_valid}") + return {"is_valid": is_valid, "message": message} + + +def execute_event_c(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["tv_event_c"] += 1 + + card_number = order_data.get("card_number", "") + expiration_date = order_data.get("expiration_date", "") + cvv = order_data.get("cvv", "") + card_digits = extract_card_digits(card_number) + + has_card = bool(card_number and expiration_date and cvv) + correct_format = len(card_digits) == 16 + is_valid = has_card and correct_format + + if not has_card: + message = "Missing credit card information." + elif not correct_format: + message = "Invalid card number format." + else: + message = "Credit card format verified." + + print(f"Event c (verify_card_format) for order {order_id}: {is_valid}") + return {"is_valid": is_valid, "message": message} + + +def process_order_sequential(order_id): + order_data = order_cache.get(order_id) + if not order_data: + return None + + with cache_lock: + vector_clocks[order_id]["transaction_verification"] += 1 + + card_digits = extract_card_digits(order_data["card_number"]) + is_valid = True + message = "Transaction is valid." + + if not order_data["user_name"]: + is_valid = False + message = "Missing user name." + elif not order_data["user_contact"]: + is_valid = False + message = "Missing user contact." + elif order_data["item_count"] <= 0: + is_valid = False + message = "No items in order." + elif not order_data["terms_accepted"]: + is_valid = False + message = "Terms and conditions not accepted." + elif ( + not order_data["card_number"] + or not order_data["expiration_date"] + or not order_data["cvv"] + ): + is_valid = False + message = "Missing credit card information." + elif len(card_digits) != 16: + is_valid = False + message = "Invalid card number." + + result = { + "is_valid": is_valid, + "message": message, + "vector_clock": vector_clocks[order_id].copy(), + } + + print( + f"Transaction verification processed for order {order_id}: {is_valid}, {message}" + ) + return result + class TransactionVerificationService( transaction_verification_grpc.TransactionVerificationServiceServicer ): def VerifyTransaction(self, request, context): - print("Received transaction verification request") - print("user_name:", request.user_name) - print("user_contact:", request.user_contact) - masked_card_number = mask_fixed(request.card_number) - print("card_number:", masked_card_number) - print("item_count:", request.item_count) - print("terms_accepted:", request.terms_accepted) - # Compute length based on digits only to avoid counting spaces or other characters - card_digits = extract_card_digits(request.card_number) - print("card length (digits only):", len(card_digits)) - is_valid = True - message = "Transaction is valid." - - if not request.user_name: - is_valid = False - message = "Missing user name." - elif not request.user_contact: - is_valid = False - message = "Missing user contact." - elif request.item_count <= 0: - is_valid = False - message = "No items in order." - elif not request.terms_accepted: - is_valid = False - message = "Terms and conditions not accepted." - elif not request.card_number or not request.expiration_date or not request.cvv: - is_valid = False - message = "Missing credit card information." - - # Treat any non-16-digit card number as invalid - elif len(card_digits) != 16: - is_valid = False - message = "Invalid card number." + order_id = request.order_id if hasattr(request, "order_id") else "" + print(f"Received transaction verification request for order: {order_id}") + + with cache_lock: + order_cache[order_id] = { + "user_name": request.user_name, + "user_contact": request.user_contact, + "card_number": request.card_number, + "expiration_date": request.expiration_date, + "cvv": request.cvv, + "item_count": request.item_count, + "terms_accepted": request.terms_accepted, + } + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vector_clocks[order_id]["transaction_verification"] = 0 + + print( + f"Order {order_id} cached in transaction_verification. Vector clock: {vector_clocks.get(order_id)}" + ) response = transaction_verification.TransactionVerificationResponse() - response.is_valid = is_valid - response.message = message + response.is_valid = True + response.message = "Order cached. Waiting for execution trigger." - print("Returning verification result:", response.is_valid, response.message) return response + def TriggerVerification(self, request, context): + order_id = request.order_id + event_type = request.event_type if hasattr(request, "event_type") else "all" + print( + f"Triggering transaction verification for order {order_id}, event: {event_type}" + ) + + with cache_lock: + if order_id not in vector_clocks: + vector_clocks[order_id] = initialize_vector_clock(order_id) + vc = vector_clocks.get(order_id, {}) + + results = {} + + if event_type == "all": + result = process_order_sequential(order_id) + if result: + with cache_lock: + if order_id in order_cache: + del order_cache[order_id] + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = result["is_valid"] + response.message = result["message"] + response.failed = not result["is_valid"] + print( + f"[VC] Transaction verification for order {order_id}: VC={vector_clocks.get(order_id, {})}" + ) + return response + elif event_type == "event_a": + result = execute_event_a(order_id) + if result: + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = result["is_valid"] + response.message = result["message"] + response.failed = not result["is_valid"] + print( + f"[VC] Event a for order {order_id}: VC tv_event_a={vector_clocks.get(order_id, {}).get('tv_event_a', 0)}" + ) + return response + elif event_type == "event_b": + # Event B (verify_user_data) can proceed without dependencies usually + result = execute_event_b(order_id) + if result: + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = result["is_valid"] + response.message = result["message"] + response.failed = not result["is_valid"] + print( + f"[VC] Event b for order {order_id}: VC tv_event_b={vector_clocks.get(order_id, {}).get('tv_event_b', 0)}" + ) + return response + elif event_type == "event_c": + # Event C (verify_credit_card) depends on Event A (verify_items_not_empty) + can_proceed, msg = check_event_dependencies(vc, ["tv_event_a"]) + if not can_proceed: + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = False + response.message = f"Cannot execute event_c: {msg}" + response.failed = True + return response + result = execute_event_c(order_id) + if result: + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = result["is_valid"] + response.message = result["message"] + response.failed = not result["is_valid"] + print( + f"[VC] Event c for order {order_id}: VC tv_event_c={vector_clocks.get(order_id, {}).get('tv_event_c', 0)}" + ) + return response + + response = transaction_verification.TransactionVerificationResponse() + response.is_valid = False + response.message = "Order not found in cache or invalid event type." + return response + + def GetVectorClock(self, request, context): + order_id = request.order_id + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + response = transaction_verification.VectorClockResponse() + response.transaction_verification = vc.get("transaction_verification", 0) + response.tv_event_a = vc.get("tv_event_a", 0) + response.tv_event_b = vc.get("tv_event_b", 0) + response.tv_event_c = vc.get("tv_event_c", 0) + response.fraud_detection = vc.get("fraud_detection", 0) + response.suggestions = vc.get("suggestions", 0) + return response + + def ClearOrder(self, request, context): + order_id = request.order_id + print(f"Received ClearOrder request for order: {order_id}") + + with cache_lock: + vc = vector_clocks.get(order_id, {}) + + local_vc_tv = vc.get("transaction_verification", 0) + local_vc_a = vc.get("tv_event_a", 0) + local_vc_b = vc.get("tv_event_b", 0) + local_vc_c = vc.get("tv_event_c", 0) + + expected_vc_tv = request.final_vc_transaction_verification + expected_vc_a = request.final_vc_tv_event_a + expected_vc_b = request.final_vc_tv_event_b + expected_vc_c = request.final_vc_tv_event_c + + if ( + local_vc_tv <= expected_vc_tv + and local_vc_a <= expected_vc_a + and local_vc_b <= expected_vc_b + and local_vc_c <= expected_vc_c + ): + if order_id in order_cache: + del order_cache[order_id] + if order_id in vector_clocks: + del vector_clocks[order_id] + print( + f"Order {order_id} cleared successfully in transaction_verification" + ) + response = transaction_verification.ClearOrderResponse() + response.success = True + response.message = "Order cleared successfully" + return response + else: + print(f"Vector clock mismatch for order {order_id}. Local VC: {vc}") + response = transaction_verification.ClearOrderResponse() + response.success = False + response.message = f"Vector clock mismatch. Local: tv={local_vc_tv}, a={local_vc_a}, b={local_vc_b}, c={local_vc_c}. Expected: tv={expected_vc_tv}, a={expected_vc_a}, b={expected_vc_b}, c={expected_vc_c}" + return response + def extract_card_digits(card: str) -> str: """ Return only the digit characters from the given card number. """ - return ''.join(c for c in str(card) if c.isdigit()) + return "".join(c for c in str(card) if c.isdigit()) def serve(): @@ -78,10 +333,12 @@ def serve(): print("Transaction verification server started. Listening on port 50052.") server.wait_for_termination() + def mask_fixed(card: str) -> str: - digits = ''.join(c for c in str(card) if c.isdigit()) - masked = '*' * 12 + digits[-4:].rjust(4, '*') - return ' '.join(masked[i:i+4] for i in range(0, 16, 4)) + digits = "".join(c for c in str(card) if c.isdigit()) + masked = "*" * 12 + digits[-4:].rjust(4, "*") + return " ".join(masked[i : i + 4] for i in range(0, 16, 4)) + -if __name__ == '__main__': - serve() \ No newline at end of file +if __name__ == "__main__": + serve() diff --git a/utils/pb/fraud_detection/fraud_detection.proto b/utils/pb/fraud_detection/fraud_detection.proto index d575366b1..094c37808 100644 --- a/utils/pb/fraud_detection/fraud_detection.proto +++ b/utils/pb/fraud_detection/fraud_detection.proto @@ -1,27 +1,61 @@ syntax = "proto3"; -package hello; +package fraud_detection; + +service FraudDetectionService { -service HelloService { - rpc SayHello (HelloRequest) returns (HelloResponse); rpc CheckFraud (FraudCheckRequest) returns (FraudCheckResponse); + rpc TriggerFraudCheck (TriggerRequest) returns (FraudCheckResponse); + rpc GetVectorClock (VectorClockRequest) returns (VectorClockResponse); + rpc ClearOrder (ClearOrderRequest) returns (ClearOrderResponse); } -message HelloRequest { - string name = 1; +message FraudCheckRequest { + string order_id = 1; + string user_name = 2; + string card_number = 3; + int32 item_count = 4; } -message HelloResponse { - string greeting = 1; +message FraudCheckResponse { + bool is_fraud = 1; + string message = 2; + bool failed = 3; } -message FraudCheckRequest { - string user_name = 1; - string card_number = 2; - int32 item_count = 3; +message TriggerRequest { + string order_id = 1; + string event_type = 2; } -message FraudCheckResponse { - bool is_fraud = 1; +message VectorClockRequest { + string order_id = 1; +} + +message VectorClockResponse { + int32 fraud_detection = 1; + int32 fd_event_d = 2; + int32 fd_event_e = 3; + int32 transaction_verification = 4; + int32 tv_event_a = 5; + int32 tv_event_b = 6; + int32 tv_event_c = 7; + int32 suggestions = 8; +} + +message ClearOrderRequest { + string order_id = 1; + int32 final_vc_fraud_detection = 2; + int32 final_vc_fd_event_d = 3; + int32 final_vc_fd_event_e = 4; + int32 final_vc_transaction_verification = 5; + int32 final_vc_tv_event_a = 6; + int32 final_vc_tv_event_b = 7; + int32 final_vc_tv_event_c = 8; + int32 final_vc_suggestions = 9; +} + +message ClearOrderResponse { + bool success = 1; string message = 2; } \ No newline at end of file diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.py b/utils/pb/fraud_detection/fraud_detection_pb2.py index d405d9cf5..9d993cb43 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: fraud_detection.proto -# Protobuf Python Version: 4.25.0 +# NO CHECKED-IN PROTOBUF GENCODE +# source: utils/pb/fraud_detection/fraud_detection.proto +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'utils/pb/fraud_detection/fraud_detection.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,21 +24,27 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66raud_detection.proto\x12\x05hello\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"!\n\rHelloResponse\x12\x10\n\x08greeting\x18\x01 \x01(\t\"O\n\x11\x46raudCheckRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x02 \x01(\t\x12\x12\n\nitem_count\x18\x03 \x01(\x05\"7\n\x12\x46raudCheckResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\x88\x01\n\x0cHelloService\x12\x35\n\x08SayHello\x12\x13.hello.HelloRequest\x1a\x14.hello.HelloResponse\x12\x41\n\nCheckFraud\x12\x18.hello.FraudCheckRequest\x1a\x19.hello.FraudCheckResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n.utils/pb/fraud_detection/fraud_detection.proto\x12\x0f\x66raud_detection\"a\n\x11\x46raudCheckRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x03 \x01(\t\x12\x12\n\nitem_count\x18\x04 \x01(\x05\"G\n\x12\x46raudCheckResponse\x12\x10\n\x08is_fraud\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0e\n\x06\x66\x61iled\x18\x03 \x01(\x08\"6\n\x0eTriggerRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x12\n\nevent_type\x18\x02 \x01(\t\"&\n\x12VectorClockRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\"\xc9\x01\n\x13VectorClockResponse\x12\x17\n\x0f\x66raud_detection\x18\x01 \x01(\x05\x12\x12\n\nfd_event_d\x18\x02 \x01(\x05\x12\x12\n\nfd_event_e\x18\x03 \x01(\x05\x12 \n\x18transaction_verification\x18\x04 \x01(\x05\x12\x12\n\ntv_event_a\x18\x05 \x01(\x05\x12\x12\n\ntv_event_b\x18\x06 \x01(\x05\x12\x12\n\ntv_event_c\x18\x07 \x01(\x05\x12\x13\n\x0bsuggestions\x18\x08 \x01(\x05\"\xa1\x02\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12 \n\x18\x66inal_vc_fraud_detection\x18\x02 \x01(\x05\x12\x1b\n\x13\x66inal_vc_fd_event_d\x18\x03 \x01(\x05\x12\x1b\n\x13\x66inal_vc_fd_event_e\x18\x04 \x01(\x05\x12)\n!final_vc_transaction_verification\x18\x05 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_a\x18\x06 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_b\x18\x07 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_c\x18\x08 \x01(\x05\x12\x1c\n\x14\x66inal_vc_suggestions\x18\t \x01(\x05\"6\n\x12\x43learOrderResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xfd\x02\n\x15\x46raudDetectionService\x12U\n\nCheckFraud\x12\".fraud_detection.FraudCheckRequest\x1a#.fraud_detection.FraudCheckResponse\x12Y\n\x11TriggerFraudCheck\x12\x1f.fraud_detection.TriggerRequest\x1a#.fraud_detection.FraudCheckResponse\x12[\n\x0eGetVectorClock\x12#.fraud_detection.VectorClockRequest\x1a$.fraud_detection.VectorClockResponse\x12U\n\nClearOrder\x12\".fraud_detection.ClearOrderRequest\x1a#.fraud_detection.ClearOrderResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'fraud_detection_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_HELLOREQUEST']._serialized_start=32 - _globals['_HELLOREQUEST']._serialized_end=60 - _globals['_HELLORESPONSE']._serialized_start=62 - _globals['_HELLORESPONSE']._serialized_end=95 - _globals['_FRAUDCHECKREQUEST']._serialized_start=97 - _globals['_FRAUDCHECKREQUEST']._serialized_end=176 - _globals['_FRAUDCHECKRESPONSE']._serialized_start=178 - _globals['_FRAUDCHECKRESPONSE']._serialized_end=233 - _globals['_HELLOSERVICE']._serialized_start=236 - _globals['_HELLOSERVICE']._serialized_end=372 +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'utils.pb.fraud_detection.fraud_detection_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_FRAUDCHECKREQUEST']._serialized_start=67 + _globals['_FRAUDCHECKREQUEST']._serialized_end=164 + _globals['_FRAUDCHECKRESPONSE']._serialized_start=166 + _globals['_FRAUDCHECKRESPONSE']._serialized_end=237 + _globals['_TRIGGERREQUEST']._serialized_start=239 + _globals['_TRIGGERREQUEST']._serialized_end=293 + _globals['_VECTORCLOCKREQUEST']._serialized_start=295 + _globals['_VECTORCLOCKREQUEST']._serialized_end=333 + _globals['_VECTORCLOCKRESPONSE']._serialized_start=336 + _globals['_VECTORCLOCKRESPONSE']._serialized_end=537 + _globals['_CLEARORDERREQUEST']._serialized_start=540 + _globals['_CLEARORDERREQUEST']._serialized_end=829 + _globals['_CLEARORDERRESPONSE']._serialized_start=831 + _globals['_CLEARORDERRESPONSE']._serialized_end=885 + _globals['_FRAUDDETECTIONSERVICE']._serialized_start=888 + _globals['_FRAUDDETECTIONSERVICE']._serialized_end=1269 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/fraud_detection/fraud_detection_pb2.pyi b/utils/pb/fraud_detection/fraud_detection_pb2.pyi index fe3863b06..072034151 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2.pyi +++ b/utils/pb/fraud_detection/fraud_detection_pb2.pyi @@ -17,19 +17,87 @@ class HelloResponse(_message.Message): def __init__(self, greeting: _Optional[str] = ...) -> None: ... class FraudCheckRequest(_message.Message): - __slots__ = ("user_name", "card_number", "item_count") + __slots__ = ("order_id", "user_name", "card_number", "item_count") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] USER_NAME_FIELD_NUMBER: _ClassVar[int] CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + order_id: str user_name: str card_number: str item_count: int - def __init__(self, user_name: _Optional[str] = ..., card_number: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... + def __init__(self, order_id: _Optional[str] = ..., user_name: _Optional[str] = ..., card_number: _Optional[str] = ..., item_count: _Optional[int] = ...) -> None: ... class FraudCheckResponse(_message.Message): - __slots__ = ("is_fraud", "message") + __slots__ = ("is_fraud", "message", "failed") IS_FRAUD_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] + FAILED_FIELD_NUMBER: _ClassVar[int] is_fraud: bool message: str - def __init__(self, is_fraud: bool = ..., message: _Optional[str] = ...) -> None: ... + failed: bool + def __init__(self, is_fraud: bool = ..., message: _Optional[str] = ..., failed: bool = ...) -> None: ... + +class TriggerRequest(_message.Message): + __slots__ = ("order_id", "event_type") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + EVENT_TYPE_FIELD_NUMBER: _ClassVar[int] + order_id: str + event_type: str + def __init__(self, order_id: _Optional[str] = ..., event_type: _Optional[str] = ...) -> None: ... + +class VectorClockRequest(_message.Message): + __slots__ = ("order_id",) + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + order_id: str + def __init__(self, order_id: _Optional[str] = ...) -> None: ... + +class VectorClockResponse(_message.Message): + __slots__ = ("fraud_detection", "fd_event_d", "fd_event_e", "transaction_verification", "tv_event_a", "tv_event_b", "tv_event_c", "suggestions") + FRAUD_DETECTION_FIELD_NUMBER: _ClassVar[int] + FD_EVENT_D_FIELD_NUMBER: _ClassVar[int] + FD_EVENT_E_FIELD_NUMBER: _ClassVar[int] + TRANSACTION_VERIFICATION_FIELD_NUMBER: _ClassVar[int] + TV_EVENT_A_FIELD_NUMBER: _ClassVar[int] + TV_EVENT_B_FIELD_NUMBER: _ClassVar[int] + TV_EVENT_C_FIELD_NUMBER: _ClassVar[int] + SUGGESTIONS_FIELD_NUMBER: _ClassVar[int] + fraud_detection: int + fd_event_d: int + fd_event_e: int + transaction_verification: int + tv_event_a: int + tv_event_b: int + tv_event_c: int + suggestions: int + def __init__(self, fraud_detection: _Optional[int] = ..., fd_event_d: _Optional[int] = ..., fd_event_e: _Optional[int] = ..., transaction_verification: _Optional[int] = ..., tv_event_a: _Optional[int] = ..., tv_event_b: _Optional[int] = ..., tv_event_c: _Optional[int] = ..., suggestions: _Optional[int] = ...) -> None: ... + +class ClearOrderRequest(_message.Message): + __slots__ = ("order_id", "final_vc_fraud_detection", "final_vc_fd_event_d", "final_vc_fd_event_e", "final_vc_transaction_verification", "final_vc_tv_event_a", "final_vc_tv_event_b", "final_vc_tv_event_c", "final_vc_suggestions") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FRAUD_DETECTION_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FD_EVENT_D_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_FD_EVENT_E_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_TRANSACTION_VERIFICATION_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_TV_EVENT_A_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_TV_EVENT_B_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_TV_EVENT_C_FIELD_NUMBER: _ClassVar[int] + FINAL_VC_SUGGESTIONS_FIELD_NUMBER: _ClassVar[int] + order_id: str + final_vc_fraud_detection: int + final_vc_fd_event_d: int + final_vc_fd_event_e: int + final_vc_transaction_verification: int + final_vc_tv_event_a: int + final_vc_tv_event_b: int + final_vc_tv_event_c: int + final_vc_suggestions: int + def __init__(self, order_id: _Optional[str] = ..., final_vc_fraud_detection: _Optional[int] = ..., final_vc_fd_event_d: _Optional[int] = ..., final_vc_fd_event_e: _Optional[int] = ..., final_vc_transaction_verification: _Optional[int] = ..., final_vc_tv_event_a: _Optional[int] = ..., final_vc_tv_event_b: _Optional[int] = ..., final_vc_tv_event_c: _Optional[int] = ..., final_vc_suggestions: _Optional[int] = ...) -> None: ... + +class ClearOrderResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... diff --git a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py index 3ada2e86e..6039c712f 100644 --- a/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py +++ b/utils/pb/fraud_detection/fraud_detection_pb2_grpc.py @@ -1,11 +1,31 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings -import fraud_detection_pb2 as fraud__detection__pb2 +from utils.pb.fraud_detection import fraud_detection_pb2 as utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2 +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False -class HelloServiceStub(object): +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in utils/pb/fraud_detection/fraud_detection_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class FraudDetectionServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,58 +34,91 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.SayHello = channel.unary_unary( - '/hello.HelloService/SayHello', - request_serializer=fraud__detection__pb2.HelloRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.HelloResponse.FromString, - ) self.CheckFraud = channel.unary_unary( - '/hello.HelloService/CheckFraud', - request_serializer=fraud__detection__pb2.FraudCheckRequest.SerializeToString, - response_deserializer=fraud__detection__pb2.FraudCheckResponse.FromString, - ) + '/fraud_detection.FraudDetectionService/CheckFraud', + request_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.FromString, + _registered_method=True) + self.TriggerFraudCheck = channel.unary_unary( + '/fraud_detection.FraudDetectionService/TriggerFraudCheck', + request_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.TriggerRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.FromString, + _registered_method=True) + self.GetVectorClock = channel.unary_unary( + '/fraud_detection.FraudDetectionService/GetVectorClock', + request_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/fraud_detection.FraudDetectionService/ClearOrder', + request_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderResponse.FromString, + _registered_method=True) -class HelloServiceServicer(object): +class FraudDetectionServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def SayHello(self, request, context): + def CheckFraud(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def CheckFraud(self, request, context): + def TriggerFraudCheck(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetVectorClock(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClearOrder(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') -def add_HelloServiceServicer_to_server(servicer, server): +def add_FraudDetectionServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'SayHello': grpc.unary_unary_rpc_method_handler( - servicer.SayHello, - request_deserializer=fraud__detection__pb2.HelloRequest.FromString, - response_serializer=fraud__detection__pb2.HelloResponse.SerializeToString, - ), 'CheckFraud': grpc.unary_unary_rpc_method_handler( servicer.CheckFraud, - request_deserializer=fraud__detection__pb2.FraudCheckRequest.FromString, - response_serializer=fraud__detection__pb2.FraudCheckResponse.SerializeToString, + request_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckRequest.FromString, + response_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.SerializeToString, + ), + 'TriggerFraudCheck': grpc.unary_unary_rpc_method_handler( + servicer.TriggerFraudCheck, + request_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.TriggerRequest.FromString, + response_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.SerializeToString, + ), + 'GetVectorClock': grpc.unary_unary_rpc_method_handler( + servicer.GetVectorClock, + request_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockRequest.FromString, + response_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderRequest.FromString, + response_serializer=utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'hello.HelloService', rpc_method_handlers) + 'fraud_detection.FraudDetectionService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('fraud_detection.FraudDetectionService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. -class HelloService(object): +class FraudDetectionService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def SayHello(request, + def CheckFraud(request, target, options=(), channel_credentials=None, @@ -75,14 +128,24 @@ def SayHello(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/hello.HelloService/SayHello', - fraud__detection__pb2.HelloRequest.SerializeToString, - fraud__detection__pb2.HelloResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/CheckFraud', + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckRequest.SerializeToString, + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod - def CheckFraud(request, + def TriggerFraudCheck(request, target, options=(), channel_credentials=None, @@ -92,8 +155,72 @@ def CheckFraud(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/hello.HelloService/CheckFraud', - fraud__detection__pb2.FraudCheckRequest.SerializeToString, - fraud__detection__pb2.FraudCheckResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/TriggerFraudCheck', + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.TriggerRequest.SerializeToString, + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.FraudCheckResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetVectorClock(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/GetVectorClock', + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockRequest.SerializeToString, + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.VectorClockResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/fraud_detection.FraudDetectionService/ClearOrder', + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderRequest.SerializeToString, + utils_dot_pb_dot_fraud__detection_dot_fraud__detection__pb2.ClearOrderResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/utils/pb/suggestions/suggestions.proto b/utils/pb/suggestions/suggestions.proto index 12f15f4cb..48f19da10 100644 --- a/utils/pb/suggestions/suggestions.proto +++ b/utils/pb/suggestions/suggestions.proto @@ -4,11 +4,15 @@ package suggestions; service SuggestionsService { rpc GetSuggestions (SuggestionsRequest) returns (SuggestionsResponse); + rpc TriggerSuggestions (TriggerRequest) returns (SuggestionsResponse); + rpc GetVectorClock (VectorClockRequest) returns (VectorClockResponse); + rpc ClearOrder (ClearOrderRequest) returns (ClearOrderResponse); } message SuggestionsRequest { - string user_name = 1; - int32 item_count = 2; + string order_id = 1; + string user_name = 2; + int32 item_count = 3; } message SuggestedBook { @@ -19,4 +23,42 @@ message SuggestedBook { message SuggestionsResponse { repeated SuggestedBook books = 1; + bool failed = 2; +} + +message TriggerRequest { + string order_id = 1; + string event_type = 2; +} + +message VectorClockRequest { + string order_id = 1; +} + +message VectorClockResponse { + int32 suggestions = 1; + int32 fraud_detection = 2; + int32 fd_event_d = 3; + int32 fd_event_e = 4; + int32 transaction_verification = 5; + int32 tv_event_a = 6; + int32 tv_event_b = 7; + int32 tv_event_c = 8; +} + +message ClearOrderRequest { + string order_id = 1; + int32 final_vc_suggestions = 2; + int32 final_vc_fraud_detection = 3; + int32 final_vc_fd_event_d = 4; + int32 final_vc_fd_event_e = 5; + int32 final_vc_transaction_verification = 6; + int32 final_vc_tv_event_a = 7; + int32 final_vc_tv_event_b = 8; + int32 final_vc_tv_event_c = 9; +} + +message ClearOrderResponse { + bool success = 1; + string message = 2; } \ No newline at end of file diff --git a/utils/pb/suggestions/suggestions_pb2.py b/utils/pb/suggestions/suggestions_pb2.py index 550a4d48e..722953e6a 100644 --- a/utils/pb/suggestions/suggestions_pb2.py +++ b/utils/pb/suggestions/suggestions_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: suggestions.proto -# Protobuf Python Version: 4.25.0 +# NO CHECKED-IN PROTOBUF GENCODE +# source: utils/pb/suggestions/suggestions.proto +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'utils/pb/suggestions/suggestions.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,19 +24,29 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11suggestions.proto\x12\x0bsuggestions\";\n\x12SuggestionsRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x12\n\nitem_count\x18\x02 \x01(\x05\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"@\n\x13SuggestionsResponse\x12)\n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x1a.suggestions.SuggestedBook2i\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&utils/pb/suggestions/suggestions.proto\x12\x0bsuggestions\"M\n\x12SuggestionsRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x12\n\nitem_count\x18\x03 \x01(\x05\">\n\rSuggestedBook\x12\x0e\n\x06\x62ookId\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x03 \x01(\t\"P\n\x13SuggestionsResponse\x12)\n\x05\x62ooks\x18\x01 \x03(\x0b\x32\x1a.suggestions.SuggestedBook\x12\x0e\n\x06\x66\x61iled\x18\x02 \x01(\x08\"6\n\x0eTriggerRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x12\n\nevent_type\x18\x02 \x01(\t\"&\n\x12VectorClockRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\"\xc9\x01\n\x13VectorClockResponse\x12\x13\n\x0bsuggestions\x18\x01 \x01(\x05\x12\x17\n\x0f\x66raud_detection\x18\x02 \x01(\x05\x12\x12\n\nfd_event_d\x18\x03 \x01(\x05\x12\x12\n\nfd_event_e\x18\x04 \x01(\x05\x12 \n\x18transaction_verification\x18\x05 \x01(\x05\x12\x12\n\ntv_event_a\x18\x06 \x01(\x05\x12\x12\n\ntv_event_b\x18\x07 \x01(\x05\x12\x12\n\ntv_event_c\x18\x08 \x01(\x05\"\xa1\x02\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x1c\n\x14\x66inal_vc_suggestions\x18\x02 \x01(\x05\x12 \n\x18\x66inal_vc_fraud_detection\x18\x03 \x01(\x05\x12\x1b\n\x13\x66inal_vc_fd_event_d\x18\x04 \x01(\x05\x12\x1b\n\x13\x66inal_vc_fd_event_e\x18\x05 \x01(\x05\x12)\n!final_vc_transaction_verification\x18\x06 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_a\x18\x07 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_b\x18\x08 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_c\x18\t \x01(\x05\"6\n\x12\x43learOrderResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xe2\x02\n\x12SuggestionsService\x12S\n\x0eGetSuggestions\x12\x1f.suggestions.SuggestionsRequest\x1a .suggestions.SuggestionsResponse\x12S\n\x12TriggerSuggestions\x12\x1b.suggestions.TriggerRequest\x1a .suggestions.SuggestionsResponse\x12S\n\x0eGetVectorClock\x12\x1f.suggestions.VectorClockRequest\x1a .suggestions.VectorClockResponse\x12M\n\nClearOrder\x12\x1e.suggestions.ClearOrderRequest\x1a\x1f.suggestions.ClearOrderResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'suggestions_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_SUGGESTIONSREQUEST']._serialized_start=34 - _globals['_SUGGESTIONSREQUEST']._serialized_end=93 - _globals['_SUGGESTEDBOOK']._serialized_start=95 - _globals['_SUGGESTEDBOOK']._serialized_end=157 - _globals['_SUGGESTIONSRESPONSE']._serialized_start=159 - _globals['_SUGGESTIONSRESPONSE']._serialized_end=223 - _globals['_SUGGESTIONSSERVICE']._serialized_start=225 - _globals['_SUGGESTIONSSERVICE']._serialized_end=330 +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'utils.pb.suggestions.suggestions_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_SUGGESTIONSREQUEST']._serialized_start=55 + _globals['_SUGGESTIONSREQUEST']._serialized_end=132 + _globals['_SUGGESTEDBOOK']._serialized_start=134 + _globals['_SUGGESTEDBOOK']._serialized_end=196 + _globals['_SUGGESTIONSRESPONSE']._serialized_start=198 + _globals['_SUGGESTIONSRESPONSE']._serialized_end=278 + _globals['_TRIGGERREQUEST']._serialized_start=280 + _globals['_TRIGGERREQUEST']._serialized_end=334 + _globals['_VECTORCLOCKREQUEST']._serialized_start=336 + _globals['_VECTORCLOCKREQUEST']._serialized_end=374 + _globals['_VECTORCLOCKRESPONSE']._serialized_start=377 + _globals['_VECTORCLOCKRESPONSE']._serialized_end=578 + _globals['_CLEARORDERREQUEST']._serialized_start=581 + _globals['_CLEARORDERREQUEST']._serialized_end=870 + _globals['_CLEARORDERRESPONSE']._serialized_start=872 + _globals['_CLEARORDERRESPONSE']._serialized_end=926 + _globals['_SUGGESTIONSSERVICE']._serialized_start=929 + _globals['_SUGGESTIONSSERVICE']._serialized_end=1283 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/suggestions/suggestions_pb2_grpc.py b/utils/pb/suggestions/suggestions_pb2_grpc.py index ca1154a0b..c2de54c5e 100644 --- a/utils/pb/suggestions/suggestions_pb2_grpc.py +++ b/utils/pb/suggestions/suggestions_pb2_grpc.py @@ -1,8 +1,28 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings -import suggestions_pb2 as suggestions__pb2 +from utils.pb.suggestions import suggestions_pb2 as utils_dot_pb_dot_suggestions_dot_suggestions__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in utils/pb/suggestions/suggestions_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) class SuggestionsServiceStub(object): @@ -16,9 +36,24 @@ def __init__(self, channel): """ self.GetSuggestions = channel.unary_unary( '/suggestions.SuggestionsService/GetSuggestions', - request_serializer=suggestions__pb2.SuggestionsRequest.SerializeToString, - response_deserializer=suggestions__pb2.SuggestionsResponse.FromString, - ) + request_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.FromString, + _registered_method=True) + self.TriggerSuggestions = channel.unary_unary( + '/suggestions.SuggestionsService/TriggerSuggestions', + request_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.TriggerRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.FromString, + _registered_method=True) + self.GetVectorClock = channel.unary_unary( + '/suggestions.SuggestionsService/GetVectorClock', + request_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/suggestions.SuggestionsService/ClearOrder', + request_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderResponse.FromString, + _registered_method=True) class SuggestionsServiceServicer(object): @@ -30,18 +65,52 @@ def GetSuggestions(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def TriggerSuggestions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetVectorClock(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClearOrder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_SuggestionsServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'GetSuggestions': grpc.unary_unary_rpc_method_handler( servicer.GetSuggestions, - request_deserializer=suggestions__pb2.SuggestionsRequest.FromString, - response_serializer=suggestions__pb2.SuggestionsResponse.SerializeToString, + request_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsRequest.FromString, + response_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.SerializeToString, + ), + 'TriggerSuggestions': grpc.unary_unary_rpc_method_handler( + servicer.TriggerSuggestions, + request_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.TriggerRequest.FromString, + response_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.SerializeToString, + ), + 'GetVectorClock': grpc.unary_unary_rpc_method_handler( + servicer.GetVectorClock, + request_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockRequest.FromString, + response_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderRequest.FromString, + response_serializer=utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'suggestions.SuggestionsService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('suggestions.SuggestionsService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -59,8 +128,99 @@ def GetSuggestions(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/suggestions.SuggestionsService/GetSuggestions', - suggestions__pb2.SuggestionsRequest.SerializeToString, - suggestions__pb2.SuggestionsResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/GetSuggestions', + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsRequest.SerializeToString, + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def TriggerSuggestions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/TriggerSuggestions', + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.TriggerRequest.SerializeToString, + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.SuggestionsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetVectorClock(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/GetVectorClock', + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockRequest.SerializeToString, + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.VectorClockResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/suggestions.SuggestionsService/ClearOrder', + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderRequest.SerializeToString, + utils_dot_pb_dot_suggestions_dot_suggestions__pb2.ClearOrderResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/utils/pb/transaction_verification/transaction_verification.proto b/utils/pb/transaction_verification/transaction_verification.proto index 44ba9cec7..a3027f0dd 100644 --- a/utils/pb/transaction_verification/transaction_verification.proto +++ b/utils/pb/transaction_verification/transaction_verification.proto @@ -4,19 +4,57 @@ package transaction_verification; service TransactionVerificationService { rpc VerifyTransaction (TransactionVerificationRequest) returns (TransactionVerificationResponse); + rpc TriggerVerification (TriggerRequest) returns (TransactionVerificationResponse); + rpc GetVectorClock (VectorClockRequest) returns (VectorClockResponse); + rpc ClearOrder (ClearOrderRequest) returns (ClearOrderResponse); } message TransactionVerificationRequest { - string user_name = 1; - string user_contact = 2; - string card_number = 3; - string expiration_date = 4; - string cvv = 5; - int32 item_count = 6; - bool terms_accepted = 7; + string order_id = 1; + string user_name = 2; + string user_contact = 3; + string card_number = 4; + string expiration_date = 5; + string cvv = 6; + int32 item_count = 7; + bool terms_accepted = 8; } message TransactionVerificationResponse { bool is_valid = 1; string message = 2; + bool failed = 3; +} + +message TriggerRequest { + string order_id = 1; + string event_type = 2; +} + +message VectorClockRequest { + string order_id = 1; +} + +message VectorClockResponse { + int32 transaction_verification = 1; + int32 tv_event_a = 2; + int32 tv_event_b = 3; + int32 tv_event_c = 4; + int32 fraud_detection = 5; + int32 suggestions = 6; +} + +message ClearOrderRequest { + string order_id = 1; + int32 final_vc_transaction_verification = 2; + int32 final_vc_tv_event_a = 3; + int32 final_vc_tv_event_b = 4; + int32 final_vc_tv_event_c = 5; + int32 final_vc_fraud_detection = 6; + int32 final_vc_suggestions = 7; +} + +message ClearOrderResponse { + bool success = 1; + string message = 2; } \ No newline at end of file diff --git a/utils/pb/transaction_verification/transaction_verification_pb2.py b/utils/pb/transaction_verification/transaction_verification_pb2.py index 447731158..218725786 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: transaction_verification.proto -# Protobuf Python Version: 4.25.0 +# NO CHECKED-IN PROTOBUF GENCODE +# source: utils/pb/transaction_verification/transaction_verification.proto +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'utils/pb/transaction_verification/transaction_verification.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,17 +24,27 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1etransaction_verification.proto\x12\x18transaction_verification\"\xb0\x01\n\x1eTransactionVerificationRequest\x12\x11\n\tuser_name\x18\x01 \x01(\t\x12\x14\n\x0cuser_contact\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x03 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x04 \x01(\t\x12\x0b\n\x03\x63vv\x18\x05 \x01(\t\x12\x12\n\nitem_count\x18\x06 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x07 \x01(\x08\"D\n\x1fTransactionVerificationResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xab\x01\n\x1eTransactionVerificationService\x12\x88\x01\n\x11VerifyTransaction\x12\x38.transaction_verification.TransactionVerificationRequest\x1a\x39.transaction_verification.TransactionVerificationResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n@utils/pb/transaction_verification/transaction_verification.proto\x12\x18transaction_verification\"\xc2\x01\n\x1eTransactionVerificationRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"T\n\x1fTransactionVerificationResponse\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0e\n\x06\x66\x61iled\x18\x03 \x01(\x08\"6\n\x0eTriggerRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x12\n\nevent_type\x18\x02 \x01(\t\"&\n\x12VectorClockRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\"\xa1\x01\n\x13VectorClockResponse\x12 \n\x18transaction_verification\x18\x01 \x01(\x05\x12\x12\n\ntv_event_a\x18\x02 \x01(\x05\x12\x12\n\ntv_event_b\x18\x03 \x01(\x05\x12\x12\n\ntv_event_c\x18\x04 \x01(\x05\x12\x17\n\x0f\x66raud_detection\x18\x05 \x01(\x05\x12\x13\n\x0bsuggestions\x18\x06 \x01(\x05\"\xe7\x01\n\x11\x43learOrderRequest\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12)\n!final_vc_transaction_verification\x18\x02 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_a\x18\x03 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_b\x18\x04 \x01(\x05\x12\x1b\n\x13\x66inal_vc_tv_event_c\x18\x05 \x01(\x05\x12 \n\x18\x66inal_vc_fraud_detection\x18\x06 \x01(\x05\x12\x1c\n\x14\x66inal_vc_suggestions\x18\x07 \x01(\x05\"6\n\x12\x43learOrderResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xff\x03\n\x1eTransactionVerificationService\x12\x88\x01\n\x11VerifyTransaction\x12\x38.transaction_verification.TransactionVerificationRequest\x1a\x39.transaction_verification.TransactionVerificationResponse\x12z\n\x13TriggerVerification\x12(.transaction_verification.TriggerRequest\x1a\x39.transaction_verification.TransactionVerificationResponse\x12m\n\x0eGetVectorClock\x12,.transaction_verification.VectorClockRequest\x1a-.transaction_verification.VectorClockResponse\x12g\n\nClearOrder\x12+.transaction_verification.ClearOrderRequest\x1a,.transaction_verification.ClearOrderResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'transaction_verification_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_start=61 - _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_end=237 - _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_start=239 - _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_end=307 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=310 - _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=481 +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'utils.pb.transaction_verification.transaction_verification_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_start=95 + _globals['_TRANSACTIONVERIFICATIONREQUEST']._serialized_end=289 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_start=291 + _globals['_TRANSACTIONVERIFICATIONRESPONSE']._serialized_end=375 + _globals['_TRIGGERREQUEST']._serialized_start=377 + _globals['_TRIGGERREQUEST']._serialized_end=431 + _globals['_VECTORCLOCKREQUEST']._serialized_start=433 + _globals['_VECTORCLOCKREQUEST']._serialized_end=471 + _globals['_VECTORCLOCKRESPONSE']._serialized_start=474 + _globals['_VECTORCLOCKRESPONSE']._serialized_end=635 + _globals['_CLEARORDERREQUEST']._serialized_start=638 + _globals['_CLEARORDERREQUEST']._serialized_end=869 + _globals['_CLEARORDERRESPONSE']._serialized_start=871 + _globals['_CLEARORDERRESPONSE']._serialized_end=925 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_start=928 + _globals['_TRANSACTIONVERIFICATIONSERVICE']._serialized_end=1439 # @@protoc_insertion_point(module_scope) diff --git a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py index 32d7e091c..c9eb0c7db 100644 --- a/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py +++ b/utils/pb/transaction_verification/transaction_verification_pb2_grpc.py @@ -1,8 +1,28 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings -import transaction_verification_pb2 as transaction__verification__pb2 +from utils.pb.transaction_verification import transaction_verification_pb2 as utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in utils/pb/transaction_verification/transaction_verification_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) class TransactionVerificationServiceStub(object): @@ -16,9 +36,24 @@ def __init__(self, channel): """ self.VerifyTransaction = channel.unary_unary( '/transaction_verification.TransactionVerificationService/VerifyTransaction', - request_serializer=transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, - response_deserializer=transaction__verification__pb2.TransactionVerificationResponse.FromString, - ) + request_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.FromString, + _registered_method=True) + self.TriggerVerification = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/TriggerVerification', + request_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TriggerRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.FromString, + _registered_method=True) + self.GetVectorClock = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/GetVectorClock', + request_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockResponse.FromString, + _registered_method=True) + self.ClearOrder = channel.unary_unary( + '/transaction_verification.TransactionVerificationService/ClearOrder', + request_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderRequest.SerializeToString, + response_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderResponse.FromString, + _registered_method=True) class TransactionVerificationServiceServicer(object): @@ -30,18 +65,52 @@ def VerifyTransaction(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def TriggerVerification(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetVectorClock(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ClearOrder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_TransactionVerificationServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'VerifyTransaction': grpc.unary_unary_rpc_method_handler( servicer.VerifyTransaction, - request_deserializer=transaction__verification__pb2.TransactionVerificationRequest.FromString, - response_serializer=transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, + request_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationRequest.FromString, + response_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, + ), + 'TriggerVerification': grpc.unary_unary_rpc_method_handler( + servicer.TriggerVerification, + request_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TriggerRequest.FromString, + response_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.SerializeToString, + ), + 'GetVectorClock': grpc.unary_unary_rpc_method_handler( + servicer.GetVectorClock, + request_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockRequest.FromString, + response_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockResponse.SerializeToString, + ), + 'ClearOrder': grpc.unary_unary_rpc_method_handler( + servicer.ClearOrder, + request_deserializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderRequest.FromString, + response_serializer=utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'transaction_verification.TransactionVerificationService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('transaction_verification.TransactionVerificationService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -59,8 +128,99 @@ def VerifyTransaction(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/transaction_verification.TransactionVerificationService/VerifyTransaction', - transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, - transaction__verification__pb2.TransactionVerificationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/VerifyTransaction', + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationRequest.SerializeToString, + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def TriggerVerification(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/TriggerVerification', + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TriggerRequest.SerializeToString, + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.TransactionVerificationResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetVectorClock(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/GetVectorClock', + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockRequest.SerializeToString, + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.VectorClockResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ClearOrder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/transaction_verification.TransactionVerificationService/ClearOrder', + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderRequest.SerializeToString, + utils_dot_pb_dot_transaction__verification_dot_transaction__verification__pb2.ClearOrderResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) From c7fe3bf6738ee3d5ea58004d0f1225e83651ee0d Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Wed, 25 Mar 2026 11:36:57 +0200 Subject: [PATCH 21/58] Implement Seminar 7 leader election --- docker-compose.yaml | 59 ++++- orchestrator/src/app.py | 98 ++++++-- order_executor/Dockerfile | 5 + order_executor/requirements.txt | 3 + order_executor/src/app.py | 233 ++++++++++++++++++ order_queue/Dockerfile | 5 + order_queue/requirements.txt | 3 + order_queue/src/app.py | 70 ++++++ utils/pb/order_executor/__init__.py | 0 utils/pb/order_executor/order_executor.proto | 29 +++ utils/pb/order_executor/order_executor_pb2.py | 46 ++++ .../pb/order_executor/order_executor_pb2.pyi | 35 +++ .../order_executor/order_executor_pb2_grpc.py | 183 ++++++++++++++ utils/pb/order_queue/__init__.py | 0 utils/pb/order_queue/order_queue.proto | 38 +++ utils/pb/order_queue/order_queue_pb2.py | 46 ++++ utils/pb/order_queue/order_queue_pb2.pyi | 55 +++++ utils/pb/order_queue/order_queue_pb2_grpc.py | 140 +++++++++++ 18 files changed, 1025 insertions(+), 23 deletions(-) create mode 100644 order_executor/Dockerfile create mode 100644 order_executor/requirements.txt create mode 100644 order_executor/src/app.py create mode 100644 order_queue/Dockerfile create mode 100644 order_queue/requirements.txt create mode 100644 order_queue/src/app.py create mode 100644 utils/pb/order_executor/__init__.py create mode 100644 utils/pb/order_executor/order_executor.proto create mode 100644 utils/pb/order_executor/order_executor_pb2.py create mode 100644 utils/pb/order_executor/order_executor_pb2.pyi create mode 100644 utils/pb/order_executor/order_executor_pb2_grpc.py create mode 100644 utils/pb/order_queue/__init__.py create mode 100644 utils/pb/order_queue/order_queue.proto create mode 100644 utils/pb/order_queue/order_queue_pb2.py create mode 100644 utils/pb/order_queue/order_queue_pb2.pyi create mode 100644 utils/pb/order_queue/order_queue_pb2_grpc.py diff --git a/docker-compose.yaml b/docker-compose.yaml index e4818a9c8..c8030e3e6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,4 @@ -version: '3' + services: frontend: build: @@ -36,6 +36,11 @@ services: - ./utils:/app/utils # Mount the orchestrator/src directory in the current directory to the /app/orchestrator/src directory in the container - ./orchestrator/src:/app/orchestrator/src + depends_on: + - fraud_detection + - transaction_verification + - suggestions + - order_queue fraud_detection: build: @@ -63,6 +68,7 @@ services: transaction_verification: build: # Use the current directory as the build context + # This allows us to access the files in the current directory inside the Dockerfile context: ./ # Use the Dockerfile in the transaction_verification directory dockerfile: ./transaction_verification/Dockerfile @@ -91,4 +97,53 @@ services: - PYTHONFILE=/app/suggestions/src/app.py volumes: - ./utils:/app/utils - - ./suggestions/src:/app/suggestions/src \ No newline at end of file + - ./suggestions/src:/app/suggestions/src + + order_queue: + build: + context: ./ + dockerfile: ./order_queue/Dockerfile + ports: + - 50054:50054 + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/order_queue/src/app.py + volumes: + - ./utils:/app/utils + - ./order_queue/src:/app/order_queue/src + + order_executor_1: + build: + context: ./ + dockerfile: ./order_executor/Dockerfile + ports: + - 50055:50055 + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/order_executor/src/app.py + - EXECUTOR_ID=1 + - EXECUTOR_PORT=50055 + - PEERS=1@order_executor_1:50055,2@order_executor_2:50055 + volumes: + - ./utils:/app/utils + - ./order_executor/src:/app/order_executor/src + depends_on: + - order_queue + + order_executor_2: + build: + context: ./ + dockerfile: ./order_executor/Dockerfile + ports: + - 50056:50055 + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/order_executor/src/app.py + - EXECUTOR_ID=2 + - EXECUTOR_PORT=50055 + - PEERS=1@order_executor_1:50055,2@order_executor_2:50055 + volumes: + - ./utils:/app/utils + - ./order_executor/src:/app/order_executor/src + depends_on: + - order_queue \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index f1aaec6f5..395c1be25 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -31,6 +31,13 @@ import suggestions_pb2 as suggestions import suggestions_pb2_grpc as suggestions_grpc +order_queue_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/order_queue") +) +sys.path.insert(0, order_queue_grpc_path) +import order_queue_pb2 as order_queue +import order_queue_pb2_grpc as order_queue_grpc + app = Flask(__name__) CORS(app, resources={r"/*": {"origins": "*"}}) @@ -50,7 +57,9 @@ def merge_vcs(*vectors): return result -def build_order_kwargs(user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted): +def build_order_kwargs( + user_name, user_contact, card_number, expiration_date, cvv, item_count, terms_accepted +): return { "user_name": user_name, "user_contact": user_contact, @@ -89,6 +98,24 @@ def init_suggestions_service(order_id, order_kwargs): return stub.InitOrder(request, timeout=5.0) +def enqueue_order(order_id, order_kwargs): + with grpc.insecure_channel("order_queue:50054") as channel: + stub = order_queue_grpc.OrderQueueServiceStub(channel) + request = order_queue.EnqueueRequest( + order=order_queue.OrderData( + order_id=order_id, + user_name=order_kwargs["user_name"], + user_contact=order_kwargs["user_contact"], + card_number=order_kwargs["card_number"], + expiration_date=order_kwargs["expiration_date"], + cvv=order_kwargs["cvv"], + item_count=order_kwargs["item_count"], + terms_accepted=order_kwargs["terms_accepted"], + ) + ) + return stub.Enqueue(request, timeout=5.0) + + def tv_validate_items(order_id, vc): with grpc.insecure_channel("transaction_verification:50052") as channel: stub = transaction_verification_grpc.TransactionVerificationServiceStub(channel) @@ -189,6 +216,16 @@ def clear_suggestions_service(order_id, final_vc): return stub.ClearOrder(request, timeout=5.0) +def broadcast_clear(order_id, final_vc): + try: + clear_transaction_service(order_id, final_vc) + clear_fraud_service(order_id, final_vc) + clear_suggestions_service(order_id, final_vc) + print(f"[ORCH] order={order_id} clear_broadcast_sent final_vc={final_vc}") + except Exception as e: + print(f"[ORCH] order={order_id} clear_broadcast_warning={e}") + + @app.route("/", methods=["GET"]) def index(): return {"message": "Orchestrator is running."}, 200 @@ -201,7 +238,7 @@ def checkout(): return { "error": { "code": "BAD_REQUEST", - "message": "Request body must be valid JSON." + "message": "Request body must be valid JSON.", } }, 400 @@ -221,7 +258,7 @@ def checkout(): return { "error": { "code": "BAD_REQUEST", - "message": "User name is required." + "message": "User name is required.", } }, 400 @@ -229,7 +266,7 @@ def checkout(): return { "error": { "code": "BAD_REQUEST", - "message": "User contact is required." + "message": "User contact is required.", } }, 400 @@ -261,7 +298,7 @@ def checkout(): return { "error": { "code": "INTERNAL_ERROR", - "message": "Failed to initialize backend services." + "message": "Failed to initialize backend services.", } }, 500 @@ -271,7 +308,9 @@ def checkout(): ("InitSuggestions", init_sug), ]: if not response.success: - print(f"[ORCH] order={order_id} step={name} success=False message={response.message}") + print( + f"[ORCH] order={order_id} step={name} success=False message={response.message}" + ) return { "orderId": order_id, "status": "Order Rejected", @@ -298,7 +337,7 @@ def checkout(): "event_vcs": {}, "final_vc": [0, 0, 0], "books": [], - "failure_kind": None, # "event_failure" or "internal_error" + "failure_kind": None, # "event_failure" or "internal_error" "failed_step": None, "failure_message": None, } @@ -375,11 +414,13 @@ def run_finalize(): if response.success: books = [] for book in response.books: - books.append({ - "bookId": book.bookId, - "title": book.title, - "author": book.author, - }) + books.append( + { + "bookId": book.bookId, + "title": book.title, + "author": book.author, + } + ) with lock: state["books"] = books else: @@ -417,16 +458,8 @@ def run_finalize(): final_vc = state["final_vc"] - # Bonus cleanup / useful for repeat testing - try: - clear_transaction_service(order_id, final_vc) - clear_fraud_service(order_id, final_vc) - clear_suggestions_service(order_id, final_vc) - print(f"[ORCH] order={order_id} clear_broadcast_sent final_vc={final_vc}") - except Exception as e: - print(f"[ORCH] order={order_id} clear_broadcast_warning={e}") - if state["failure_kind"] == "internal_error": + broadcast_clear(order_id, final_vc) return { "error": { "code": "INTERNAL_ERROR", @@ -435,6 +468,7 @@ def run_finalize(): }, 500 if state["failure_kind"] == "event_failure": + broadcast_clear(order_id, final_vc) return { "orderId": order_id, "status": "Order Rejected", @@ -442,6 +476,28 @@ def run_finalize(): "reason": state["failure_message"], }, 200 + try: + enqueue_response = enqueue_order(order_id, order_kwargs) + except Exception as e: + print(f"[ORCH] order={order_id} enqueue_error={e}") + return { + "error": { + "code": "INTERNAL_ERROR", + "message": "Order was approved but could not be queued.", + } + }, 500 + + if not enqueue_response.success: + print(f"[ORCH] order={order_id} enqueue_failed message={enqueue_response.message}") + return { + "error": { + "code": "INTERNAL_ERROR", + "message": enqueue_response.message, + } + }, 500 + + print(f"[ORCH] order={order_id} enqueue_success") + broadcast_clear(order_id, final_vc) print(f"[ORCH] order={order_id} final_status=APPROVED final_vc={final_vc}") return { diff --git a/order_executor/Dockerfile b/order_executor/Dockerfile new file mode 100644 index 000000000..f32faaae0 --- /dev/null +++ b/order_executor/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.11 +WORKDIR /app +COPY ./order_executor/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +CMD python utils/other/hotreload.py "order_executor/src/app.py" \ No newline at end of file diff --git a/order_executor/requirements.txt b/order_executor/requirements.txt new file mode 100644 index 000000000..1731b14fb --- /dev/null +++ b/order_executor/requirements.txt @@ -0,0 +1,3 @@ +grpcio==1.78.0 +grpcio-tools==1.78.0 +watchdog==6.0.0 \ No newline at end of file diff --git a/order_executor/src/app.py b/order_executor/src/app.py new file mode 100644 index 000000000..c3d529ca7 --- /dev/null +++ b/order_executor/src/app.py @@ -0,0 +1,233 @@ +import os +import sys +import time +import grpc +import threading +from concurrent import futures + +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") + +executor_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/order_executor") +) +queue_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/order_queue") +) + +sys.path.insert(0, executor_grpc_path) +sys.path.insert(0, queue_grpc_path) + +import order_executor_pb2 as executor_pb2 +import order_executor_pb2_grpc as executor_grpc +import order_queue_pb2 as queue_pb2 +import order_queue_pb2_grpc as queue_grpc + + +EXECUTOR_ID = int(os.getenv("EXECUTOR_ID", "1")) +EXECUTOR_PORT = os.getenv("EXECUTOR_PORT", "50055") +HEARTBEAT_INTERVAL = 2.0 +LEADER_TIMEOUT = 5.0 + +state_lock = threading.Lock() +leader_id = None +last_heartbeat = 0.0 +is_leader = False +election_in_progress = False + + +def parse_peers(): + peers = [] + raw = os.getenv("PEERS", "") + for item in raw.split(","): + item = item.strip() + if not item: + continue + peer_id, peer_addr = item.split("@", 1) + peers.append((int(peer_id), peer_addr)) + return peers + + +PEERS = parse_peers() + + +class ControlService(executor_grpc.OrderExecutorControlServicer): + def Election(self, request, context): + global election_in_progress + if EXECUTOR_ID > request.candidate_id: + print(f"[EXEC-{EXECUTOR_ID}] received election from {request.candidate_id}") + threading.Thread(target=start_election, daemon=True).start() + return executor_pb2.ElectionResponse(alive=True) + return executor_pb2.ElectionResponse(alive=False) + + def Coordinator(self, request, context): + global leader_id, is_leader, election_in_progress, last_heartbeat + with state_lock: + leader_id = request.leader_id + is_leader = leader_id == EXECUTOR_ID + election_in_progress = False + last_heartbeat = time.time() + + print(f"[EXEC-{EXECUTOR_ID}] new leader is {leader_id}") + return executor_pb2.Ack(ok=True) + + def Heartbeat(self, request, context): + global leader_id, is_leader, last_heartbeat + with state_lock: + leader_id = request.leader_id + is_leader = leader_id == EXECUTOR_ID + last_heartbeat = time.time() + return executor_pb2.Ack(ok=True) + + +def send_rpc(addr, fn): + try: + with grpc.insecure_channel(addr) as channel: + stub = executor_grpc.OrderExecutorControlStub(channel) + return fn(stub) + except Exception: + return None + + +def start_election(): + global election_in_progress + with state_lock: + if election_in_progress: + return + election_in_progress = True + + print(f"[EXEC-{EXECUTOR_ID}] starting election") + + higher_peers = [(pid, addr) for pid, addr in PEERS if pid > EXECUTOR_ID] + got_answer = False + + for pid, addr in higher_peers: + response = send_rpc( + addr, + lambda stub: stub.Election( + executor_pb2.ElectionRequest(candidate_id=EXECUTOR_ID), + timeout=2.0, + ), + ) + if response and response.alive: + got_answer = True + + if not got_answer: + become_leader() + else: + time.sleep(LEADER_TIMEOUT) + with state_lock: + current_leader = leader_id + election_in_progress = False + + if current_leader is None: + start_election() + + +def become_leader(): + global leader_id, is_leader, election_in_progress, last_heartbeat + with state_lock: + leader_id = EXECUTOR_ID + is_leader = True + election_in_progress = False + last_heartbeat = time.time() + + print(f"[EXEC-{EXECUTOR_ID}] became leader") + + for pid, addr in PEERS: + if pid == EXECUTOR_ID: + continue + send_rpc( + addr, + lambda stub: stub.Coordinator( + executor_pb2.CoordinatorRequest(leader_id=EXECUTOR_ID), + timeout=2.0, + ), + ) + + +def heartbeat_loop(): + while True: + time.sleep(HEARTBEAT_INTERVAL) + with state_lock: + leader_now = is_leader + if not leader_now: + continue + + for pid, addr in PEERS: + if pid == EXECUTOR_ID: + continue + send_rpc( + addr, + lambda stub: stub.Heartbeat( + executor_pb2.HeartbeatRequest(leader_id=EXECUTOR_ID), + timeout=2.0, + ), + ) + + +def timeout_loop(): + global leader_id + while True: + time.sleep(1.0) + with state_lock: + expired = (not is_leader) and ( + leader_id is None or (time.time() - last_heartbeat > LEADER_TIMEOUT) + ) + if expired: + print(f"[EXEC-{EXECUTOR_ID}] leader timeout detected") + with state_lock: + leader_id = None + start_election() + + +def consume_loop(): + while True: + time.sleep(1.0) + + with state_lock: + if not is_leader: + continue + + try: + with grpc.insecure_channel("order_queue:50054") as channel: + stub = queue_grpc.OrderQueueServiceStub(channel) + response = stub.Dequeue( + queue_pb2.DequeueRequest(executor_id=str(EXECUTOR_ID)), + timeout=2.0, + ) + except Exception as e: + print(f"[EXEC-{EXECUTOR_ID}] queue error: {e}") + continue + + if not response.success: + continue + + print( + f"[EXEC-{EXECUTOR_ID}] leader={EXECUTOR_ID} " + f"executing order={response.order.order_id} " + f'user="{response.order.user_name}" item_count={response.order.item_count}' + ) + print("Order is being executed...") + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + executor_grpc.add_OrderExecutorControlServicer_to_server( + ControlService(), server + ) + server.add_insecure_port("[::]:" + EXECUTOR_PORT) + server.start() + print(f"[EXEC-{EXECUTOR_ID}] listening on port {EXECUTOR_PORT}") + + threading.Thread(target=heartbeat_loop, daemon=True).start() + threading.Thread(target=timeout_loop, daemon=True).start() + threading.Thread(target=consume_loop, daemon=True).start() + + time.sleep(1.0) + start_election() + + server.wait_for_termination() + + +if __name__ == "__main__": + serve() \ No newline at end of file diff --git a/order_queue/Dockerfile b/order_queue/Dockerfile new file mode 100644 index 000000000..dd5446043 --- /dev/null +++ b/order_queue/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.11 +WORKDIR /app +COPY ./order_queue/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +CMD python utils/other/hotreload.py "order_queue/src/app.py" \ No newline at end of file diff --git a/order_queue/requirements.txt b/order_queue/requirements.txt new file mode 100644 index 000000000..1731b14fb --- /dev/null +++ b/order_queue/requirements.txt @@ -0,0 +1,3 @@ +grpcio==1.78.0 +grpcio-tools==1.78.0 +watchdog==6.0.0 \ No newline at end of file diff --git a/order_queue/src/app.py b/order_queue/src/app.py new file mode 100644 index 000000000..d1ce51b07 --- /dev/null +++ b/order_queue/src/app.py @@ -0,0 +1,70 @@ +import os +import sys +import threading +from collections import deque +from concurrent import futures + +FILE = __file__ if "__file__" in globals() else os.getenv("PYTHONFILE", "") + +queue_grpc_path = os.path.abspath( + os.path.join(FILE, "../../../utils/pb/order_queue") +) +sys.path.insert(0, queue_grpc_path) + +import grpc +import order_queue_pb2 as order_queue +import order_queue_pb2_grpc as order_queue_grpc + + +orders = deque() +queue_lock = threading.Lock() + + +class OrderQueueService(order_queue_grpc.OrderQueueServiceServicer): + def Enqueue(self, request, context): + with queue_lock: + orders.append(request.order) + + print( + f"[QUEUE] action=enqueue order={request.order.order_id} " + f"size={len(orders)}" + ) + return order_queue.QueueResponse( + success=True, + message="Order enqueued." + ) + + def Dequeue(self, request, context): + with queue_lock: + if not orders: + return order_queue.DequeueResponse( + success=False, + message="Queue is empty." + ) + order = orders.popleft() + + print( + f"[QUEUE] action=dequeue order={order.order_id} " + f"executor={request.executor_id} size={len(orders)}" + ) + return order_queue.DequeueResponse( + success=True, + message="Order dequeued.", + order=order + ) + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + order_queue_grpc.add_OrderQueueServiceServicer_to_server( + OrderQueueService(), server + ) + port = "50054" + server.add_insecure_port("[::]:" + port) + server.start() + print(f"Order queue server started. Listening on port {port}.") + server.wait_for_termination() + + +if __name__ == "__main__": + serve() \ No newline at end of file diff --git a/utils/pb/order_executor/__init__.py b/utils/pb/order_executor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/pb/order_executor/order_executor.proto b/utils/pb/order_executor/order_executor.proto new file mode 100644 index 000000000..51c665f98 --- /dev/null +++ b/utils/pb/order_executor/order_executor.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package order_executor; + +service OrderExecutorControl { + rpc Election (ElectionRequest) returns (ElectionResponse); + rpc Coordinator (CoordinatorRequest) returns (Ack); + rpc Heartbeat (HeartbeatRequest) returns (Ack); +} + +message ElectionRequest { + int32 candidate_id = 1; +} + +message ElectionResponse { + bool alive = 1; +} + +message CoordinatorRequest { + int32 leader_id = 1; +} + +message HeartbeatRequest { + int32 leader_id = 1; +} + +message Ack { + bool ok = 1; +} \ No newline at end of file diff --git a/utils/pb/order_executor/order_executor_pb2.py b/utils/pb/order_executor/order_executor_pb2.py new file mode 100644 index 000000000..50b3d873b --- /dev/null +++ b/utils/pb/order_executor/order_executor_pb2.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: order_executor.proto +# Protobuf Python Version: 5.29.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'order_executor.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14order_executor.proto\x12\x0eorder_executor\"\'\n\x0f\x45lectionRequest\x12\x14\n\x0c\x63\x61ndidate_id\x18\x01 \x01(\x05\"!\n\x10\x45lectionResponse\x12\r\n\x05\x61live\x18\x01 \x01(\x08\"\'\n\x12\x43oordinatorRequest\x12\x11\n\tleader_id\x18\x01 \x01(\x05\"%\n\x10HeartbeatRequest\x12\x11\n\tleader_id\x18\x01 \x01(\x05\"\x11\n\x03\x41\x63k\x12\n\n\x02ok\x18\x01 \x01(\x08\x32\xf1\x01\n\x14OrderExecutorControl\x12M\n\x08\x45lection\x12\x1f.order_executor.ElectionRequest\x1a .order_executor.ElectionResponse\x12\x46\n\x0b\x43oordinator\x12\".order_executor.CoordinatorRequest\x1a\x13.order_executor.Ack\x12\x42\n\tHeartbeat\x12 .order_executor.HeartbeatRequest\x1a\x13.order_executor.Ackb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'order_executor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_ELECTIONREQUEST']._serialized_start=40 + _globals['_ELECTIONREQUEST']._serialized_end=79 + _globals['_ELECTIONRESPONSE']._serialized_start=81 + _globals['_ELECTIONRESPONSE']._serialized_end=114 + _globals['_COORDINATORREQUEST']._serialized_start=116 + _globals['_COORDINATORREQUEST']._serialized_end=155 + _globals['_HEARTBEATREQUEST']._serialized_start=157 + _globals['_HEARTBEATREQUEST']._serialized_end=194 + _globals['_ACK']._serialized_start=196 + _globals['_ACK']._serialized_end=213 + _globals['_ORDEREXECUTORCONTROL']._serialized_start=216 + _globals['_ORDEREXECUTORCONTROL']._serialized_end=457 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/order_executor/order_executor_pb2.pyi b/utils/pb/order_executor/order_executor_pb2.pyi new file mode 100644 index 000000000..c8ad8e7c6 --- /dev/null +++ b/utils/pb/order_executor/order_executor_pb2.pyi @@ -0,0 +1,35 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class ElectionRequest(_message.Message): + __slots__ = ("candidate_id",) + CANDIDATE_ID_FIELD_NUMBER: _ClassVar[int] + candidate_id: int + def __init__(self, candidate_id: _Optional[int] = ...) -> None: ... + +class ElectionResponse(_message.Message): + __slots__ = ("alive",) + ALIVE_FIELD_NUMBER: _ClassVar[int] + alive: bool + def __init__(self, alive: bool = ...) -> None: ... + +class CoordinatorRequest(_message.Message): + __slots__ = ("leader_id",) + LEADER_ID_FIELD_NUMBER: _ClassVar[int] + leader_id: int + def __init__(self, leader_id: _Optional[int] = ...) -> None: ... + +class HeartbeatRequest(_message.Message): + __slots__ = ("leader_id",) + LEADER_ID_FIELD_NUMBER: _ClassVar[int] + leader_id: int + def __init__(self, leader_id: _Optional[int] = ...) -> None: ... + +class Ack(_message.Message): + __slots__ = ("ok",) + OK_FIELD_NUMBER: _ClassVar[int] + ok: bool + def __init__(self, ok: bool = ...) -> None: ... diff --git a/utils/pb/order_executor/order_executor_pb2_grpc.py b/utils/pb/order_executor/order_executor_pb2_grpc.py new file mode 100644 index 000000000..7c9c28f14 --- /dev/null +++ b/utils/pb/order_executor/order_executor_pb2_grpc.py @@ -0,0 +1,183 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import order_executor_pb2 as order__executor__pb2 + +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in order_executor_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class OrderExecutorControlStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Election = channel.unary_unary( + '/order_executor.OrderExecutorControl/Election', + request_serializer=order__executor__pb2.ElectionRequest.SerializeToString, + response_deserializer=order__executor__pb2.ElectionResponse.FromString, + _registered_method=True) + self.Coordinator = channel.unary_unary( + '/order_executor.OrderExecutorControl/Coordinator', + request_serializer=order__executor__pb2.CoordinatorRequest.SerializeToString, + response_deserializer=order__executor__pb2.Ack.FromString, + _registered_method=True) + self.Heartbeat = channel.unary_unary( + '/order_executor.OrderExecutorControl/Heartbeat', + request_serializer=order__executor__pb2.HeartbeatRequest.SerializeToString, + response_deserializer=order__executor__pb2.Ack.FromString, + _registered_method=True) + + +class OrderExecutorControlServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Election(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Coordinator(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Heartbeat(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_OrderExecutorControlServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Election': grpc.unary_unary_rpc_method_handler( + servicer.Election, + request_deserializer=order__executor__pb2.ElectionRequest.FromString, + response_serializer=order__executor__pb2.ElectionResponse.SerializeToString, + ), + 'Coordinator': grpc.unary_unary_rpc_method_handler( + servicer.Coordinator, + request_deserializer=order__executor__pb2.CoordinatorRequest.FromString, + response_serializer=order__executor__pb2.Ack.SerializeToString, + ), + 'Heartbeat': grpc.unary_unary_rpc_method_handler( + servicer.Heartbeat, + request_deserializer=order__executor__pb2.HeartbeatRequest.FromString, + response_serializer=order__executor__pb2.Ack.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'order_executor.OrderExecutorControl', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('order_executor.OrderExecutorControl', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class OrderExecutorControl(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Election(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/order_executor.OrderExecutorControl/Election', + order__executor__pb2.ElectionRequest.SerializeToString, + order__executor__pb2.ElectionResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Coordinator(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/order_executor.OrderExecutorControl/Coordinator', + order__executor__pb2.CoordinatorRequest.SerializeToString, + order__executor__pb2.Ack.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Heartbeat(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/order_executor.OrderExecutorControl/Heartbeat', + order__executor__pb2.HeartbeatRequest.SerializeToString, + order__executor__pb2.Ack.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/utils/pb/order_queue/__init__.py b/utils/pb/order_queue/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/pb/order_queue/order_queue.proto b/utils/pb/order_queue/order_queue.proto new file mode 100644 index 000000000..a00047fe4 --- /dev/null +++ b/utils/pb/order_queue/order_queue.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package order_queue; + +service OrderQueueService { + rpc Enqueue (EnqueueRequest) returns (QueueResponse); + rpc Dequeue (DequeueRequest) returns (DequeueResponse); +} + +message OrderData { + string order_id = 1; + string user_name = 2; + string user_contact = 3; + string card_number = 4; + string expiration_date = 5; + string cvv = 6; + int32 item_count = 7; + bool terms_accepted = 8; +} + +message EnqueueRequest { + OrderData order = 1; +} + +message DequeueRequest { + string executor_id = 1; +} + +message QueueResponse { + bool success = 1; + string message = 2; +} + +message DequeueResponse { + bool success = 1; + string message = 2; + OrderData order = 3; +} \ No newline at end of file diff --git a/utils/pb/order_queue/order_queue_pb2.py b/utils/pb/order_queue/order_queue_pb2.py new file mode 100644 index 000000000..4449f90e2 --- /dev/null +++ b/utils/pb/order_queue/order_queue_pb2.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: order_queue.proto +# Protobuf Python Version: 5.29.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'order_queue.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11order_queue.proto\x12\x0border_queue\"\xad\x01\n\tOrderData\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x11\n\tuser_name\x18\x02 \x01(\t\x12\x14\n\x0cuser_contact\x18\x03 \x01(\t\x12\x13\n\x0b\x63\x61rd_number\x18\x04 \x01(\t\x12\x17\n\x0f\x65xpiration_date\x18\x05 \x01(\t\x12\x0b\n\x03\x63vv\x18\x06 \x01(\t\x12\x12\n\nitem_count\x18\x07 \x01(\x05\x12\x16\n\x0eterms_accepted\x18\x08 \x01(\x08\"7\n\x0e\x45nqueueRequest\x12%\n\x05order\x18\x01 \x01(\x0b\x32\x16.order_queue.OrderData\"%\n\x0e\x44\x65queueRequest\x12\x13\n\x0b\x65xecutor_id\x18\x01 \x01(\t\"1\n\rQueueResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"Z\n\x0f\x44\x65queueResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x05order\x18\x03 \x01(\x0b\x32\x16.order_queue.OrderData2\x9d\x01\n\x11OrderQueueService\x12\x42\n\x07\x45nqueue\x12\x1b.order_queue.EnqueueRequest\x1a\x1a.order_queue.QueueResponse\x12\x44\n\x07\x44\x65queue\x12\x1b.order_queue.DequeueRequest\x1a\x1c.order_queue.DequeueResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'order_queue_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_ORDERDATA']._serialized_start=35 + _globals['_ORDERDATA']._serialized_end=208 + _globals['_ENQUEUEREQUEST']._serialized_start=210 + _globals['_ENQUEUEREQUEST']._serialized_end=265 + _globals['_DEQUEUEREQUEST']._serialized_start=267 + _globals['_DEQUEUEREQUEST']._serialized_end=304 + _globals['_QUEUERESPONSE']._serialized_start=306 + _globals['_QUEUERESPONSE']._serialized_end=355 + _globals['_DEQUEUERESPONSE']._serialized_start=357 + _globals['_DEQUEUERESPONSE']._serialized_end=447 + _globals['_ORDERQUEUESERVICE']._serialized_start=450 + _globals['_ORDERQUEUESERVICE']._serialized_end=607 +# @@protoc_insertion_point(module_scope) diff --git a/utils/pb/order_queue/order_queue_pb2.pyi b/utils/pb/order_queue/order_queue_pb2.pyi new file mode 100644 index 000000000..657843d1f --- /dev/null +++ b/utils/pb/order_queue/order_queue_pb2.pyi @@ -0,0 +1,55 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class OrderData(_message.Message): + __slots__ = ("order_id", "user_name", "user_contact", "card_number", "expiration_date", "cvv", "item_count", "terms_accepted") + ORDER_ID_FIELD_NUMBER: _ClassVar[int] + USER_NAME_FIELD_NUMBER: _ClassVar[int] + USER_CONTACT_FIELD_NUMBER: _ClassVar[int] + CARD_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_DATE_FIELD_NUMBER: _ClassVar[int] + CVV_FIELD_NUMBER: _ClassVar[int] + ITEM_COUNT_FIELD_NUMBER: _ClassVar[int] + TERMS_ACCEPTED_FIELD_NUMBER: _ClassVar[int] + order_id: str + user_name: str + user_contact: str + card_number: str + expiration_date: str + cvv: str + item_count: int + terms_accepted: bool + def __init__(self, order_id: _Optional[str] = ..., user_name: _Optional[str] = ..., user_contact: _Optional[str] = ..., card_number: _Optional[str] = ..., expiration_date: _Optional[str] = ..., cvv: _Optional[str] = ..., item_count: _Optional[int] = ..., terms_accepted: bool = ...) -> None: ... + +class EnqueueRequest(_message.Message): + __slots__ = ("order",) + ORDER_FIELD_NUMBER: _ClassVar[int] + order: OrderData + def __init__(self, order: _Optional[_Union[OrderData, _Mapping]] = ...) -> None: ... + +class DequeueRequest(_message.Message): + __slots__ = ("executor_id",) + EXECUTOR_ID_FIELD_NUMBER: _ClassVar[int] + executor_id: str + def __init__(self, executor_id: _Optional[str] = ...) -> None: ... + +class QueueResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class DequeueResponse(_message.Message): + __slots__ = ("success", "message", "order") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + ORDER_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + order: OrderData + def __init__(self, success: bool = ..., message: _Optional[str] = ..., order: _Optional[_Union[OrderData, _Mapping]] = ...) -> None: ... diff --git a/utils/pb/order_queue/order_queue_pb2_grpc.py b/utils/pb/order_queue/order_queue_pb2_grpc.py new file mode 100644 index 000000000..fc1f3f8bf --- /dev/null +++ b/utils/pb/order_queue/order_queue_pb2_grpc.py @@ -0,0 +1,140 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import order_queue_pb2 as order__queue__pb2 + +GRPC_GENERATED_VERSION = '1.70.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in order_queue_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class OrderQueueServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Enqueue = channel.unary_unary( + '/order_queue.OrderQueueService/Enqueue', + request_serializer=order__queue__pb2.EnqueueRequest.SerializeToString, + response_deserializer=order__queue__pb2.QueueResponse.FromString, + _registered_method=True) + self.Dequeue = channel.unary_unary( + '/order_queue.OrderQueueService/Dequeue', + request_serializer=order__queue__pb2.DequeueRequest.SerializeToString, + response_deserializer=order__queue__pb2.DequeueResponse.FromString, + _registered_method=True) + + +class OrderQueueServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Enqueue(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Dequeue(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_OrderQueueServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Enqueue': grpc.unary_unary_rpc_method_handler( + servicer.Enqueue, + request_deserializer=order__queue__pb2.EnqueueRequest.FromString, + response_serializer=order__queue__pb2.QueueResponse.SerializeToString, + ), + 'Dequeue': grpc.unary_unary_rpc_method_handler( + servicer.Dequeue, + request_deserializer=order__queue__pb2.DequeueRequest.FromString, + response_serializer=order__queue__pb2.DequeueResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'order_queue.OrderQueueService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('order_queue.OrderQueueService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class OrderQueueService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Enqueue(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/order_queue.OrderQueueService/Enqueue', + order__queue__pb2.EnqueueRequest.SerializeToString, + order__queue__pb2.QueueResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Dequeue(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/order_queue.OrderQueueService/Dequeue', + order__queue__pb2.DequeueRequest.SerializeToString, + order__queue__pb2.DequeueResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) From 1208b717a8c8551c810b33aebc393717447d42c5 Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Wed, 1 Apr 2026 15:56:58 +0300 Subject: [PATCH 22/58] Add 3-executor leader election bonus implementation --- docker-compose.yaml | 70 ++++++++++-------------- order_executor/src/app.py | 108 ++++++++++++++++++++++++++++---------- 2 files changed, 107 insertions(+), 71 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index c8030e3e6..d0b2eab32 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,40 +1,24 @@ - services: frontend: build: - # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ dockerfile: ./frontend/Dockerfile ports: - # Expose port 8080 on the host, and map port 80 of the container to port 8080 on the host - # Access the application at http://localhost:8080 - "8080:80" volumes: - # Mount the frontend directory - ./frontend/src:/usr/share/nginx/html orchestrator: build: - # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the orchestrator directory dockerfile: ./orchestrator/Dockerfile ports: - # Expose port 8081 on the host, and map port 5000 of the container to port 8081 on the host - - 8081:5000 + - "8081:5000" environment: - # Pass the environment variables to the container - # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console - PYTHONUNBUFFERED=TRUE - # The PYTHONFILE environment variable specifies the absolute entry point of the application - # Check app.py in the orchestrator directory to see how this is used - PYTHONFILE=/app/orchestrator/src/app.py volumes: - # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils - # Mount the orchestrator/src directory in the current directory to the /app/orchestrator/src directory in the container - ./orchestrator/src:/app/orchestrator/src depends_on: - fraud_detection @@ -44,46 +28,28 @@ services: fraud_detection: build: - # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the fraud_detection directory dockerfile: ./fraud_detection/Dockerfile ports: - # Expose port 50051 on the host, and map port 50051 of the container to port 50051 on the host - - 50051:50051 + - "50051:50051" environment: - # Pass the environment variables to the container - # The PYTHONUNBUFFERED environment variable ensures that the output from the application is logged to the console - PYTHONUNBUFFERED=TRUE - # The PYTHONFILE environment variable specifies the absolute entry point of the application - # Check app.py in the fraud_detection directory to see how this is used - PYTHONFILE=/app/fraud_detection/src/app.py volumes: - # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils - # Mount the fraud_detection/src directory in the current directory to the /app/fraud_detection/src directory in the container - ./fraud_detection/src:/app/fraud_detection/src transaction_verification: build: - # Use the current directory as the build context - # This allows us to access the files in the current directory inside the Dockerfile context: ./ - # Use the Dockerfile in the transaction_verification directory dockerfile: ./transaction_verification/Dockerfile ports: - # Expose port 50052 on the host, and map port 50052 of the container to port 50052 on the host - - 50052:50052 + - "50052:50052" environment: - # Pass the environment variables to the container - PYTHONUNBUFFERED=TRUE - # The PYTHONFILE environment variable specifies the absolute entry point of the application - PYTHONFILE=/app/transaction_verification/src/app.py volumes: - # Mount the utils directory in the current directory to the /app/utils directory in the container - ./utils:/app/utils - # Mount the transaction_verification/src directory in the current directory to the container - ./transaction_verification/src:/app/transaction_verification/src suggestions: @@ -91,7 +57,7 @@ services: context: ./ dockerfile: ./suggestions/Dockerfile ports: - - 50053:50053 + - "50053:50053" environment: - PYTHONUNBUFFERED=TRUE - PYTHONFILE=/app/suggestions/src/app.py @@ -104,7 +70,7 @@ services: context: ./ dockerfile: ./order_queue/Dockerfile ports: - - 50054:50054 + - "50054:50054" environment: - PYTHONUNBUFFERED=TRUE - PYTHONFILE=/app/order_queue/src/app.py @@ -117,13 +83,13 @@ services: context: ./ dockerfile: ./order_executor/Dockerfile ports: - - 50055:50055 + - "50055:50055" environment: - PYTHONUNBUFFERED=TRUE - PYTHONFILE=/app/order_executor/src/app.py - EXECUTOR_ID=1 - EXECUTOR_PORT=50055 - - PEERS=1@order_executor_1:50055,2@order_executor_2:50055 + - PEERS=1@order_executor_1:50055,2@order_executor_2:50055,3@order_executor_3:50055 volumes: - ./utils:/app/utils - ./order_executor/src:/app/order_executor/src @@ -135,13 +101,31 @@ services: context: ./ dockerfile: ./order_executor/Dockerfile ports: - - 50056:50055 + - "50056:50055" environment: - PYTHONUNBUFFERED=TRUE - PYTHONFILE=/app/order_executor/src/app.py - EXECUTOR_ID=2 - EXECUTOR_PORT=50055 - - PEERS=1@order_executor_1:50055,2@order_executor_2:50055 + - PEERS=1@order_executor_1:50055,2@order_executor_2:50055,3@order_executor_3:50055 + volumes: + - ./utils:/app/utils + - ./order_executor/src:/app/order_executor/src + depends_on: + - order_queue + + order_executor_3: + build: + context: ./ + dockerfile: ./order_executor/Dockerfile + ports: + - "50057:50055" + environment: + - PYTHONUNBUFFERED=TRUE + - PYTHONFILE=/app/order_executor/src/app.py + - EXECUTOR_ID=3 + - EXECUTOR_PORT=50055 + - PEERS=1@order_executor_1:50055,2@order_executor_2:50055,3@order_executor_3:50055 volumes: - ./utils:/app/utils - ./order_executor/src:/app/order_executor/src diff --git a/order_executor/src/app.py b/order_executor/src/app.py index c3d529ca7..144086bfa 100644 --- a/order_executor/src/app.py +++ b/order_executor/src/app.py @@ -30,7 +30,7 @@ state_lock = threading.Lock() leader_id = None -last_heartbeat = 0.0 +last_heartbeat = time.time() is_leader = False election_in_progress = False @@ -50,14 +50,48 @@ def parse_peers(): PEERS = parse_peers() +def has_fresh_leader_locked(): + if leader_id is None: + return False + if is_leader and leader_id == EXECUTOR_ID: + return True + return (time.time() - last_heartbeat) <= LEADER_TIMEOUT + + +def announce_coordinator(): + for pid, addr in PEERS: + if pid == EXECUTOR_ID: + continue + send_rpc( + addr, + lambda stub: stub.Coordinator( + executor_pb2.CoordinatorRequest(leader_id=EXECUTOR_ID), + timeout=2.0, + ), + ) + + class ControlService(executor_grpc.OrderExecutorControlServicer): def Election(self, request, context): global election_in_progress - if EXECUTOR_ID > request.candidate_id: - print(f"[EXEC-{EXECUTOR_ID}] received election from {request.candidate_id}") + + if EXECUTOR_ID <= request.candidate_id: + return executor_pb2.ElectionResponse(alive=False) + + print(f"[EXEC-{EXECUTOR_ID}] received election from {request.candidate_id}") + + with state_lock: + already_leader = is_leader + election_running = election_in_progress + + # If I am already the leader, just re-announce myself instead of + # starting a brand new election. + if already_leader: + threading.Thread(target=announce_coordinator, daemon=True).start() + elif not election_running: threading.Thread(target=start_election, daemon=True).start() - return executor_pb2.ElectionResponse(alive=True) - return executor_pb2.ElectionResponse(alive=False) + + return executor_pb2.ElectionResponse(alive=True) def Coordinator(self, request, context): global leader_id, is_leader, election_in_progress, last_heartbeat @@ -89,10 +123,16 @@ def send_rpc(addr, fn): def start_election(): - global election_in_progress + global election_in_progress, leader_id + with state_lock: if election_in_progress: return + + # Do not start a new election if a healthy leader is already known. + if has_fresh_leader_locked(): + return + election_in_progress = True print(f"[EXEC-{EXECUTOR_ID}] starting election") @@ -113,14 +153,19 @@ def start_election(): if not got_answer: become_leader() - else: - time.sleep(LEADER_TIMEOUT) - with state_lock: - current_leader = leader_id - election_in_progress = False + return - if current_leader is None: - start_election() + # Wait for a higher node to announce a leader. + time.sleep(LEADER_TIMEOUT) + + with state_lock: + fresh_leader = has_fresh_leader_locked() + election_in_progress = False + + if not fresh_leader: + with state_lock: + leader_id = None + start_election() def become_leader(): @@ -132,24 +177,16 @@ def become_leader(): last_heartbeat = time.time() print(f"[EXEC-{EXECUTOR_ID}] became leader") - - for pid, addr in PEERS: - if pid == EXECUTOR_ID: - continue - send_rpc( - addr, - lambda stub: stub.Coordinator( - executor_pb2.CoordinatorRequest(leader_id=EXECUTOR_ID), - timeout=2.0, - ), - ) + announce_coordinator() def heartbeat_loop(): while True: time.sleep(HEARTBEAT_INTERVAL) + with state_lock: leader_now = is_leader + if not leader_now: continue @@ -167,12 +204,21 @@ def heartbeat_loop(): def timeout_loop(): global leader_id + while True: time.sleep(1.0) + with state_lock: - expired = (not is_leader) and ( - leader_id is None or (time.time() - last_heartbeat > LEADER_TIMEOUT) - ) + if is_leader or election_in_progress: + continue + + # During startup, if no leader is known yet, do not immediately + # treat that as a timeout storm. + if leader_id is None: + continue + + expired = (time.time() - last_heartbeat) > LEADER_TIMEOUT + if expired: print(f"[EXEC-{EXECUTOR_ID}] leader timeout detected") with state_lock: @@ -223,8 +269,14 @@ def serve(): threading.Thread(target=timeout_loop, daemon=True).start() threading.Thread(target=consume_loop, daemon=True).start() + # Give peers a brief moment to come up, then start election only if + # no leader is already known. time.sleep(1.0) - start_election() + with state_lock: + should_start = (leader_id is None) and (not election_in_progress) + + if should_start: + start_election() server.wait_for_termination() From 4d11f0258c6a610f58f5e959bdadeb34362db3d1 Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Sat, 4 Apr 2026 12:40:34 +0300 Subject: [PATCH 23/58] Fix checkpoint 2 clear broadcast and add verification script Implementation-based checks pass via scripts/checkpoint2-checks.ps1, including checkout scenarios and leader failover. Remaining checkpoint 2 work is documentation and diagrams. --- orchestrator/src/app.py | 37 ++- scripts/checkpoint2-checks.ps1 | 465 +++++++++++++++++++++++++++++++++ 2 files changed, 494 insertions(+), 8 deletions(-) create mode 100644 scripts/checkpoint2-checks.ps1 diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 395c1be25..6c16bf6bb 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -218,12 +218,29 @@ def clear_suggestions_service(order_id, final_vc): def broadcast_clear(order_id, final_vc): try: - clear_transaction_service(order_id, final_vc) - clear_fraud_service(order_id, final_vc) - clear_suggestions_service(order_id, final_vc) + clear_results = [ + ("transaction_verification", clear_transaction_service(order_id, final_vc)), + ("fraud_detection", clear_fraud_service(order_id, final_vc)), + ("suggestions", clear_suggestions_service(order_id, final_vc)), + ] + failed_services = [ + f"{service}: {response.message}" + for service, response in clear_results + if not response.success + ] + + if failed_services: + print( + f"[ORCH] order={order_id} clear_broadcast_warning=" + f"{'; '.join(failed_services)} final_vc={final_vc}" + ) + return False + print(f"[ORCH] order={order_id} clear_broadcast_sent final_vc={final_vc}") + return True except Exception as e: print(f"[ORCH] order={order_id} clear_broadcast_warning={e}") + return False @app.route("/", methods=["GET"]) @@ -347,7 +364,7 @@ def store_event_result(step, response): vc = list(response.vc.values) with lock: state["event_vcs"][step] = vc - state["final_vc"] = vc + state["final_vc"] = merge_vcs(state["final_vc"], vc) def merged_from(*steps): with lock: @@ -362,7 +379,9 @@ def record_event_failure(step, response): state["failure_kind"] = "event_failure" state["failed_step"] = step state["failure_message"] = response.message - state["final_vc"] = list(response.vc.values) + state["final_vc"] = merge_vcs( + state["final_vc"], list(response.vc.values) + ) cancelled.set() print(f"[ORCH] order={order_id} step={step} success=False message={response.message}") @@ -372,7 +391,7 @@ def record_internal_failure(step, message, fallback_vc): state["failure_kind"] = "internal_error" state["failed_step"] = step state["failure_message"] = message - state["final_vc"] = fallback_vc + state["final_vc"] = merge_vcs(state["final_vc"], fallback_vc) cancelled.set() print(f"[ORCH] order={order_id} step={step} internal_error={message}") @@ -456,7 +475,9 @@ def run_finalize(): print(f"[ORCH] order={order_id} all_worker_threads_finished") - final_vc = state["final_vc"] + with lock: + final_vc = merge_vcs(state["final_vc"], *state["event_vcs"].values()) + state["final_vc"] = final_vc if state["failure_kind"] == "internal_error": broadcast_clear(order_id, final_vc) @@ -508,4 +529,4 @@ def run_finalize(): if __name__ == "__main__": - app.run(host="0.0.0.0") \ No newline at end of file + app.run(host="0.0.0.0") diff --git a/scripts/checkpoint2-checks.ps1 b/scripts/checkpoint2-checks.ps1 new file mode 100644 index 000000000..e3228c658 --- /dev/null +++ b/scripts/checkpoint2-checks.ps1 @@ -0,0 +1,465 @@ +param( + [switch]$SkipBuild, + [switch]$SkipFailover +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$projectRoot = Split-Path -Parent $PSScriptRoot +Set-Location $projectRoot + +$apiUrl = "http://127.0.0.1:8081/checkout" +$executorServices = @("order_executor_1", "order_executor_2", "order_executor_3") +$logServices = @( + "orchestrator", + "transaction_verification", + "fraud_detection", + "suggestions", + "order_queue" +) + $executorServices +$pythonFiles = @( + "orchestrator/src/app.py", + "transaction_verification/src/app.py", + "fraud_detection/src/app.py", + "suggestions/src/app.py", + "order_queue/src/app.py", + "order_executor/src/app.py" +) +$results = [System.Collections.Generic.List[object]]::new() +$script:CurrentFailureList = $null + +function Write-Section { + param([string]$Message) + Write-Host "" + Write-Host "== $Message ==" +} + +function Add-CheckResult { + param( + [string]$Name, + [bool]$Passed, + [string]$Details + ) + + $results.Add([pscustomobject]@{ + Name = $Name + Passed = $Passed + Details = $Details + }) + + $status = if ($Passed) { "PASS" } else { "FAIL" } + Write-Host ("[{0}] {1} - {2}" -f $status, $Name, $Details) +} + +function Run-Compose { + param([string[]]$ComposeArgs) + + $output = & docker compose @ComposeArgs 2>&1 + $exitCode = $LASTEXITCODE + + return [pscustomobject]@{ + ExitCode = $exitCode + Output = ($output | Out-String).TrimEnd() + } +} + +function Get-ComposeLogs { + param( + [string[]]$Services, + [int]$Tail = 400, + [string]$Since + ) + + $args = @("logs", "--no-color") + if ($Since) { + $args += @("--since", $Since) + } + $args += "--tail=$Tail" + $args += $Services + + $result = Run-Compose $args + if ($result.ExitCode -ne 0) { + throw "docker compose logs failed.`n$($result.Output)" + } + + return $result.Output +} + +function Invoke-Checkout { + param([string]$FilePath) + + $body = Get-Content $FilePath -Raw + $response = Invoke-WebRequest ` + -Uri $apiUrl ` + -Method POST ` + -ContentType "application/json" ` + -Body $body + + return [pscustomobject]@{ + StatusCode = [int]$response.StatusCode + Json = ($response.Content | ConvertFrom-Json) + Raw = $response.Content + } +} + +function Wait-ForOrchestrator { + for ($attempt = 1; $attempt -le 30; $attempt++) { + try { + $response = Invoke-WebRequest ` + -Uri "http://127.0.0.1:8081/" ` + -Method GET + if ([int]$response.StatusCode -eq 200) { + return + } + } + catch { + } + + Start-Sleep -Seconds 2 + } + + throw "Orchestrator did not become ready on http://127.0.0.1:8081/." +} + +function Assert-Condition { + param( + [bool]$Condition, + [string]$Message + ) + + if (-not $Condition) { + if ($null -eq $script:CurrentFailureList) { + throw "Current failure list is not initialized." + } + $script:CurrentFailureList.Add($Message) + } +} + +function Get-OrderLogLines { + param( + [string]$Logs, + [string]$OrderId + ) + + $normalizedOrderId = $OrderId.Trim() + + return @( + ($Logs -split "\r?\n") | Where-Object { $_ -like "*$normalizedOrderId*" } + ) +} + +function Get-CurrentLeaderId { + $logs = Get-ComposeLogs -Services (@("order_queue") + $executorServices) -Tail 800 -Since "30m" + $dequeueMatch = [regex]::Matches($logs, "action=dequeue .* executor=(\d+)") + if ($dequeueMatch.Count -gt 0) { + return [int]$dequeueMatch[$dequeueMatch.Count - 1].Groups[1].Value + } + + $executionMatch = [regex]::Matches($logs, "\[EXEC-(\d+)\] leader=\d+ executing order=") + if ($executionMatch.Count -gt 0) { + return [int]$executionMatch[$executionMatch.Count - 1].Groups[1].Value + } + + $leaderMatch = [regex]::Matches($logs, "\[EXEC-(\d+)\] became leader") + if ($leaderMatch.Count -gt 0) { + return [int]$leaderMatch[$leaderMatch.Count - 1].Groups[1].Value + } + + $leaderAnnouncement = [regex]::Matches($logs, "new leader is (\d+)") + if ($leaderAnnouncement.Count -gt 0) { + return [int]$leaderAnnouncement[$leaderAnnouncement.Count - 1].Groups[1].Value + } + + throw "Could not determine the current leader from executor logs." +} + +function Wait-ForOrderLogs { + param( + [string]$OrderId, + [bool]$ShouldQueue, + [bool]$ShouldExecute, + [int]$TimeoutSeconds = 20 + ) + + $deadline = (Get-Date).AddSeconds($TimeoutSeconds) + $latestLogs = "" + + while ((Get-Date) -lt $deadline) { + $latestLogs = Get-ComposeLogs -Services $logServices -Tail 1200 -Since "30m" + $orderLines = Get-OrderLogLines -Logs $latestLogs -OrderId $OrderId + + $hasTvClear = @($orderLines | Where-Object { $_ -match "\[TV\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }).Count -ge 1 + $hasFdClear = @($orderLines | Where-Object { $_ -match "\[FD\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }).Count -ge 1 + $hasSugClear = @($orderLines | Where-Object { $_ -match "\[SUG\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }).Count -ge 1 + $hasOrchClear = @($orderLines | Where-Object { $_ -match "\[ORCH\]" -and $_ -match "clear_broadcast_sent" }).Count -ge 1 + $hasQueueEnqueue = @($orderLines | Where-Object { $_ -match "\[QUEUE\]" -and $_ -match "action=enqueue" }).Count -ge 1 + $hasQueueDequeue = @($orderLines | Where-Object { $_ -match "\[QUEUE\]" -and $_ -match "action=dequeue" }).Count -ge 1 + $hasExecution = @($orderLines | Where-Object { $_ -match "\[EXEC-" -and $_ -match "executing order=" }).Count -ge 1 + + $ready = $hasTvClear -and $hasFdClear -and $hasSugClear -and $hasOrchClear + if ($ShouldQueue) { + $ready = $ready -and $hasQueueEnqueue -and $hasQueueDequeue + } + if ($ShouldExecute) { + $ready = $ready -and $hasExecution + } + + if ($ready) { + return $latestLogs + } + + Start-Sleep -Seconds 2 + } + + return $latestLogs +} + +function Test-CheckoutCase { + param( + [string]$Name, + [string]$FilePath, + [string]$ExpectedStatus, + [string]$ExpectedReason, + [bool]$ShouldQueue, + [bool]$ShouldExecute, + [bool]$RequireAllEventLogs + ) + + $failures = [System.Collections.Generic.List[string]]::new() + $script:CurrentFailureList = $failures + $response = Invoke-Checkout -FilePath $FilePath + + $orderId = ([string]$response.Json.orderId).Trim() + $logs = Wait-ForOrderLogs ` + -OrderId $orderId ` + -ShouldQueue $ShouldQueue ` + -ShouldExecute $ShouldExecute + $orderLines = Get-OrderLogLines -Logs $logs -OrderId $orderId + $tvClear = @($orderLines | Where-Object { $_ -match "\[TV\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }) + $fdClear = @($orderLines | Where-Object { $_ -match "\[FD\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }) + $sugClear = @($orderLines | Where-Object { $_ -match "\[SUG\]" -and $_ -match "event=ClearOrder" -and $_ -match "success=True" }) + $queueEnqueue = @($orderLines | Where-Object { $_ -match "\[QUEUE\]" -and $_ -match "action=enqueue" }) + $queueDequeue = @($orderLines | Where-Object { $_ -match "\[QUEUE\]" -and $_ -match "action=dequeue" }) + $executionLines = @($orderLines | Where-Object { $_ -match "\[EXEC-" -and $_ -match "executing order=" }) + $warningLines = @($orderLines | Where-Object { $_ -match "clear_broadcast_warning" }) + + Assert-Condition ($response.StatusCode -eq 200) "Expected HTTP 200 but got $($response.StatusCode)." + Assert-Condition ($ExpectedStatus -eq [string]$response.Json.status) "Expected status '$ExpectedStatus' but got '$($response.Json.status)'." + Assert-Condition ([string]::IsNullOrEmpty($orderId) -eq $false) "Response did not include an orderId." + Assert-Condition ($warningLines.Count -eq 0) "Found clear_broadcast_warning log lines for order $orderId." + Assert-Condition ($tvClear.Count -ge 1) "Transaction verification did not clear order $orderId successfully." + Assert-Condition ($fdClear.Count -ge 1) "Fraud detection did not clear order $orderId successfully." + Assert-Condition ($sugClear.Count -ge 1) "Suggestions did not clear order $orderId successfully." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[ORCH\]" -and $_ -match "initialization_complete" }).Count -ge 1) "Orchestrator initialization log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[ORCH\]" -and $_ -match "clear_broadcast_sent" }).Count -ge 1) "Clear broadcast log is missing for order $orderId." + + if ($ExpectedReason) { + Assert-Condition ($ExpectedReason -eq [string]$response.Json.reason) "Expected reason '$ExpectedReason' but got '$($response.Json.reason)'." + } + + if ($ShouldQueue) { + Assert-Condition ($queueEnqueue.Count -ge 1) "Queue enqueue log is missing for order $orderId." + Assert-Condition ($queueDequeue.Count -ge 1) "Queue dequeue log is missing for order $orderId." + } + else { + Assert-Condition ($queueEnqueue.Count -eq 0) "Order $orderId should not have been enqueued." + Assert-Condition ($queueDequeue.Count -eq 0) "Order $orderId should not have been dequeued." + } + + if ($ShouldExecute) { + Assert-Condition ($executionLines.Count -ge 1) "No executor executed order $orderId." + } + else { + Assert-Condition ($executionLines.Count -eq 0) "A replica executed rejected order $orderId." + } + + if ($RequireAllEventLogs) { + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[TV\]" -and $_ -match "event=ValidateItems" -and $_ -match "vc=\[" }).Count -ge 1) "ValidateItems VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[TV\]" -and $_ -match "event=ValidateUserData" -and $_ -match "vc=\[" }).Count -ge 1) "ValidateUserData VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[TV\]" -and $_ -match "event=ValidateCardFormat" -and $_ -match "vc=\[" }).Count -ge 1) "ValidateCardFormat VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[FD\]" -and $_ -match "event=CheckUserFraud" -and $_ -match "vc=\[" }).Count -ge 1) "CheckUserFraud VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[FD\]" -and $_ -match "event=CheckCardFraud" -and $_ -match "vc=\[" }).Count -ge 1) "CheckCardFraud VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[SUG\]" -and $_ -match "event=PrecomputeSuggestions" -and $_ -match "vc=\[" }).Count -ge 1) "PrecomputeSuggestions VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[SUG\]" -and $_ -match "event=FinalizeSuggestions" -and $_ -match "vc=\[" }).Count -ge 1) "FinalizeSuggestions VC log is missing for order $orderId." + Assert-Condition (@($orderLines | Where-Object { $_ -match "\[ORCH\]" -and $_ -match "final_status=APPROVED" }).Count -ge 1) "Final approval log is missing for order $orderId." + } + + $passed = $failures.Count -eq 0 + $details = if ($passed) { + "orderId=$orderId status=$($response.Json.status)" + } + else { + ($failures -join " ") + } + + Add-CheckResult -Name "checkout:$Name" -Passed $passed -Details $details + $script:CurrentFailureList = $null + + return [pscustomobject]@{ + Passed = $passed + OrderId = $orderId + Response = $response + } +} + +function Test-LeaderFailover { + $failures = [System.Collections.Generic.List[string]]::new() + $script:CurrentFailureList = $failures + $leaderId = Get-CurrentLeaderId + $leaderService = "order_executor_$leaderId" + + Write-Host "Stopping current leader service $leaderService to test failover..." + + try { + $stopResult = Run-Compose @("stop", $leaderService) + if ($stopResult.ExitCode -ne 0) { + throw "Failed to stop $leaderService.`n$($stopResult.Output)" + } + + Start-Sleep -Seconds 8 + $failoverLogs = Get-ComposeLogs -Services $executorServices -Tail 250 -Since "30s" + Assert-Condition ($failoverLogs -match "leader timeout detected") "No leader timeout was detected after stopping $leaderService." + Assert-Condition ($failoverLogs -match "became leader") "No replacement leader was elected after stopping $leaderService." + + $response = Invoke-Checkout -FilePath "test_checkout.json" + + $orderId = ([string]$response.Json.orderId).Trim() + $logs = Wait-ForOrderLogs ` + -OrderId $orderId ` + -ShouldQueue $true ` + -ShouldExecute $true ` + -TimeoutSeconds 25 + $orderLines = Get-OrderLogLines -Logs $logs -OrderId $orderId + $executionLines = @($orderLines | Where-Object { $_ -match "\[EXEC-" -and $_ -match "executing order=" }) + $dequeueLines = @($orderLines | Where-Object { $_ -match "\[QUEUE\]" -and $_ -match "action=dequeue" }) + $stoppedExecutorExecution = @($executionLines | Where-Object { $_ -match "\[EXEC-$leaderId\]" }) + $stoppedExecutorDequeue = @($dequeueLines | Where-Object { $_ -match "executor=$leaderId" }) + + Assert-Condition ($response.StatusCode -eq 200) "Failover checkout returned HTTP $($response.StatusCode)." + Assert-Condition ("Order Approved" -eq [string]$response.Json.status) "Failover checkout was not approved." + Assert-Condition ($executionLines.Count -ge 1) "No executor executed the failover test order $orderId." + Assert-Condition ($dequeueLines.Count -ge 1) "Queue did not dequeue the failover test order $orderId." + Assert-Condition ($stoppedExecutorExecution.Count -eq 0) "Stopped leader executor $leaderId executed order $orderId." + Assert-Condition ($stoppedExecutorDequeue.Count -eq 0) "Stopped leader executor $leaderId dequeued order $orderId." + } + finally { + Write-Host "Restoring $leaderService..." + $restoreResult = Run-Compose @("up", "-d", $leaderService) + if ($restoreResult.ExitCode -ne 0) { + Add-CheckResult -Name "failover:restore" -Passed $false -Details $restoreResult.Output + } + else { + Start-Sleep -Seconds 4 + } + } + + $passed = $failures.Count -eq 0 + $details = if ($passed) { + "Failover succeeded after stopping $leaderService." + } + else { + ($failures -join " ") + } + + Add-CheckResult -Name "leader-failover" -Passed $passed -Details $details + $script:CurrentFailureList = $null +} + +Write-Section "Environment" + +$dockerVersion = & docker --version +Add-CheckResult -Name "docker" -Passed ($LASTEXITCODE -eq 0) -Details $dockerVersion + +$composeVersion = & docker compose version +Add-CheckResult -Name "docker-compose" -Passed ($LASTEXITCODE -eq 0) -Details $composeVersion + +$configResult = Run-Compose @("config") +Add-CheckResult -Name "compose-config" -Passed ($configResult.ExitCode -eq 0) -Details "docker compose config exited with code $($configResult.ExitCode)." + +Write-Section "Startup" + +if ($SkipBuild) { + $upResult = Run-Compose @("up", "-d") + Add-CheckResult -Name "compose-up" -Passed ($upResult.ExitCode -eq 0) -Details "Started stack without rebuild." +} +else { + $upResult = Run-Compose @("up", "--build", "-d") + Add-CheckResult -Name "compose-up" -Passed ($upResult.ExitCode -eq 0) -Details "Started stack with rebuild." +} + +Wait-ForOrchestrator +Add-CheckResult -Name "orchestrator-ready" -Passed $true -Details "HTTP endpoint is reachable." + +$psResult = Run-Compose @("ps") +Add-CheckResult -Name "compose-ps" -Passed ($psResult.ExitCode -eq 0) -Details "docker compose ps completed." + +Write-Section "Syntax" + +foreach ($path in $pythonFiles) { + python -m py_compile $path + Add-CheckResult -Name "py-compile:$path" -Passed ($LASTEXITCODE -eq 0) -Details "Syntax OK." +} + +Write-Section "Checkout Scenarios" + +Test-CheckoutCase ` + -Name "valid" ` + -FilePath "test_checkout.json" ` + -ExpectedStatus "Order Approved" ` + -ExpectedReason "" ` + -ShouldQueue $true ` + -ShouldExecute $true ` + -RequireAllEventLogs $true | Out-Null + +Test-CheckoutCase ` + -Name "fraud" ` + -FilePath "test_checkout_fraud.json" ` + -ExpectedStatus "Order Rejected" ` + -ExpectedReason "Suspicious card number pattern." ` + -ShouldQueue $false ` + -ShouldExecute $false ` + -RequireAllEventLogs $false | Out-Null + +Test-CheckoutCase ` + -Name "empty-items" ` + -FilePath "test_checkout_empty_items.json" ` + -ExpectedStatus "Order Rejected" ` + -ExpectedReason "No items in order." ` + -ShouldQueue $false ` + -ShouldExecute $false ` + -RequireAllEventLogs $false | Out-Null + +Test-CheckoutCase ` + -Name "terms-false" ` + -FilePath "test_checkout_terms_false.json" ` + -ExpectedStatus "Order Rejected" ` + -ExpectedReason "Terms and conditions not accepted." ` + -ShouldQueue $false ` + -ShouldExecute $false ` + -RequireAllEventLogs $false | Out-Null + +if (-not $SkipFailover) { + Write-Section "Failover" + Test-LeaderFailover +} + +Write-Section "Summary" + +$passedCount = @($results | Where-Object { $_.Passed }).Count +$failedCount = @($results | Where-Object { -not $_.Passed }).Count + +foreach ($result in $results) { + $status = if ($result.Passed) { "PASS" } else { "FAIL" } + Write-Host ("{0} {1}" -f $status, $result.Name) +} + +Write-Host "" +Write-Host ("Passed: {0}" -f $passedCount) +Write-Host ("Failed: {0}" -f $failedCount) + +if ($failedCount -gt 0) { + exit 1 +} + +exit 0 From e4fb60a854cfe1f751424dad3cd5d88569d4c791 Mon Sep 17 00:00:00 2001 From: Sten-Qy-Li Date: Sat, 4 Apr 2026 12:58:59 +0300 Subject: [PATCH 24/58] Add Checkpoint 2 documentation and diagrams --- README.md | 215 +++++++++++++++++++++++++----- docs/README.md | 6 +- docs/diagrams/leader-election.svg | 116 ++++++++++++++++ docs/diagrams/vector-clocks.svg | 131 ++++++++++++++++++ 4 files changed, 431 insertions(+), 37 deletions(-) create mode 100644 docs/diagrams/leader-election.svg create mode 100644 docs/diagrams/vector-clocks.svg diff --git a/README.md b/README.md index e34340b5a..a360d88cc 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,201 @@ -# Distributed Systems @ University of Tartu +# Distributed Systems Practice Project - Checkpoint 2 -This repository contains the initial code for the practice sessions of the Distributed Systems course at the University of Tartu. +## How to demonstrate that this repository works +This section is intentionally first so it can be used as a short live-demo checklist. -## Getting started +1. Start Docker Desktop, then start the full stack from the repository root. -### Overview - -The code consists of multiple services. Each service is located in a separate folder. The `frontend` service folder contains a Dockerfile and the code for an example bookstore application. Each backend service folder (e.g. `orchestrator` or `fraud_detection`) contains a Dockerfile, a requirements.txt file and the source code of the service. During the practice sessions, you will implement the missing functionality in these backend services, or extend the backend with new services. - -There is also a `utils` folder that contains some helper code or specifications that are used by multiple services. Check the `utils` folder for more information. +```powershell +docker compose up --build -d +docker compose ps +``` -### Architecture +Expected result: all 9 services are up (`frontend`, `orchestrator`, the 3 backend services, `order_queue`, and the 3 executor replicas). -The following diagrams give a high-level view of the system and how the services interact. +2. Run the reusable Checkpoint 2 verification script. -**System overview** +```powershell +.\scripts\checkpoint2-checks.ps1 +``` -![System diagram](./system_diagram.png) +After the first full build, the quicker rerun is: -**Service architecture** +```powershell +.\scripts\checkpoint2-checks.ps1 -SkipBuild +``` -![Service architecture diagram](./architecture_diagram.png) +Expected result: all implementation-based checks pass, including valid checkout, rejection cases, vector-clock log checks, queue/executor checks, and leader failover. -### Running the code with Docker Compose [recommended] +3. If the teaching assistants want a manual happy-path demo, open the frontend at `http://127.0.0.1:8080` and submit a normal order. The REST API is also available at `http://127.0.0.1:8081`. -To run the code, you need to clone this repository, make sure you have Docker and Docker Compose installed, and run the following command in the root folder of the repository: +4. If they want manual API testing, use the prepared payload files in the repo: -```bash -docker compose up +```powershell +Invoke-WebRequest ` + -Uri http://127.0.0.1:8081/checkout ` + -Method POST ` + -ContentType "application/json" ` + -Body (Get-Content .\test_checkout.json -Raw) ``` -This will start the system with the multiple services. Each service will be restarted automatically when you make changes to the code, so you don't have to restart the system manually while developing. If you want to know how the services are started and configured, check the `docker-compose.yaml` file. +Swap in `test_checkout_fraud.json`, `test_checkout_empty_items.json`, and `test_checkout_terms_false.json` to show rejection paths. -The checkpoint evaluations will be done using the code that is started with Docker Compose, so make sure that your code works with Docker Compose. +5. Show the logs that prove the distributed behavior: -If, for some reason, changes to the code are not reflected, try to force rebuilding the Docker images with the following command: - -```bash -docker compose up --build +```powershell +docker compose logs --no-color --tail 200 orchestrator transaction_verification fraud_detection suggestions +docker compose logs --no-color --tail 200 order_queue order_executor_1 order_executor_2 order_executor_3 ``` -### Run the code locally - -Even though you can run the code locally, it is recommended to use Docker and Docker Compose to run the code. This way you don't have to install any dependencies locally and you can easily run the code on any platform. +Point out: +- `vc=[...]` in the 3 backend services +- `clear_broadcast_sent final_vc=[...]` in the orchestrator +- `action=enqueue` and `action=dequeue` in the queue +- `executing order=` in exactly one executor replica + +6. Show the leader-election bonus by either rerunning the script or doing a quick manual failover: + +```powershell +docker compose stop order_executor_3 +Invoke-WebRequest ` + -Uri http://127.0.0.1:8081/checkout ` + -Method POST ` + -ContentType "application/json" ` + -Body (Get-Content .\test_checkout.json -Raw) +docker compose logs --no-color --since 30s order_queue order_executor_1 order_executor_2 order_executor_3 +docker compose up -d order_executor_3 +``` -If you want to run the code locally, you need to install the following dependencies: +Expected result: another executor becomes leader after timeout, dequeues the next approved order, and execution still happens exactly once. -backend services: -- Python 3.8 or newer -- pip -- [grpcio-tools](https://grpc.io/docs/languages/python/quickstart/) -- requirements.txt dependencies from each service +7. Stop the stack when the demo is over. -frontend service: -- It's a simple static HTML page, you can open `frontend/src/index.html` in your browser. +```powershell +docker compose down +``` -And then run each service individually. +## Checkpoint 2 deliverables in this repo +This repository now contains the implementation and documentation required for Checkpoint 2: + +- vector clocks across `transaction_verification`, `fraud_detection`, and `suggestions` +- order queuing plus 3 replicated order executors +- leader election and mutual exclusion for queue consumption +- logs that expose vector-clock values, queue actions, and executor leadership +- a reusable verification script at `scripts/checkpoint2-checks.ps1` +- the required vector-clocks diagram, leader-election diagram, and system-model write-up + +## Vector clocks +The vector clock has 3 positions in the fixed service order `[TV, FD, SUG]`. + +Each backend service: +- stores per-order state after `InitOrder` +- merges the incoming vector clock with its local vector clock +- increments its own component before logging and replying +- clears the order only if `local_vc <= final_vc` + +The diagram below shows one successful execution observed in this repository. The orchestrator starts `ValidateItems` and `ValidateUserData` together, so their relative order may swap between runs. The diagram documents one valid run captured from the logs. + +![Vector clocks diagram](./docs/diagrams/vector-clocks.svg) + +Observed successful event sequence: + +| Step | Service | Event | Vector clock | +| --- | --- | --- | --- | +| 1 | Transaction verification | `ValidateUserData` | `[1, 0, 0]` | +| 2 | Transaction verification | `ValidateItems` | `[2, 0, 0]` | +| 3 | Fraud detection | `CheckUserFraud` | `[1, 1, 0]` | +| 4 | Suggestions | `PrecomputeSuggestions` | `[2, 0, 1]` | +| 5 | Transaction verification | `ValidateCardFormat` | `[3, 0, 0]` | +| 6 | Fraud detection | `CheckCardFraud` | `[3, 2, 0]` | +| 7 | Suggestions | `FinalizeSuggestions` | `[3, 2, 2]` | + +Bonus behavior: +- the orchestrator merges all completed event clocks into one `final_vc` +- the orchestrator broadcasts `ClearOrder(final_vc)` to all 3 services +- each service clears only when its local vector clock is not ahead of the final one + +## Leader election and mutual exclusion +The order execution tier uses 3 replicas: `order_executor_1`, `order_executor_2`, and `order_executor_3`. + +The implementation follows a bully-style pattern: +- a replica starts an election only if no healthy leader is known +- a replica contacts only higher-numbered peers during election +- the highest live executor becomes leader and announces itself +- the leader sends heartbeats +- followers start a new election if the leader times out +- only the current leader dequeues from `order_queue` + +![Leader election diagram](./docs/diagrams/leader-election.svg) + +Why this satisfies the checkpoint requirements: +- leader election is visible in logs through `starting election`, `became leader`, and `new leader is ...` +- mutual exclusion is enforced because only the leader calls `Dequeue` +- the failover path is demonstrable with 3 replicas by stopping the current leader and submitting another valid order + +## System model +### Architecture +The system is a small distributed online-bookstore workflow: + +- `frontend` serves the browser UI +- `orchestrator` accepts checkout requests over HTTP and coordinates the workflow +- `transaction_verification`, `fraud_detection`, and `suggestions` are gRPC services that participate in the vector-clock event flow +- `order_queue` stores approved orders in FIFO order +- `order_executor_1..3` form a replicated execution tier that elects a leader and consumes approved orders + +### Communication model +- the browser communicates with the orchestrator over HTTP +- the orchestrator communicates with backend services over synchronous gRPC calls +- executor replicas communicate with each other over gRPC for election, coordinator announcements, and heartbeats +- the order queue is a separate gRPC service used by the orchestrator and the current leader +- all services run in Docker Compose on one virtual network, but they still behave as separate processes with separate local state + +### Concurrency and ordering +- the orchestrator starts multiple validation/fraud/suggestion steps in parallel threads +- there is no global clock +- ordering is captured by vector clocks rather than wall-clock timestamps +- approval requires the full dependency chain to complete successfully +- queue consumption is serialized by leadership: only one replica is allowed to dequeue at a time + +### Failure assumptions +- the executor layer assumes crash-stop failures, not Byzantine behavior +- a failed leader is detected through missing heartbeats +- after timeout, surviving replicas re-run election and the highest live replica becomes leader +- backend service state for vector clocks is kept in memory per order, so restarting a container loses that in-memory state +- the queue is also in-memory, so queued orders are not durable across queue restarts + +### Safety properties +- every order gets a unique `orderId` from the orchestrator +- vector-clock logs expose causal relationships between backend events +- approved orders are enqueued once by the orchestrator +- only the elected leader dequeues and executes an approved order +- the clear broadcast uses the merged final vector clock so services do not clear too early + +### Known limitations +- there is no persistent database yet +- the queue and service caches are process-local memory only +- the frontend and orchestrator are single-instance services +- retries and network partitions are not handled beyond the simple crash-stop assumptions needed for this checkpoint + +## Logs and verification +The reusable verification script is `scripts/checkpoint2-checks.ps1`. + +It checks: +- Docker and Docker Compose availability +- Compose startup +- Python syntax for all backend services +- one valid checkout +- three rejection scenarios +- vector-clock log presence +- queue enqueue and dequeue behavior +- leader failover and executor recovery + +Prepared input files: +- `test_checkout.json` +- `test_checkout_fraud.json` +- `test_checkout_empty_items.json` +- `test_checkout_terms_false.json` + +The required documentation assets are also available in `docs/`: +- `docs/diagrams/vector-clocks.svg` +- `docs/diagrams/leader-election.svg` +- `docs/README.md` diff --git a/docs/README.md b/docs/README.md index 75ae1828a..c67a1b818 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,7 @@ # Documentation -This folder should contain your documentation, explaining the structure and content of your project. It should also contain your diagrams, explaining the architecture. The recommended writing format is Markdown. +This folder contains the Checkpoint 2 documentation assets referenced from the repository root `README.md`. + +- [Vector clocks diagram](./diagrams/vector-clocks.svg) +- [Leader election diagram](./diagrams/leader-election.svg) +- [Main Checkpoint 2 write-up](../README.md) diff --git a/docs/diagrams/leader-election.svg b/docs/diagrams/leader-election.svg new file mode 100644 index 000000000..46a53d1ae --- /dev/null +++ b/docs/diagrams/leader-election.svg @@ -0,0 +1,116 @@ + + Leader election diagram for the order executor replicas + Sequence-style diagram showing a bully-style leader election at startup and failover after executor 3 stops, with only the leader dequeuing from the order queue. + + + + + + + + + + Leader election and single-consumer execution + The executor replicas use a bully-style election. Only the current leader is allowed to dequeue from the order queue. + + + + + + + Executor 1 + Executor 2 + Executor 3 + Order Queue + + + + + + + Phase 1: startup election + + + Election(1) + + + Election(1) + + + Election(2) + + + Executor 3 becomes leader + highest live id wins + + + Coordinator(3) + + + Coordinator(3) + + + + heartbeats every 2s + + + leader dequeues approved order + + + Phase 2: failover after executor 3 stops + + + Executor 3 stops + followers stop receiving heartbeats + + + + + Timeout detected + Executor 1 starts election + + + Election(1) + + + Executor 2 becomes leader + no higher live peer answers + Coordinator(2) follows + + + Coordinator(2) + + + new leader dequeues the next approved order + + + Mutual exclusion + Only the leader calls `Dequeue`. + That is why one approved order + is executed only once. + + + Failure assumption + Crash-stop failures, 5s timeout, + and simple heartbeat-based recovery. + diff --git a/docs/diagrams/vector-clocks.svg b/docs/diagrams/vector-clocks.svg new file mode 100644 index 000000000..6c005550e --- /dev/null +++ b/docs/diagrams/vector-clocks.svg @@ -0,0 +1,131 @@ + + Vector clocks diagram for a successful checkout + Sequence-style diagram showing one successful run across orchestrator, transaction verification, fraud detection, and suggestions, with vector clock values in the order [TV, FD, SUG]. + + + + + + + + + + Vector clocks for one successful checkout + Vector order is [TV, FD, SUG]. The diagram shows one real successful run observed from this repository's logs. + + + + + + + Orchestrator + Transaction Verification + Fraud Detection + Suggestions + + + + + + + + Create order and init services + `InitOrder` sent to TV, FD, SUG + + + InitOrder + vc = [0, 0, 0] + + + InitOrder + vc = [0, 0, 0] + + + InitOrder + vc = [0, 0, 0] + + + + + + + ValidateUserData + vc = [1, 0, 0] + + + ValidateItems + vc = [2, 0, 0] + + + CheckUserFraud + vc = [1, 1, 0] + + + PrecomputeSuggestions + vc = [2, 0, 1] + + + ValidateCardFormat + vc = [3, 0, 0] + + + CheckCardFraud + vc = [3, 2, 0] + + + FinalizeSuggestions + vc = [3, 2, 2] + + + Merge all event clocks + final_vc = [3, 2, 2] + + + ClearOrder OK + local_vc [3, 0, 0] <= final_vc + + + ClearOrder OK + local_vc [3, 2, 0] <= final_vc + + + ClearOrder OK + local_vc [3, 2, 2] <= final_vc + + + + + + + + + + + + + + + + + Bonus clear step: orchestrator broadcasts one merged final_vc. + From 49ced61c370dcfede95d784d1d545dff9ffe5744 Mon Sep 17 00:00:00 2001 From: anupkumar Date: Sun, 5 Apr 2026 12:36:26 +0300 Subject: [PATCH 25/58] code refactoring --- .idea/.gitignore | 5 +++++ .idea/ds-practice-2026.iml | 12 ++++++++++++ .idea/inspectionProfiles/profiles_settings.xml | 6 ++++++ .idea/misc.xml | 7 +++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ orchestrator/src/app.py | 10 +++++----- 7 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/ds-practice-2026.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..b58b603fe --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/ds-practice-2026.iml b/.idea/ds-practice-2026.iml new file mode 100644 index 000000000..460d4026f --- /dev/null +++ b/.idea/ds-practice-2026.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..105ce2da2 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..1d3ce46ba --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..8ce804af9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/orchestrator/src/app.py b/orchestrator/src/app.py index 6c16bf6bb..46f75834b 100644 --- a/orchestrator/src/app.py +++ b/orchestrator/src/app.py @@ -491,11 +491,11 @@ def run_finalize(): if state["failure_kind"] == "event_failure": broadcast_clear(order_id, final_vc) return { - "orderId": order_id, - "status": "Order Rejected", - "suggestedBooks": [], - "reason": state["failure_message"], - }, 200 + "error": { + "code": "ORDER_REJECTED", + "message": state["failure_message"], + } + }, 400 try: enqueue_response = enqueue_order(order_id, order_kwargs) From b38b29c237b2feb7e524f753b605e05c6714125a Mon Sep 17 00:00:00 2001 From: anupkumar Date: Mon, 6 Apr 2026 10:50:37 +0300 Subject: [PATCH 26/58] code refactoring and updated system models --- README.md | 7 +++++++ architecture_diagram.png | Bin 138538 -> 0 bytes docs/diagrams/architecture-diagram.jpg | Bin 0 -> 178306 bytes docs/diagrams/system-flow-diagram.jpg | Bin 0 -> 134834 bytes system_diagram.png | Bin 239025 -> 0 bytes 5 files changed, 7 insertions(+) delete mode 100644 architecture_diagram.png create mode 100644 docs/diagrams/architecture-diagram.jpg create mode 100644 docs/diagrams/system-flow-diagram.jpg delete mode 100644 system_diagram.png diff --git a/README.md b/README.md index a360d88cc..7da4b2c2f 100644 --- a/README.md +++ b/README.md @@ -136,12 +136,19 @@ Why this satisfies the checkpoint requirements: ### Architecture The system is a small distributed online-bookstore workflow: +![Architecture diagram](./docs/diagrams/architecture-diagram.jpg) + - `frontend` serves the browser UI - `orchestrator` accepts checkout requests over HTTP and coordinates the workflow - `transaction_verification`, `fraud_detection`, and `suggestions` are gRPC services that participate in the vector-clock event flow - `order_queue` stores approved orders in FIFO order - `order_executor_1..3` form a replicated execution tier that elects a leader and consumes approved orders +### System flow +The following diagram shows the end-to-end flow of an order through the system: + +![System flow diagram](./docs/diagrams/system-flow-diagram.jpg) + ### Communication model - the browser communicates with the orchestrator over HTTP - the orchestrator communicates with backend services over synchronous gRPC calls diff --git a/architecture_diagram.png b/architecture_diagram.png deleted file mode 100644 index 2071d8908c5e0f672577a734431fedeccd67b7ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138538 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