from flask import request
from flask_restful import Resource
from flask_jwt_extended import get_jwt_claims

import iot_logging, calendar, json, math
log = iot_logging.getLogger(__name__)

from collections import namedtuple

from iot_api.user_api.model import User
from iot_api.user_api.Utils import is_system
from iot_api.user_api.JwtUtils import admin_regular_allowed
from iot_api.user_api.repository import ResourceUsageRepository, TagRepository, PacketRepository
from iot_api.user_api import Error

class ResourceUsageInformationAPI(Resource):
    """ Endpoint to get the resource usage of an asset of a given type
    Request parameters:
        - asset_type (required): type of requested asset (can be device or gateway).
        - asset_id (required): database id of the asset
        - min_rssi: for filtering packets list, return only packets with rssi not lower than this value
        - max_rssi: for filtering packets list, return only packets with rssi not higher than this value
        - min_lsnr: for filtering packets list, return only packets with lsnr not lower than this value
        - max_lsnr: for filtering packets list, return only packets with lsnr not higher than this value
    Returns:
        - JSON with requested resource usage information.
    """
    @admin_regular_allowed
    def get(self, asset_type, asset_id):
        organization_id = get_jwt_claims().get('organization_id')
        asset = ResourceUsageRepository.get_with(asset_id, asset_type,  organization_id)

        packets = None
        lsnr_values = None
        rssi_values = None
        if asset_type == 'device':
            temp_packets = PacketRepository.get_with(
                    ids_list=json.loads(asset.last_packets_list),
                    min_rssi = request.args.get('min_rssi', default = None, type=int),
                    max_rssi = request.args.get('max_rssi', default = None, type=int),
                    min_lsnr = request.args.get('min_lsnr', default = None, type=float),
                    max_lsnr = request.args.get('max_lsnr', default = None, type=float)
                )
            packets = []
            for packet, gw_id in temp_packets:
                to_add = packet.to_json()
                to_add.update({'gateway_id': gw_id})
                packets.append(to_add)
            lsnr_values = [packet['lsnr'] for packet in packets if packet['lsnr'] is not None]
            rssi_values = [packet['rssi'] for packet in packets if packet['rssi'] is not None]

        # Here the dev_addr is not returned to avoid a join between Device and DeviceSession,
        # since it is already reported in the inventory endpoint.
        response = {
            'id': asset.id,
            'hex_id': asset.hex_id,
            'type': asset.type,
            'name': asset.name,
            'data_collector': asset.data_collector,
            'app_name': asset.app_name,
            'connected': asset.connected,
            'last_activity': calendar.timegm(asset.last_activity.timetuple()),
            'activity_freq': asset.activity_freq,
            'activity_freq_variance': asset.activity_freq_variance,
            'is_regular': asset_is_regular(asset),
            'packets_up': buildPacketsInfo(asset.PACKETS_UP, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)),
            'packets_down': buildPacketsInfo(asset.PACKETS_DOWN, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)),
            'packets_lost': buildPacketsInfo(asset.PACKETS_LOST, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)) if asset.type == 'Device' else None,
            'retransmissions': asset.RETRANSMISSIONS,
            'join_requests': asset.JOIN_REQUESTS,
            'failed_join_requests': asset.FAILED_JOIN_REQUESTS,
            'max_rssi': asset.max_rssi,
            'max_lsnr': asset.max_lsnr,
            'ngateways_connected_to': asset.ngateways_connected_to,
            'payload_size': asset.payload_size,
            'tags': [{
                "id": tag.id,
                "name": tag.name,
                "color": tag.color
            } for tag in TagRepository.list_asset_tags(asset_id, asset_type, organization_id)],
            'last_packets_list': packets,
            'min_lsnr_packets': min(lsnr_values) if lsnr_values and len(lsnr_values) > 0 else None,
            'max_lsnr_packets': max(lsnr_values) if lsnr_values and len(lsnr_values) > 0 else None,
            'min_rssi_packets': min(rssi_values) if rssi_values and len(rssi_values) > 0 else None,
            'max_rssi_packets': max(rssi_values) if rssi_values and len(rssi_values) > 0 else None,
        }

        return response, 200

class ResourceUsageListAPI(Resource):
    """ Endpoint to list assets (devices + gateways)
    Request parameters (all optional):
        - page: for pagination.
        - size: for pagination.
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - JSON with list of assets and their resource usage (see code for more details about the fields).
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')
        page = request.args.get('page', default=1, type=int)
        size = request.args.get('size', default=20, type=int)
        
        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                return {
                        'assets': [],
                        'total_pages': 0,
                        'total_items': 0
                    }, 200

        results = ResourceUsageRepository.list_all(
            organization_id = organization_id,
            page = page, size = size,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids=request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )

        assets = [{
            'id': dev.id,
            'hex_id': dev.hex_id,
            'dev_addr': dev.dev_addr,
            'type': dev.type,
            'name': dev.name,
            'data_collector': dev.data_collector,
            'app_name': dev.app_name,
            'connected': dev.connected,
            'last_activity': calendar.timegm(dev.last_activity.timetuple()),
            'activity_freq': dev.activity_freq,
            'activity_freq_variance': dev.activity_freq_variance,
            'is_regular': asset_is_regular(dev),
            'packets_up': buildPacketsInfo(dev.PACKETS_UP, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)),
            'packets_down': buildPacketsInfo(dev.PACKETS_DOWN, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)),
            'packets_lost': buildPacketsInfo(dev.PACKETS_LOST, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)) if dev.type == 'Device' else None,
            'retransmissions': dev.RETRANSMISSIONS,
            'join_requests': dev.JOIN_REQUESTS,
            'failed_join_requests': dev.FAILED_JOIN_REQUESTS,
            'max_rssi': dev.max_rssi,
            'max_lsnr': dev.max_lsnr,
            'payload_size':dev.payload_size,
            'ngateways_connected_to':dev.ngateways_connected_to
        } for dev in results.items]
        response = {
            'assets': assets,
            'total_pages': results.pages,
            'total_items': results.total
        }
        return response, 200

def buildPacketsInfo(count, total):
    """ Helper function to calculate data related with packets received from an asset
    Request parameters (all required):
        - count: number of packets of the type (up, down, lost) to calculate data for
        - total: number of packets in total
    Returns:
        - JSON with packets related information (see code for more details about the fields).
    """
    return {
        'total': count,
        'percentage': 100*count/total if total else None
    }

def asset_is_regular(asset):
    params = json.loads(asset.policy_parameters or '{}')

    #Consider the asset as regular if irregularity cannot be checked
    if 'deviation_tolerance' not in params or asset.type == 'Gateway' or not asset.activity_freq:
        return True

    return math.sqrt(asset.activity_freq_variance)/asset.activity_freq <= params['deviation_tolerance']

class ResourceUsagePerStatusCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by status (connected/disconnected).
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has three fields: id, name, count. (id = name = 'connected'/'disconnected')
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')   

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"
        
        groups = ResourceUsageRepository.count_per_status(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerGatewayCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by gateway.
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has three fields: id, name, count. (name = hex_id)
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_gateway(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerSignalStrengthCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by signal strength.
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has the following structure:
            'id': {
                'min_signal_strength': int,
                'max_signal_strength': int
            },
            'name': 'Unusable'/'Very weak'/'Weak'/'Okay'/'Great'/'Excellent',
            'count': int
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_signal_strength(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        # Make the ids a JSON object instead of a tuple
        for signal_range in groups:
            signal_range['id'] = {
                'min_signal_strength': signal_range['id'][0],
                'max_signal_strength': signal_range['id'][1]
            }

        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerPacketLossCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by packet loss percentage
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has the following structure:
            'id': {
                'min_packet_loss': int,
                'max_packet_loss': int
            },
            'name': '[0,10)'/'[10,20)'/.../'[80,90)'/'[90,100]',
            'count': int
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_packet_loss(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        # Make the ids a JSON object instead of a tuple
        for loss_range in groups:
            loss_range['id'] = {
                'min_packet_loss': loss_range['id'][0],
                'max_packet_loss': loss_range['id'][1]
            }

        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

from flask import request
from flask_restful import Resource
from flask_jwt_extended import get_jwt_claims

import iot_logging, calendar, json, math
log = iot_logging.getLogger(__name__)

from collections import namedtuple

from iot_api.user_api.model import User
from iot_api.user_api.Utils import is_system
from iot_api.user_api.JwtUtils import admin_regular_allowed
from iot_api.user_api.repository import ResourceUsageRepository, TagRepository, PacketRepository
from iot_api.user_api import Error

class ResourceUsageInformationAPI(Resource):
    """ Endpoint to get the resource usage of an asset of a given type
    Request parameters:
        - asset_type (required): type of requested asset (can be device or gateway).
        - asset_id (required): database id of the asset
        - min_rssi: for filtering packets list, return only packets with rssi not lower than this value
        - max_rssi: for filtering packets list, return only packets with rssi not higher than this value
        - min_lsnr: for filtering packets list, return only packets with lsnr not lower than this value
        - max_lsnr: for filtering packets list, return only packets with lsnr not higher than this value
    Returns:
        - JSON with requested resource usage information.
    """
    @admin_regular_allowed
    def get(self, asset_type, asset_id):
        organization_id = get_jwt_claims().get('organization_id')
        asset = ResourceUsageRepository.get_with(asset_id, asset_type,  organization_id)

        packets = None
        lsnr_values = None
        rssi_values = None
        if asset_type == 'device':
            temp_packets = PacketRepository.get_with(
                    ids_list=json.loads(asset.last_packets_list),
                    min_rssi = request.args.get('min_rssi', default = None, type=int),
                    max_rssi = request.args.get('max_rssi', default = None, type=int),
                    min_lsnr = request.args.get('min_lsnr', default = None, type=float),
                    max_lsnr = request.args.get('max_lsnr', default = None, type=float)
                )
            packets = []
            for packet, gw_id in temp_packets:
                to_add = packet.to_json()
                to_add.update({'gateway_id': gw_id})
                packets.append(to_add)
            lsnr_values = [packet['lsnr'] for packet in packets if packet['lsnr'] is not None]
            rssi_values = [packet['rssi'] for packet in packets if packet['rssi'] is not None]

        # Here the dev_addr is not returned to avoid a join between Device and DeviceSession,
        # since it is already reported in the inventory endpoint.
        response = {
            'id': asset.id,
            'hex_id': asset.hex_id,
            'type': asset.type,
            'name': asset.name,
            'data_collector': asset.data_collector,
            'app_name': asset.app_name,
            'connected': asset.connected,
            'last_activity': calendar.timegm(asset.last_activity.timetuple()),
            'activity_freq': asset.activity_freq,
            'activity_freq_variance': asset.activity_freq_variance,
            'is_regular': asset_is_regular(asset),
            'packets_up': buildPacketsInfo(asset.PACKETS_UP, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)),
            'packets_down': buildPacketsInfo(asset.PACKETS_DOWN, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)),
            'packets_lost': buildPacketsInfo(asset.PACKETS_LOST, asset.PACKETS_UP+asset.PACKETS_DOWN+(asset.PACKETS_LOST or 0)) if asset.type == 'Device' else None,
            'retransmissions': asset.RETRANSMISSIONS,
            'join_requests': asset.JOIN_REQUESTS,
            'failed_join_requests': asset.FAILED_JOIN_REQUESTS,
            'max_rssi': asset.max_rssi,
            'max_lsnr': asset.max_lsnr,
            'ngateways_connected_to': asset.ngateways_connected_to,
            'payload_size': asset.payload_size,
            'tags': [{
                "id": tag.id,
                "name": tag.name,
                "color": tag.color
            } for tag in TagRepository.list_asset_tags(asset_id, asset_type, organization_id)],
            'last_packets_list': packets,
            'min_lsnr_packets': min(lsnr_values) if lsnr_values and len(lsnr_values) > 0 else None,
            'max_lsnr_packets': max(lsnr_values) if lsnr_values and len(lsnr_values) > 0 else None,
            'min_rssi_packets': min(rssi_values) if rssi_values and len(rssi_values) > 0 else None,
            'max_rssi_packets': max(rssi_values) if rssi_values and len(rssi_values) > 0 else None,
        }

        return response, 200

class ResourceUsageListAPI(Resource):
    """ Endpoint to list assets (devices + gateways)
    Request parameters (all optional):
        - page: for pagination.
        - size: for pagination.
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - JSON with list of assets and their resource usage (see code for more details about the fields).
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')
        page = request.args.get('page', default=1, type=int)
        size = request.args.get('size', default=20, type=int)
        
        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                return {
                        'assets': [],
                        'total_pages': 0,
                        'total_items': 0
                    }, 200

        results = ResourceUsageRepository.list_all(
            organization_id = organization_id,
            page = page, size = size,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids=request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )

        assets = [{
            'id': dev.id,
            'hex_id': dev.hex_id,
            'dev_addr': dev.dev_addr,
            'type': dev.type,
            'name': dev.name,
            'data_collector': dev.data_collector,
            'app_name': dev.app_name,
            'connected': dev.connected,
            'last_activity': calendar.timegm(dev.last_activity.timetuple()),
            'activity_freq': dev.activity_freq,
            'activity_freq_variance': dev.activity_freq_variance,
            'is_regular': asset_is_regular(dev),
            'packets_up': buildPacketsInfo(dev.PACKETS_UP, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)),
            'packets_down': buildPacketsInfo(dev.PACKETS_DOWN, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)),
            'packets_lost': buildPacketsInfo(dev.PACKETS_LOST, dev.PACKETS_UP+dev.PACKETS_DOWN+(dev.PACKETS_LOST or 0)) if dev.type == 'Device' else None,
            'retransmissions': dev.RETRANSMISSIONS,
            'join_requests': dev.JOIN_REQUESTS,
            'failed_join_requests': dev.FAILED_JOIN_REQUESTS,
            'max_rssi': dev.max_rssi,
            'max_lsnr': dev.max_lsnr,
            'payload_size':dev.payload_size,
            'ngateways_connected_to':dev.ngateways_connected_to
        } for dev in results.items]
        response = {
            'assets': assets,
            'total_pages': results.pages,
            'total_items': results.total
        }
        return response, 200

def buildPacketsInfo(count, total):
    """ Helper function to calculate data related with packets received from an asset
    Request parameters (all required):
        - count: number of packets of the type (up, down, lost) to calculate data for
        - total: number of packets in total
    Returns:
        - JSON with packets related information (see code for more details about the fields).
    """
    return {
        'total': count,
        'percentage': 100*count/total if total else None
    }

def asset_is_regular(asset):
    params = json.loads(asset.policy_parameters or '{}')

    #Consider the asset as regular if irregularity cannot be checked
    if 'deviation_tolerance' not in params or asset.type == 'Gateway' or not asset.activity_freq:
        return True

    return math.sqrt(asset.activity_freq_variance)/asset.activity_freq <= params['deviation_tolerance']

class ResourceUsagePerStatusCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by status (connected/disconnected).
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has three fields: id, name, count. (id = name = 'connected'/'disconnected')
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')   

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"
        
        groups = ResourceUsageRepository.count_per_status(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerGatewayCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by gateway.
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has three fields: id, name, count. (name = hex_id)
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_gateway(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerSignalStrengthCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by signal strength.
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has the following structure:
            'id': {
                'min_signal_strength': int,
                'max_signal_strength': int
            },
            'name': 'Unusable'/'Very weak'/'Weak'/'Okay'/'Great'/'Excellent',
            'count': int
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_signal_strength(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        # Make the ids a JSON object instead of a tuple
        for signal_range in groups:
            signal_range['id'] = {
                'min_signal_strength': signal_range['id'][0],
                'max_signal_strength': signal_range['id'][1]
            }

        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

class ResourceUsagePerPacketLossCountAPI(Resource):
    """ Endpoint to count assets (devices+gateways) grouped by packet loss percentage
    Request parameters: 
        - asset_type: for filtering, count only this type of asset ("device" or "gateway").
        - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").
        - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.
        - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.
        - device_ids[]: for filtering, list only the assets related to ANY of these devices
        - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)
        - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)
        - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)
        - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)
    Returns:
        - A list of JSONs, where each JSON has the following structure:
            'id': {
                'min_packet_loss': int,
                'max_packet_loss': int
            },
            'name': '[0,10)'/'[10,20)'/.../'[80,90)'/'[90,100]',
            'count': int
    """
    @admin_regular_allowed
    def get(self):
        organization_id = get_jwt_claims().get('organization_id')

        asset_type = request.args.get('asset_type', default=None, type=str)

        min_signal_strength = request.args.get('min_signal_strength', default = None, type=int)
        max_signal_strength = request.args.get('max_signal_strength', default = None, type=int)
        min_packet_loss = request.args.get('min_packet_loss', default = None, type=int)
        max_packet_loss = request.args.get('max_packet_loss', default = None, type=int)

        if any(param is not None for param in [min_signal_strength, max_signal_strength, min_packet_loss, max_packet_loss]):
            if asset_type is None:
                asset_type = "device"
            elif asset_type == "gateway":
                asset_type = "none"

        groups = ResourceUsageRepository.count_per_packet_loss(
            organization_id = organization_id,
            asset_type = asset_type,
            asset_status = request.args.get('asset_status', default=None, type=str),
            data_collector_ids = request.args.getlist('data_collector_ids[]'),
            gateway_ids = request.args.getlist('gateway_ids[]'),
            device_ids = request.args.getlist('device_ids[]'),
            min_signal_strength = min_signal_strength,
            max_signal_strength = max_signal_strength,
            min_packet_loss = min_packet_loss,
            max_packet_loss = max_packet_loss
        )
        # Make the ids a JSON object instead of a tuple
        for loss_range in groups:
            loss_range['id'] = {
                'min_packet_loss': loss_range['id'][0],
                'max_packet_loss': loss_range['id'][1]
            }

        return {
            'total_count': sum(group['count'] for group in groups),
            'groups': groups
        }, 200

Endpoint to get the resource usage of an asset of a given type     Request parameters:         - asset_type (required): type of requested asset (can be device or gateway).         - asset_id (required): database id of the asset         - min_rssi: for filtering packets list, return only packets with rssi not lower than this value         - max_rssi: for filtering packets list, return only packets with rssi not higher than this value         - min_lsnr: for filtering packets list, return only packets with lsnr not lower than this value         - max_lsnr: for filtering packets list, return only packets with lsnr not higher than this value     Returns:         - JSON with requested resource usage information. # Here the dev_addr is not returned to avoid a join between Device and DeviceSession, # since it is already reported in the inventory endpoint. Endpoint to list assets (devices + gateways)     Request parameters (all optional):         - page: for pagination.         - size: for pagination.         - asset_type: for filtering, count only this type of asset ("device" or "gateway").         - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").         - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.         - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.         - device_ids[]: for filtering, list only the assets related to ANY of these devices         - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)         - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)         - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)         - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)     Returns:         - JSON with list of assets and their resource usage (see code for more details about the fields). Helper function to calculate data related with packets received from an asset     Request parameters (all required):         - count: number of packets of the type (up, down, lost) to calculate data for         - total: number of packets in total     Returns:         - JSON with packets related information (see code for more details about the fields). #Consider the asset as regular if irregularity cannot be checked Endpoint to count assets (devices+gateways) grouped by status (connected/disconnected).     Request parameters:          - asset_type: for filtering, count only this type of asset ("device" or "gateway").         - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").         - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.         - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.         - device_ids[]: for filtering, list only the assets related to ANY of these devices         - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)         - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)         - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)         - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)     Returns:         - A list of JSONs, where each JSON has three fields: id, name, count. (id = name = 'connected'/'disconnected') Endpoint to count assets (devices+gateways) grouped by gateway.     Request parameters:          - asset_type: for filtering, count only this type of asset ("device" or "gateway").         - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").         - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.         - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.         - device_ids[]: for filtering, list only the assets related to ANY of these devices         - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)         - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)         - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)         - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)     Returns:         - A list of JSONs, where each JSON has three fields: id, name, count. (name = hex_id) Endpoint to count assets (devices+gateways) grouped by signal strength.     Request parameters:          - asset_type: for filtering, count only this type of asset ("device" or "gateway").         - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").         - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.         - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.         - device_ids[]: for filtering, list only the assets related to ANY of these devices         - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)         - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)         - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)         - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)     Returns:         - A list of JSONs, where each JSON has the following structure:             'id': {                 'min_signal_strength': int,                 'max_signal_strength': int             },             'name': 'Unusable'/'Very weak'/'Weak'/'Okay'/'Great'/'Excellent',             'count': int # Make the ids a JSON object instead of a tuple Endpoint to count assets (devices+gateways) grouped by packet loss percentage     Request parameters:          - asset_type: for filtering, count only this type of asset ("device" or "gateway").         - asset_status: for filtering, count only assets with this status ("connected" or "disconnected").         - data_collector_ids[]: for filtering, count only the assets belongs to these data collectors.         - gateway_ids[]: for filtering, count only the assets connected to ANY one of these gateways.         - device_ids[]: for filtering, list only the assets related to ANY of these devices         - min_signal_strength: for filtering, count only the assets with signal strength not lower than this value (dBm)         - max_signal_strength: for filtering, count only the assets with signal strength not higher than this value (dBm)         - min_packet_loss: for filtering, count only the assets with packet loss not lower than this value (percentage)         - max_packet_loss: for filtering, count only the assets with packet loss not higher than this value (percentage)     Returns:         - A list of JSONs, where each JSON has the following structure:             'id': {                 'min_packet_loss': int,                 'max_packet_loss': int             },             'name': '[0,10)'/'[10,20)'/.../'[80,90)'/'[90,100]',             'count': int # Make the ids a JSON object instead of a tuple