Browse Source

initial commit

aguryev 3 years ago
commit
665483d057

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+lvenv
+venv
+logs
+migrations
+__pycache__
+
+
+*.pyc
+*.db
+*.log

+ 110 - 0
antrag.py

@@ -0,0 +1,110 @@
+#
+# Sample Antrag class definition
+#
+
+
+from polzybackend.mediators import Antrag
+from pms.fast_offer import get_fast_offer, get_auto_brands
+from functools import reduce
+
+
+class SampleAntrag(Antrag):
+    def initialize(self):
+        #
+        # initializes antrag instance within Policy Management System 
+        #
+        result = get_fast_offer(self.product_name)
+        if result:
+            # expand result 
+            result.update({
+                'id': self.id,
+                'status': 'Neu',
+                'product_line': {
+                    'name': self.product_name,
+                    'attributes': {
+                        'Produkt': "Auto Insuarance",
+                    }
+                }
+            })
+            self.instance = result
+            return
+
+        raise Exception(f'No offer available for product {self.product_name}')
+
+
+    def get(self):
+        #
+        # returns antrag instance as json object to front-end 
+        #
+        return self.instance
+
+        
+
+    def get_field_by_name(self, name):
+        #
+        # find antrag field by name
+        #
+
+        get_field = lambda field_list: next(filter(lambda field: field['name'] == name, field_list), None)
+
+        # build list of field groups
+        field_sets = reduce(
+            lambda result, group: [*result, group['name']],
+            self.instance['field_groups'],
+            ['fields', 'field_groups'],
+        )
+
+        # search for field
+        for fs in field_sets:
+            target_field = get_field(self.instance[fs])
+            if target_field:
+                return target_field
+
+    def update_auto_brands(self):
+        #
+        # update auto brands based on vehicle type 
+        #
+
+        if self.instance['product_line'].get('name') == 'Automobile':
+            vehicle_type = self.get_field_by_name('VehicleType').get('valueChosenOrEntered')
+            brand_field = self.get_field_by_name('Brand')
+            brand_field['inputRange'] = get_auto_brands(vehicle_type)
+            if not brand_field['valueChosenOrEntered'] in brand_field['inputRange']:
+                brand_field['valueChosenOrEntered'] = brand_field['inputRange'][0]
+ 
+
+    def updateFields(self, data):
+        #
+        # updates antrag fields based on data
+        #
+
+        booleans = {
+            True: "True",
+            False: "False",
+        }
+
+        for key, value in data['values'].items():
+            field = self.get_field_by_name(key)
+            if field:
+                field['valueChosenOrEntered'] = booleans.get(value) or value
+
+        self.update_auto_brands()
+
+
+    def executeActivity(self, data):
+        #
+        # executes antrag activity defined in data
+        #
+
+        # update fields
+        self.updateFields(data)
+
+        # execute activity
+        if data['activity'] == 'Berechnen':
+            premium = self.get_field_by_name('premium')
+            premium['valueChosenOrEntered'] = 1347
+            self.instance['status'] = 'Calculated'
+            return self.get()
+
+
+        raise Exception(f'Logic for activity {data["activity"]} is not defined')

+ 27 - 0
app.py

@@ -0,0 +1,27 @@
+from polzybackend import create_app
+import os
+#import flask_monitoringdashboard as dashboard
+
+class Config(object):
+
+    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'polzy.db')
+    METHOD_GET_STAGES = 'pms.Stages'
+    CLASSNAME_PRODUCTS = 'pms.fast_offer.Products'
+    CLASSNAME_POLICY = 'policy.SamplePolicy'
+    CLASSNAME_ANTRAG = 'antrag.SampleAntrag'
+
+    HOST = 'http://localhost:5000'
+
+    # flask dashboard
+    DASHBOARD_CONFIG = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'dashboard.cfg')
+    DASHBOARD_DATABASE_URI = 'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'dashboard.db')
+    
+    DEBUG = True
+
+
+app = create_app(Config)
+
+
+
+from clauses import bp
+app.register_blueprint(bp)

+ 5 - 0
clauses/__init__.py

@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+bp = Blueprint('clauses', __name__)
+
+from . import routes

+ 20 - 0
clauses/routes.py

@@ -0,0 +1,20 @@
+from flask import render_template_string
+from . import bp
+
+@bp.route('/clauses/<string:clause_id>')
+def downloads(clause_id):
+	#
+	# sample end-point for clauses
+	#
+    #return f'This page is related to clause {clause_id}'
+
+    return render_template_string(f'''<!doctype html>
+<html>
+    <head>
+        <title>Clause {clause_id}</title>
+    </head>
+    <body>
+        <h1 style="text-align: center;">This page is related to clause {clause_id}</h1>
+    </body>
+</html>
+''')

+ 17 - 0
dashboard.cfg

@@ -0,0 +1,17 @@
+[dashboard]
+APP_VERSION=1.0
+CUSTOM_LINK=dashboard
+MONITOR_LEVEL=3
+OUTLIER_DETECTION_CONSTANT=2.5
+SAMPLING_PERIOD=20
+ENABLE_LOGGING=True
+
+[authentication]
+USERNAME=admin
+PASSWORD=admin
+SECURITY_TOKEN=cc83733cb0af8b884ff6577086b87909
+
+[visualization]
+TIMEZONE=UTC
+COLORS={'main':'[0,97,255]',
+        'static':'[255,153,0]'}

+ 0 - 0
models/__init__.py


+ 28 - 0
models/cancel_activity.py

@@ -0,0 +1,28 @@
+from polzybackend import db
+from polzybackend.auth import get_uuid
+from datetime import datetime, date
+
+
+class CancelActivity(db.Model):
+    __tablename__ = 'cancelActivities'
+    id = db.Column(db.LargeBinary, primary_key=True, default=get_uuid)
+    creator_id = db.Column(db.LargeBinary, db.ForeignKey('users.id'), nullable=False)
+    created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
+    policy_number = db.Column(db.String(64), nullable=False)
+    effective_date = db.Column(db.Date, nullable=False, default=date.today)
+    type_id = db.Column(db.Integer, db.ForeignKey('activity_types.id'), nullable=False)
+    is_finished = db.Column(db.Boolean, nullable=False, default=False)
+    
+    # relationships
+    creator = db.relationship(
+        'User',
+        backref=db.backref('created_activities', order_by='desc(Activity.created)'),
+        foreign_keys=[creator_id],
+    )
+    type = db.relationship(
+        'ActivityType',
+        foreign_keys=[type_id],
+    )
+
+    def __str__(self):
+        return str(uuid.UUID(bytes=self.id))

+ 214 - 0
pms/__init__.py

@@ -0,0 +1,214 @@
+#
+# emulator of a Policy Management System
+#
+import os
+import json
+import requests
+import random
+import string
+from time import sleep
+from flask import current_app
+
+DELAY_SECONDS = 2
+POLICY_DATASET = 'pms/data/policies.json'
+
+activities = {
+    'reactivate': {
+        "name": "Re-activate",
+        "description": "Re-activate the policy",
+        "fields": [
+            {
+                "name": "Re-activation Date",
+                "brief": "Re-activation Date",
+                "fieldType": 1,
+                "tooltip": "On what date should the re-activation be made?",
+                "type": "Datum",
+                "inputRange": [
+                    "2021-01-01",
+                    "2022-01-01"
+                ],
+                "onlyFromRange": False,
+                "valueChosenOrEntered": "",
+                "inputTriggers": False,
+                "isMandatory": True,
+            }
+        ]
+    },
+    'cancel': {
+        "name": "Cancel",
+        "description": "Cancels the policy",
+        "fields": [
+            {
+                "name": "Cancellation Reason",
+                "brief": "Cancellation Reason",
+                "fieldType": 1,
+                "tooltip": "Please select a reason for cancellation from the list of possible reasons for cancellation. After selecting the reason for cancellation, further input fields may appear",
+                "type": "Text",
+                "inputRange": [
+                    "9002 Omission",
+                    "9003 Bankruptcy",
+                ],
+                "onlyFromRange": False,
+                "valueChosenOrEntered": "9002 Omission",
+                "inputTriggers": True,
+                "isMandatory": True,
+            },
+            {
+                "name": "Cancellation Date",
+                "brief": "Cancellation Date",
+                "fieldType": 1,
+                "tooltip": "On what date should the cancellation be made?",
+                "type": "Datum",
+                "inputRange": [
+                    "2021-01-01",
+                    "2022-01-01",
+                ],
+                "onlyFromRange": False,
+                "valueChosenOrEntered": "",
+                "inputTriggers": False,
+                "isMandatory": False,
+            },
+        ],
+    },
+    'suspend': {
+        "name": "Suspend",
+        "description": "Suspend the policy",
+        "fields": [
+            {
+                "name": "Suspension Reason",
+                "brief": "Suspension Reason",
+                "fieldType": 1,
+                "tooltip": "Please select a reason for suspension from the list of possible reasons.",
+                "type": "Text",
+                "inputRange": [
+                    "8002 No Payment",
+                    "8003 Vacation",
+                    "8005 Other" 
+                ],
+                "onlyFromRange": False,
+                "valueChosenOrEntered": "8002 No Payment",
+                "inputTriggers": True,
+                "isMandatory": True,
+            },
+            {
+                "name": "Cancellation Date",
+                "brief": "Cancellation Date",
+                "fieldType": 1,
+                "tooltip": "Date of the policy suspension",
+                "type": "Datum",
+                "inputRange": [
+                    "2021-01-01",
+                    "2022-01-01",
+                ],
+                "onlyFromRange": False,
+                "valueChosenOrEntered": "",
+                "inputTriggers": False,
+                "isMandatory": False,
+            },
+        ],
+    },
+}
+
+activities_by_status = {
+    'active': [
+        'cancel',
+        'suspend',
+    ],
+    'canceled': [],
+    'suspended': [
+        'reactivate'
+    ]
+}
+
+
+class Stages:
+
+    @classmethod
+    def getAllStages(cls):
+        #
+        # returns available stages
+        #
+
+        return [
+            'development',
+            'production',
+        ]
+
+
+def random_objects():
+    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'objects.json'), 'r') as f:
+        return json.load(f).get('objects')
+
+
+def get(policy_number, effective_date):
+    #
+    # returns policy from dataset
+    #
+
+    # load policies
+    with open(POLICY_DATASET, 'r') as f:
+        policies = json.load(f)
+    
+    # delay emulation
+    #sleep(DELAY_SECONDS)
+
+    request_number = policy_number.upper().replace('-', '')
+    request_date = effective_date.replace('-', '')
+    print(f'**** REQUEST POLICY: {request_number} {request_date}')
+    for item in policies:
+        if request_number == item['number'].replace('-', '') and request_date == item['effective_date'].replace('-', ''):
+            # generate clauses
+            objects = random_objects()
+            item['clauses'] = [{
+                'name': random.choice(objects).capitalize(),
+                'number': clause,
+                'link': f'{current_app.config["HOST"]}/clauses/{clause}',
+                'description': f'Description of clause {clause}',
+            } for clause in (''.join(random.choice(string.digits) for _ in range(4)) + random.choice(string.ascii_uppercase) for i in range(random.randint(1, 5)))]
+            return item
+
+def get_activities(status):
+    #
+    # returns possible activities based on status
+    #
+
+    return [activities[_] for _ in activities_by_status[status]]
+
+def execute_activity(policy_number, activity):
+    #
+    # executes activity
+    #
+
+    print('----> PMS: Execute Activity')
+    print(f'----> PMS: Policy {policy_number}')
+
+    # load policies
+    with open(POLICY_DATASET, 'r') as f:
+        policies = json.load(f)
+
+    # delay emulation
+    sleep(DELAY_SECONDS)
+
+    statuses = {
+        'Re-activate': 'active',
+        'Cancel': 'canceled',
+        'Suspend': 'suspended',
+    }
+
+    # find policy 
+    for item in policies:
+        if item['number'] == policy_number:
+            print('----> PMS: Policy Found')
+            # execute activity
+            item['status'] = statuses.get(activity['name'])
+            for field in activity['fields']:
+                if field.get('value'):
+                    item['attributes'][field['name']] = field['value']
+
+            # update policies
+            with open(POLICY_DATASET, 'w') as f:
+                json.dump(policies, f)
+
+            return True
+
+    return False

+ 18 - 0
pms/data/antrag_activities.json

@@ -0,0 +1,18 @@
+{
+  "default": [
+    {
+      "name": "Berechnen",
+      "description": "Schnellrechner f\u00fcr eingegebene Parameter",
+      "icon": "calculate.svg",
+      "postExecution": "default",
+      "fields": []
+    },
+    {
+      "name": "Clone",
+      "description": "Antrag duplizieren",
+      "icon": "calculate.svg",
+      "postExecution": "default",
+      "fields": []
+    }
+  ]
+}

File diff suppressed because it is too large
+ 1337 - 0
pms/data/antrag_sample.json


+ 569 - 0
pms/data/antrags.json

@@ -0,0 +1,569 @@
+{
+  "Automobile": {  
+    "fields": [
+      {
+        "fieldType": 2,
+        "name": "premium",
+        "brief": "Premium",
+        "tooltip": "Indicative premium based on the inputs",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "None",
+        "valueChosenOrEnteredTech": "None",
+        "valueChosenOrEnteredOutput": "Euro None",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ],
+    "field_groups": [
+      {
+        "fieldType": 3,
+        "name": "Standard",
+        "brief": "General Data",
+        "tooltip": "General Data",
+        "icon": "",
+        "fieldDataType": "Flag",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "True",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 3,
+        "name": "VehicleData",
+        "brief": "Vehicle Data",
+        "tooltip": "Vehicle Data",
+        "icon": "",
+        "fieldDataType": "Flag",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "True",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "Kasko",
+        "brief": "Kasko",
+        "tooltip": "Kasko Insurance",
+        "icon": "",
+        "fieldDataType": "Flag",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "True",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "Accident",
+        "brief": "Accident Insurance",
+        "tooltip": "Accident Insurance",
+        "icon": "",
+        "fieldDataType": "Flag",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "True",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ],
+    "Standard": [
+      {
+        "fieldType": 3,
+        "name": "InsuranceStart",
+        "brief": "Start of insurance",
+        "tooltip": "Please select the date of start of the insurance",
+        "icon": "",
+        "fieldDataType": "Datum",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "2021-01-01",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "VehicleType",
+        "brief": "Vehicle Type",
+        "tooltip": "Please select the type of vehicle",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "Car",
+          "Light Truck",
+          "Heavy Truck",
+          "Motorcycle",
+          "Tractor",
+          "Bus"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "Car",
+        "inputTriggers": true,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "Brand",
+        "brief": "Brand",
+        "tooltip": "Please select the manufacturer of the vehicle",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "",
+          "Alfa romeo",
+          "Aston martin",
+          "Audi",
+          "Bentley",
+          "Bmw",
+          "Chevrolet",
+          "Chrysler",
+          "Citroen",
+          "Dacia",
+          "Daewoo",
+          "Daihatsu",
+          "Dodge",
+          "Ferrari",
+          "Fiat",
+          "Ford (brd)",
+          "Ford (gb)",
+          "Honda",
+          "Humer",
+          "Hyundai",
+          "Jaguar",
+          "Jeep",
+          "Kia",
+          "Lada",
+          "Lamborghini",
+          "Lancia",
+          "Land rover",
+          "Lexus",
+          "Maserati",
+          "Mazda",
+          "Mcc  (smart)",
+          "Mercedes",
+          "Mini",
+          "Mitsubishi",
+          "Morgan",
+          "Nissan",
+          "Opel",
+          "Peugeot",
+          "Porsche",
+          "Puch",
+          "Puch mercedes",
+          "Range rover",
+          "Renault",
+          "Rover",
+          "Saab",
+          "Seat",
+          "Skoda",
+          "Smart",
+          "Sonstige",
+          "Ssang yong",
+          "Steyr puch",
+          "Subaru",
+          "Suzuki",
+          "Tesla",
+          "Toyota",
+          "Volvo",
+          "VW"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "Renault",
+        "inputTriggers": true,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "OwnerAge",
+        "brief": "Owner's Age",
+        "tooltip": "Please enter the age of the license holder",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "18",
+          "80"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "30",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 3,
+        "name": "DriverUnder23",
+        "brief": "Driver Under 23",
+        "tooltip": "Do drivers under the age of 23 also drive this vehicle?",
+        "icon": "",
+        "fieldDataType": "Flag",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "True",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "OwnerPostcode",
+        "brief": "Owner's Postcode",
+        "tooltip": "Post code of the registration address of the license holder",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "1010",
+          "9999"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "1010",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "BonusLevel",
+        "brief": "Bonus Level",
+        "tooltip": "Please enter the bonus level for calculation",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "0",
+          "17"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "0",
+        "inputTriggers": true,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "IntendedUse",
+        "brief": "Intended Use",
+        "tooltip": "",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "no special use",
+          "for use in agriculture",
+          "for use in factory traffic",
+          "for use in the taxi trade"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "no special use",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "Discount",
+        "brief": "Discount",
+        "tooltip": "Please select the discount level",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "0",
+          "20"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "0",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "PaymentFrequency",
+        "brief": "Payment Frequency",
+        "tooltip": "Select the payment frequency from the list",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "monthly",
+          "semi-annually",
+          "annually"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "monthly",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ],
+    "VehicleData": [
+      {
+        "fieldType": 1,
+        "name": "VehicleAge",
+        "brief": "Vehicle Age",
+        "tooltip": "Please enter the age of the vehicle",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "New",
+          "1",
+          "2",
+          "3",
+          "4",
+          "5",
+          "6",
+          "7",
+          "8",
+          "9",
+          "10",
+          "11-20",
+          ">20"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "New",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "EngineType",
+        "brief": "Engine Type",
+        "tooltip": "Please select the type of the engine",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "Petrol",
+          "Petrol or Methane",
+          "Petrol or Natural Gas",
+          "Petrol or Liquid Gas",
+          "Diesel",
+          "Electrical",
+          "Gas",
+          "Combined Petrol and Electric Motor",
+          "Combined Diesel and Electric Motor"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "Petrol",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "CO2",
+        "brief": "CO2 Value",
+        "tooltip": "Please enter the CO2 value of the vehicle",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "1",
+          "500"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "None",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "KW",
+        "brief": "Kilowatt",
+        "tooltip": "Please enter the KW value of the vehicle",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "1",
+          "999"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "None",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 3,
+        "name": "EngineSize",
+        "brief": "Engine Size",
+        "tooltip": "Please enter the engine size of the vehicle",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "1",
+          "15000"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "None",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ],
+    "Kasko": [
+      {
+        "fieldType": 1,
+        "name": "ListPrice",
+        "brief": "List Price",
+        "tooltip": "Indication of the new list price including NOVA and VAT",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "range",
+          "1",
+          "999999"
+        ],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "None",
+        "inputTriggers": true,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "SpecialEquipment",
+        "brief": "Special Equipment",
+        "tooltip": "Specification of the special equipment including VAT. (if applicable)",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "0",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "KaskoVariant",
+        "brief": "Kasko Variant",
+        "tooltip": "Select a comprehensive insurance variant",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "Fully comprehensive partial SBH",
+          "Fully comprehensive general SBH",
+          "Partial insurance with partial SBH",
+          "Partial coverage general SBH",
+          "Parking damage partially SBH",
+          "Parking damage general SBH"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "Fully comprehensive general SBH",
+        "inputTriggers": true,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 1,
+        "name": "KaskoSBH",
+        "brief": "Deductible Kasko",
+        "tooltip": "Select which SBH should be apply",
+        "icon": "",
+        "fieldDataType": "Zahl",
+        "inputRange": [
+          "350",
+          "650",
+          "950"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "350",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ],
+    "Accident": [
+      {
+        "fieldType": 1,
+        "name": "AccidentVariants",
+        "brief": "Accident Variants",
+        "tooltip": "Select whether you want cover only the driver or driver and passenger in case of an accident",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [
+          "Only Driver",
+          "Driver and Passengers"
+        ],
+        "onlyFromRange": true,
+        "valueChosenOrEntered": "Driver and Passengers",
+        "inputTriggers": false,
+        "isMandatory": false,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 2,
+        "name": "AccidentDeath",
+        "brief": "Death",
+        "tooltip": "Sum insured in the event of death",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "\u20ac 20.000,00",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      },
+      {
+        "fieldType": 2,
+        "name": "AccidentDisability",
+        "brief": "Permanent Disability",
+        "tooltip": "Sum insured in the event of permanent disability",
+        "icon": "",
+        "fieldDataType": "Text",
+        "inputRange": [],
+        "onlyFromRange": false,
+        "valueChosenOrEntered": "\u20ac 40.000,00",
+        "valueChosenOrEnteredTech": "\u20ac 40.000,00",
+        "valueChosenOrEnteredOutput": "\u20ac 40.000,00",
+        "inputTriggers": false,
+        "isMandatory": true,
+        "errorMessage": "",
+        "endpoint": ""
+      }
+    ]
+  }
+}

+ 141 - 0
pms/data/auto_brands.json

@@ -0,0 +1,141 @@
+{
+  "Car": [
+    "Alfa Romeo",
+    "Aston Martin",
+    "Audi",
+    "Bentley",
+    "BMW",
+    "Chevrolet",
+    "Chrysler",
+    "Citroen",
+    "Dacia",
+    "Daewoo",
+    "Daihatsu",
+    "Dodge",
+    "Ferrari",
+    "Fiat",
+    "Ford",
+    "Honda",
+    "Humer",
+    "Hyundai",
+    "Jaguar",
+    "Jeep",
+    "Kia",
+    "Lada",
+    "Lamborghini",
+    "Lancia",
+    "Land rover",
+    "Lexus",
+    "Maserati",
+    "Mazda",
+    "Mcc (smart)",
+    "Mercedes",
+    "Mini",
+    "Mitsubishi",
+    "Morgan",
+    "Nissan",
+    "Opel",
+    "Peugeot",
+    "Porsche",
+    "Puch",
+    "Puch Mercedes",
+    "Range Rover",
+    "Renault",
+    "Rover",
+    "Saab",
+    "Seat",
+    "Skoda",
+    "Smart",
+    "Sonstige",
+    "Ssang Yong",
+    "Steyr Puch",
+    "Subaru",
+    "Suzuki",
+    "Tesla",
+    "Toyota",
+    "Volvo",
+    "VW"
+  ],
+  "Light Truck": [
+    "Chevrolet",
+    "DAF",
+    "Dongfeng",
+    "Ford",
+    "GMC",
+    "Great Wall",
+    "Hino",
+    "Isuzu",
+    "Iveco",
+    "Jeep",
+    "Jiangling",
+    "Mercedes-Benz",
+    "Nissan",
+    "Ram Trucks",
+    "Tata",
+    "UD"
+  ],
+  "Heavy Truck": [
+    "BharatBenz",
+    "Dongfeng",
+    "Foton",
+    "Freightliner",
+    "Hino",
+    "International",
+    "Iveco",
+    "Kenworth",
+    "MAN",
+    "Mack",
+    "Mercedes-Benz",
+    "Peterbilt",
+    "Scania",
+    "Tata",
+    "UD",
+    "Volvo",
+    "Western Star"
+  ],
+  "Motorcycle": [
+    "Aprilia",
+    "BMW",
+    "Ducati",
+    "Harley-Davidson",
+    "Honda",
+    "Indian Motorcycle",
+    "Kawasaki",
+    "KTM",
+    "Royal Enfield",
+    "Suzuki",
+    "Triumph",
+    "Yamaha"
+  ],
+  "Tractor": [
+    "Case IH",
+    "Claas",
+    "Deutz Fahr",
+    "Escorts Group",
+    "Fendt",
+    "John Deere",
+    "Kubota",
+    "Mahindra & Mahindra",
+    "Massey Ferguson",
+    "New Holland",
+    "Sonalika International"
+  ],
+  "Bus": [
+    "Anhui Ankai",
+    "Collins",
+    "Daimler",
+    "ENC",
+    "Higer",
+    "Isuzu",
+    "Iveco",
+    "Kamaz",
+    "Marcopolo",
+    "NFI",
+    "Scania",
+    "Solaris",
+    "Toyota",
+    "VW",
+    "Volvo",
+    "Zhengzhou Yutong"
+  ]
+}

+ 221 - 0
pms/data/generate_policies.py

@@ -0,0 +1,221 @@
+import requests
+import json
+from random import choice, randrange, sample
+from string import ascii_uppercase, digits
+from datetime import date, timedelta
+
+RECORD_NUMBER = 50
+OBJECTS = None
+
+def generate_persons(number):
+    print('\nGenerating persons...')
+    user_url = f'https://randomuser.me/api/?results={number}&nat=ch,de,dk,es,fi,fr,gb,ie,nl,no,tr'
+    occupation_url = 'https://raw.githubusercontent.com/dariusk/corpora/master/data/humans/occupations.json'
+    sports_url = 'https://raw.githubusercontent.com/dariusk/corpora/master/data/sports/sports.json'
+
+    # get occupations
+    print('Fetching occupations...')
+    r = requests.get(occupation_url)
+    if r.status_code != 200:
+        print(f'Occupations Response: {r.status_code}\n{r.text}')
+        return
+    occupations = r.json().get('occupations')
+
+    # get sports
+    print('Fetching sports...')
+    r = requests.get(sports_url)
+    if r.status_code != 200:
+        print(f'Sports Response: {r.status_code}\n{r.text}')
+        return
+    sports = r.json().get('sports')
+
+    # get users
+    print('Fetching users...')
+    r = requests.get(user_url)
+    if r.status_code != 200:
+        print(f'Users Response: {r.status_code}\n{r.text}')
+        return
+    users = r.json().get('results')
+
+    # health conditions
+    health_conditions = ['Good', 'Normal', 'Bad']
+
+    # generate persons
+    persons = []
+    for user in users:
+        persons.append(
+            {
+                'is_person': True,
+                'first_name': user['name'].get('first'),
+                'last_name': user['name'].get('last'),
+                'birthdate': user['dob'].get('date')[:10],
+                'address': ' '.join(map(str, user['location'].get('street').values())),
+                'city': user['location'].get('city'),
+                'country': user['location'].get('country'),
+                'postal_code': str(user['location'].get('postcode')),
+                'email': user['email'],
+                'primary_phone': user['phone'],
+                'secondary_phone': user['cell'],
+                'occupation': choice(occupations),
+                'occupation_from': user['registered'].get('date')[:10],
+                'previous_occupation': choice(occupations),
+                'sports': [choice(sports) for i in range(randrange(3))],
+                'health_condition': choice(health_conditions),
+            }
+        )
+
+    return persons
+
+def generate_companies(number):
+    print('\nGenerating companies...')
+    user_url = f'https://randomuser.me/api/?results={number}'
+    fortune_url = 'https://raw.githubusercontent.com/dariusk/corpora/master/data/corporations/fortune500.json'
+
+    # get companies
+    print('Fetching comapy names...')
+    r = requests.get(fortune_url)
+    if r.status_code != 200:
+        print(f'Companies Response: {r.status_code}\n{r.text}')
+        return
+    companies = sample(r.json().get('companies'), number)
+
+    # get users
+    print('Fetching users...')
+    r = requests.get(user_url)
+    if r.status_code != 200:
+        print(f'Users Response: {r.status_code}\n{r.text}')
+        return
+    users = r.json().get('results')
+
+    # generate companies
+    partners = []
+    for user, company in zip(users, companies):
+        partners.append(
+            {
+                'is_person': False,
+                'company_name': company,
+                'address': ' '.join(map(str, user['location'].get('street').values())),
+                'city': user['location'].get('city'),
+                'country': user['location'].get('country'),
+                'postal_code': str(user['location'].get('postcode')),
+                'email': user['email'],
+                'primary_phone': user['phone'],
+                'secondary_phone': user['cell'],
+            }
+        )
+
+    return partners
+
+def generate_attributes(name, number):
+    return {f'{name} {i+1}': choice(OBJECTS) for i in range(number)}
+
+def get_product_line_attributes(name):
+    if name == 'Life':
+        return generate_attributes('Life Attribute', 3)
+    if name == 'Health':
+        return generate_attributes('Health Attribute', 5)
+    if name == 'P&C':
+        return generate_attributes('P&C Attribute', 2)
+    if name == 'Car':
+        return generate_attributes('Car Attribute', 1)
+    return {}
+
+def get_object_type_attributes(name):
+    if name == 'House':
+        return generate_attributes('Hause Attribute', 1)
+    if name == 'Car':
+        return generate_attributes('Car Attribute', 2)
+    if name == 'Factory':
+        return generate_attributes('Factory Attribute', 3)
+    if name == 'Field':
+        return generate_attributes('Field Attribute', 2)
+    if name == 'Forest':
+        return generate_attributes('Forest Attribute', 1)
+    return {}
+
+def generate_insured_object(person=None):
+    if person:
+        return {
+            'is_person': True,
+            'partner': person,
+            'attributes': generate_attributes('Insured Person Attribute', 3),
+            'implementation_attributes': generate_attributes('Implementation Attribute', 2),
+        }
+
+    object_types = [
+        'House',
+        'Car',
+        'Factory',
+        'Field',
+        'Forest',
+    ]
+    object_type = choice(object_types)
+    return {
+        'is_person': False,
+        'type': object_type,
+        'attributes': get_object_type_attributes(object_type),
+        'implementation_attributes': generate_attributes('Implementation Attribute', 2),
+    }
+
+def generate_policies(number):
+    persons = generate_persons(number)
+    partners = persons + generate_companies(number)
+
+    statuses = [
+        'active',
+        'canceled',
+        'suspended',
+    ]
+
+    product_lines = {
+        'Life': True,
+        'Health': True,
+        'P&C': False,
+        'Car': False,
+    }
+
+    policies = []
+    for i in range(number):
+        policy_number = '-'.join((
+            ''.join(choice(ascii_uppercase) for _ in range(2)),
+            ''.join(choice(digits) for _ in range(5)),
+        ))
+        effective_date = date.today() + timedelta(days=randrange(360))
+        product_line = choice(list(product_lines))
+        if product_lines[product_line]:
+            insured_object = generate_insured_object(person=choice(persons))
+        else:
+            insured_object = generate_insured_object()
+        policies.append(
+            {
+                'number': policy_number,
+                'effective_date': str(effective_date),
+                'product_line': {
+                    'name': product_line,
+                    'attributes': get_product_line_attributes(product_line),
+                },
+                'remote_system': 'PoLZy',
+                'status': choice(statuses),
+                'premium_payer': choice(partners),
+                'insured_object': insured_object,
+                'attributes': generate_attributes('Policy Attribute', 4),
+            }
+        )
+
+    return policies
+
+
+if __name__ == '__main__':
+    # get object list to populate attributes
+    print('Fetching object list...')
+    objects_url = 'https://raw.githubusercontent.com/dariusk/corpora/master/data/objects/objects.json'
+    r = requests.get(objects_url)
+    if r.status_code != 200:
+        print(f'Objects Response: {r.status_code}\n{r.text}')
+        exit()
+    OBJECTS = r.json().get('objects')
+
+    # generate and save policies
+    policies = generate_policies(RECORD_NUMBER)
+    with open('policies.json', 'w') as f:
+        json.dump(policies, f)

+ 452 - 0
pms/data/objects.json

@@ -0,0 +1,452 @@
+{
+  "description": "List of household objects",
+  "objects": [
+    "CD",
+    "Christmas ornament",
+    "acorn",
+    "apple",
+    "bag",
+    "bag of cotton balls",
+    "bag of popcorn",
+    "bag of rubber bands",
+    "ball of yarn",
+    "balloon",
+    "banana",
+    "bananas",
+    "bandana",
+    "bangle bracelet",
+    "bar of soap",
+    "baseball",
+    "baseball bat",
+    "baseball hat",
+    "basketball",
+    "beaded bracelet",
+    "beaded necklace",
+    "bed",
+    "beef",
+    "bell",
+    "belt",
+    "blouse",
+    "blowdryer",
+    "bonesaw",
+    "book",
+    "book of jokes",
+    "book of matches",
+    "bookmark",
+    "boom box",
+    "bottle",
+    "bottle cap",
+    "bottle of glue",
+    "bottle of honey",
+    "bottle of ink",
+    "bottle of lotion",
+    "bottle of nail polish",
+    "bottle of oil",
+    "bottle of paint",
+    "bottle of perfume",
+    "bottle of pills",
+    "bottle of soda",
+    "bottle of sunscreen",
+    "bottle of syrup",
+    "bottle of water",
+    "bouquet of flowers",
+    "bow",
+    "bow tie",
+    "bowl",
+    "box",
+    "box of Q-tips",
+    "box of baking soda",
+    "box of chalk",
+    "box of chocolates",
+    "box of crayons",
+    "box of markers",
+    "box of tissues",
+    "bracelet",
+    "bread",
+    "broccoli",
+    "brush",
+    "buckle",
+    "butter knife",
+    "button",
+    "camera",
+    "can of beans",
+    "can of chili",
+    "can of peas",
+    "can of whipped cream",
+    "candle",
+    "candlestick",
+    "candy bar",
+    "candy cane",
+    "candy wrapper",
+    "canteen",
+    "canvas",
+    "car",
+    "card",
+    "carrot",
+    "carrots",
+    "cars",
+    "carton of ice cream",
+    "cat",
+    "catalogue",
+    "cell phone",
+    "cellphone",
+    "cement stone",
+    "chain",
+    "chair",
+    "chalk",
+    "chapter book",
+    "check book",
+    "chenille stick",
+    "chicken",
+    "children's book",
+    "chocolate",
+    "class ring",
+    "clay pot",
+    "clock",
+    "clothes",
+    "clothes pin",
+    "coffee mug",
+    "coffee pot",
+    "comb",
+    "comic book",
+    "computer",
+    "conditioner",
+    "container of pudding",
+    "cookie jar",
+    "cookie tin",
+    "cork",
+    "couch",
+    "cow",
+    "cowboy hat",
+    "craft book",
+    "credit card",
+    "crow",
+    "crowbar",
+    "cucumber",
+    "cup",
+    "dagger",
+    "deodorant",
+    "desk",
+    "dictionary",
+    "dog",
+    "dolphin",
+    "domino set",
+    "door",
+    "dove",
+    "drawer",
+    "drill press",
+    "egg",
+    "egg beater",
+    "egg timer",
+    "empty bottle",
+    "empty jar",
+    "empty tin can",
+    "eraser",
+    "extension cord",
+    "eye liner",
+    "face wash",
+    "fake flowers",
+    "feather",
+    "feather duster",
+    "few batteries",
+    "fish",
+    "fishing hook",
+    "flag",
+    "flashlight",
+    "floor",
+    "flowers",
+    "flyswatter",
+    "food",
+    "football",
+    "fork",
+    "fridge",
+    "frying pan",
+    "game CD",
+    "game cartridge",
+    "garden spade",
+    "giraffe",
+    "glass",
+    "glasses",
+    "glow stick",
+    "grid paper",
+    "grocery list",
+    "hair brush",
+    "hair clip",
+    "hair pin",
+    "hair ribbon",
+    "hair tie",
+    "hammer",
+    "hamster",
+    "hand bag",
+    "hand fan",
+    "hand mirror",
+    "handbasket",
+    "handful of change",
+    "handheld game system",
+    "hanger",
+    "harmonica",
+    "helmet",
+    "house",
+    "ice cream stick",
+    "ice cube",
+    "ice pick",
+    "incense holder",
+    "ipod",
+    "ipod charger",
+    "jar of jam",
+    "jar of peanut butter",
+    "jar of pickles",
+    "jigsaw puzzle",
+    "key",
+    "key chain",
+    "keyboard",
+    "keychain",
+    "keys",
+    "kitchen knife",
+    "knife",
+    "lace",
+    "ladle",
+    "lamp",
+    "lamp shade",
+    "laser pointer",
+    "leg warmers",
+    "lemon",
+    "letter opener",
+    "light",
+    "light bulb",
+    "lighter",
+    "lime",
+    "lion",
+    "lip gloss",
+    "locket",
+    "lotion",
+    "magazine",
+    "magnet",
+    "magnifying glass",
+    "map",
+    "marble",
+    "martini glass",
+    "matchbook",
+    "microphone",
+    "milk",
+    "miniature portrait",
+    "mirror",
+    "mobile phone",
+    "model car",
+    "money",
+    "monitor",
+    "mop",
+    "mouse pad",
+    "mp3 player",
+    "multitool",
+    "music CD",
+    "nail",
+    "nail clippers",
+    "nail filer",
+    "necktie",
+    "needle",
+    "notebook",
+    "notepad",
+    "novel",
+    "ocarina",
+    "orange",
+    "outlet",
+    "pack of cards",
+    "package of crisp and crunchy edibles",
+    "package of glitter",
+    "packet of seeds",
+    "pail",
+    "paint brush",
+    "paintbrush",
+    "pair of binoculars",
+    "pair of dice",
+    "pair of earrings",
+    "pair of glasses",
+    "pair of handcuffs",
+    "pair of knitting needles",
+    "pair of rubber gloves",
+    "pair of safety goggles",
+    "pair of scissors",
+    "pair of socks",
+    "pair of sunglasses",
+    "pair of tongs",
+    "pair of water goggles",
+    "panda",
+    "pants",
+    "paper",
+    "paperclip",
+    "pasta strainer",
+    "pearl necklace",
+    "pen",
+    "pencil",
+    "pencil holder",
+    "pepper shaker",
+    "perfume",
+    "phone",
+    "photo album",
+    "picture frame",
+    "piece of gum",
+    "pillow",
+    "pinecone",
+    "plastic fork",
+    "plate",
+    "plush bear",
+    "plush cat",
+    "plush dinosaur",
+    "plush dog",
+    "plush frog",
+    "plush octopus",
+    "plush pony",
+    "plush rabbit",
+    "plush unicorn",
+    "pocketknife",
+    "pocketwatch",
+    "pool stick",
+    "pop can",
+    "postage stamp",
+    "puddle",
+    "purse",
+    "purse/bag",
+    "quartz crystal",
+    "quilt",
+    "rabbit",
+    "radio",
+    "rat",
+    "remote",
+    "rhino",
+    "ring",
+    "rock",
+    "roll of duct tape",
+    "roll of gauze",
+    "roll of masking tape",
+    "roll of stickers",
+    "roll of toilet paper",
+    "rolling pin",
+    "rope",
+    "rubber band",
+    "rubber duck",
+    "rubber stamp",
+    "rug",
+    "rusty nail",
+    "safety pin",
+    "sailboat",
+    "salt shaker",
+    "sand paper",
+    "sandal",
+    "sandglass",
+    "scallop shell",
+    "scarf",
+    "scotch tape",
+    "screw",
+    "screwdriver",
+    "seat belt",
+    "shampoo",
+    "shark",
+    "sharpie",
+    "shawl",
+    "sheep",
+    "sheet of paper",
+    "shirt",
+    "shirt button",
+    "shoe lace",
+    "shoes",
+    "shopping bag",
+    "shovel",
+    "sidewalk",
+    "sketch pad",
+    "slipper",
+    "small pouch",
+    "snail shell",
+    "snowglobe",
+    "soap",
+    "soccer ball",
+    "socks",
+    "sofa",
+    "spatula",
+    "speakers",
+    "spectacles",
+    "spice bottle",
+    "sponge",
+    "spool of ribbon",
+    "spool of string",
+    "spool of thread",
+    "spool of wire",
+    "spoon",
+    "spring",
+    "squirrel",
+    "squirt gun",
+    "statuette",
+    "steak knife",
+    "stick",
+    "stick of incense",
+    "sticker book",
+    "sticky note",
+    "stockings",
+    "stop sign",
+    "straw",
+    "street lights",
+    "sun glasses",
+    "sword",
+    "table",
+    "tea cup",
+    "tea pot",
+    "teddies",
+    "television",
+    "tennis ball",
+    "tennis racket",
+    "thermometer",
+    "thimble",
+    "thread",
+    "tiger",
+    "tire swing",
+    "tissue box",
+    "toe ring",
+    "toilet",
+    "toilet paper tube",
+    "tomato",
+    "tooth pick",
+    "toothbrush",
+    "toothpaste",
+    "toothpick",
+    "towel",
+    "toy boat",
+    "toy car",
+    "toy plane",
+    "toy robot",
+    "toy soldier",
+    "toy top",
+    "trash bag",
+    "tree",
+    "trucks",
+    "tube of lip balm",
+    "tube of lipstick",
+    "turtle",
+    "tv",
+    "tweezers",
+    "twister",
+    "umbrella",
+    "vase",
+    "video games",
+    "wallet",
+    "washcloth",
+    "washing machine",
+    "watch",
+    "water",
+    "water bottle",
+    "wedding ring",
+    "whale",
+    "whip",
+    "whistle",
+    "white out",
+    "window",
+    "wine glass",
+    "wireless control",
+    "wishbone",
+    "wooden spoon",
+    "word search",
+    "wrench",
+    "wristwatch",
+    "zebra",
+    "zipper"
+  ]
+}

File diff suppressed because it is too large
+ 1 - 0
pms/data/policies.json


+ 41 - 0
pms/fast_offer.py

@@ -0,0 +1,41 @@
+import json
+import os
+
+class Products:
+
+    @classmethod
+    def getAllAvailableProducts(cls, user):
+        #
+        # returns available for user products
+        #
+
+        return [
+            {
+                'name': 'auto',
+                'description': 'Automobile',
+                'icon': 'car',
+            },
+            #{
+            #    'name': 'restate',
+            #    'description': 'Real Estate',
+            #    'icon': 'home',
+            #},
+        ]
+
+
+def get_fast_offer(product):
+    # get antrag
+    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'antrags.json'), 'r') as f:
+        antrag = json.load(f).get(product)
+
+    # get possible activities
+    if antrag:
+        with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'antrag_activities.json'), 'r') as f:
+            activities = json.load(f).get('default')
+
+        antrag['possible_activities'] = activities if activities else []
+        return antrag
+
+def get_auto_brands(type):
+    with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'auto_brands.json'), 'r') as f:
+        return json.load(f).get(type)

+ 75 - 0
policy.py

@@ -0,0 +1,75 @@
+#
+# Sample Policy class definition
+#
+
+
+from polzybackend.mediators import Policy
+import pms
+
+
+class SamplePolicy(Policy):
+
+    def get_partner(self, partner):
+        result = {}
+        if partner['is_person']:
+            result = {
+                'First Name': partner['first_name'],
+                'Last Name': partner['last_name'],
+                'Birthday': partner['birthdate'],
+                'Occupation': partner['occupation'],
+                'Occupation from': partner['occupation_from'],
+                'Previous Occupation': partner['previous_occupation'],
+                'Sports': ', '.join(partner['sports']),
+                'Health Condition': partner['health_condition'],
+            }
+        else:
+            result = {
+                'Company Name': partner.get('company_name'),
+            }
+        result.update({
+            'Address': partner['address'],
+            'City': partner['city'],
+            'Country': partner['country'],
+            'Postal Code': partner['postal_code'],
+            'E-mail': partner['email'],
+            'Phone (primary)': partner['primary_phone'],
+            'Phone (secondary)': partner['secondary_phone'],
+        })
+        return result
+
+    def get_insured_object(self, insured_object):
+        if insured_object['is_person']:
+            result = {'Type': 'Person'}
+            result.update(self.get_partner(insured_object['partner']))
+        else:
+            result = {'Type': insured_object['type']}
+            result.update(insured_object['attributes'])
+        return result
+
+
+    def reshape_data(self, data):
+        self.data = {key: data[key] for key in data if not key in ['number', 'premium_payer', 'insured_object']}
+        self.data['policy_number'] = data['number']
+        self.data['premium_payer'] = self.get_partner(data['premium_payer'])
+        self.data['insured_object'] = self.get_insured_object(data['insured_object'])
+        self.data['possible_activities'] = pms.get_activities(self.data['status'])
+
+
+    def fetch(self):
+        # fetch policy data from Policy Management System
+        data = pms.get(self.number, self.effective_date)
+        if data:
+            # update attributes
+            self.number = data['number']
+            self.effective_date = data['effective_date']
+            
+            # reshape data if needed
+            self.reshape_data(data)
+            
+            return True
+
+        return False
+
+    def executeActivity(self, data):
+        print(f'*** EXECUTE ACTIVITY:\n{data}')
+        return pms.execute_activity(self.number, data)

+ 190 - 0
populate_db.py

@@ -0,0 +1,190 @@
+#
+# create default user in PoLZy DB
+#
+
+from polzybackend.models import User, Role, Company, UserToCompany, CompanyToCompany
+from polzybackend.utils.auth_utils import generate_token
+import uuid
+from datetime import datetime, timedelta
+from flask_sqlalchemy import SQLAlchemy
+from polzybackend import create_app
+from app import Config
+import json
+
+app = create_app(Config)
+db = SQLAlchemy(app)
+print(db)
+
+# create OAuth provider
+'''
+print('Creating OAuth Provider...')
+provider = OAuthProvider(
+    name='SampleProvider',
+    client_id=str(uuid.uuid4()),
+    secret_key=generate_token(16),
+)
+'''
+
+# create roles
+print('Creating roles...')
+admin_role = Role(name='admin', is_supervisor=True)
+agent_role = Role(name='agent')
+clerk_role = Role(name='clerk')
+db.session.add_all([admin_role, agent_role])
+
+# create users
+print('Creating users...')
+admin = User(
+    email='admin@polzy.com',
+    #oauth_provider=provider,
+    #oauth_user_id=str(uuid.uuid4()),
+    #oauth_token=generate_token(16),
+    key_expired=datetime.now() + timedelta(days=360),
+)
+db.session.add(admin)
+
+agent = User(
+    email='agent@polzy.com',
+    displayed_name='Agent',
+    #oauth_provider=provider,
+    #oauth_user_id=str(uuid.uuid4()),
+    #oauth_token=generate_token(16),
+    key_expired=datetime.now() + timedelta(days=360),
+)
+db.session.add(agent)
+
+clerk = User(
+    email='clerk@polzy.com',
+    key_expired=datetime.now() + timedelta(days=360),
+)
+db.session.add(clerk)
+
+
+# create companies
+print('Creating companies...')
+company = Company(
+    name='Donau',
+    displayed_name='Donau - so stell ich mir das vor!',
+    email='info@donauversicherung.at',
+    phone='+43 50330 330',
+    country='AT',
+    post_code='1010',
+    city='Wien',
+    address='Schottenring 15',
+    attributes=json.dumps({
+        'theme': {
+            'palette': {
+                'primary': {
+                    'main': '#3c6496',
+                },
+                'secondary': {
+                    'main': '#3c6496',
+                },
+            },
+        },
+        'logo': {
+            'top': 'donau-logo.png',
+            'policy': 'LeZySEM Blue.png',
+            'antrag': 'LeZyTOR Blue.png',
+        }
+    })
+)
+# bind admin to company
+company_admin = UserToCompany(
+    user=admin,
+    company=company,
+    roles=[admin_role],
+)
+# bind agent to organization
+organization_agent = UserToCompany(
+    user=agent,
+    company=company,
+    roles=[agent_role],
+)
+organization_clerk = UserToCompany(
+    user=clerk,
+    company=company,
+    roles=[clerk_role],
+)
+db.session.add(company)
+db.session.add(company_admin)
+db.session.add(organization_agent)
+db.session.add(organization_clerk)
+
+company = Company(
+    name="WSTV",
+    displayed_name="Wiener Städtische - ihre Sorgen möchten wir haben!",
+    email="kundenservice@wienerstaedtische.at",
+    phone="+53 50350 350",
+    country="AT",
+    post_code='1010',
+    city='Wien',
+    address='Schottenring 30',
+    attributes=json.dumps({
+        'theme': {
+            'palette': {
+              'primary': {
+                'main': '#9e3c3a',
+              },
+              'secondary': {
+                'main': '#9e3c3a',
+              },
+            },
+        },
+        'logo': {
+            'top': 'wstv.png',
+            'policy': 'LeZySEM Red.png',
+            'antrag': 'LeZyTOR Red.png',
+        }
+    })
+)
+# bind admin to company
+company_admin = UserToCompany(
+    user=admin,
+    company=company,
+    roles=[admin_role, agent_role],
+)
+db.session.add(company)
+db.session.add(company_admin)
+
+organization = Company(
+    name='Sample Organization',
+    #displayed_name='',
+    #email='',
+    #phone='',
+    #country='',
+    #post_code='',
+    #city='',
+    #address='',
+)
+# bind admin to organization
+organization_admin = UserToCompany(
+    user=admin,
+    company=organization,
+    roles=[admin_role],
+)
+# bind organization to company
+organization_structure = CompanyToCompany(
+    parent=company,
+    child=organization,
+    attributes=json.dumps({
+        'policy': [
+            'admin',
+            'agent',
+        ],
+        'antrag': [
+            'admin',
+        ],
+    }),
+)
+
+db.session.add(organization)
+db.session.add(organization_admin)
+db.session.add(organization_structure)
+
+try:
+    db.session.commit()
+except Exception as e:
+    print(f"Error during commit: {e}")
+    pass
+print('Done.')

+ 2 - 0
requirements.txt

@@ -0,0 +1,2 @@
+requests
+polzybackend