"""
mcpython - a minecraft clone written in python licenced under the MIT-licence
(https://github.com/mcpython4-coding/core)

Contributors: uuk, xkcdjerry (inactive)

Based on the game of fogleman (https://github.com/fogleman/Minecraft), licenced under the MIT-licence
Original game "minecraft" by Mojang Studios (www.minecraft.net), licenced under the EULA
(https://account.mojang.com/documents/minecraft_eula)
Mod loader inspired by "Minecraft Forge" (https://github.com/MinecraftForge/MinecraftForge) and similar

This project is not official by mojang and does not relate to it.
"""
import asyncio
import itertools
import time
import typing

from mcpython import shared
from mcpython.common.world.NetworkSyncedImplementation import NetworkSyncedDimension
from mcpython.engine import logger
from mcpython.engine.network.AbstractPackage import AbstractPackage
from mcpython.engine.network.util import ReadBuffer, WriteBuffer

from .DisconnectionPackage import DisconnectionInitPackage
from .PlayerInfoPackages import PlayerInfoPackage

class DataRequestPackage(AbstractPackage):
    PACKAGE_NAME = "minecraft:world_data_request"

    def __init__(self):
        super().__init__()
        self.request_world_info_state = False
        self.request_player_info_state = False

        self.requested_dimensions = []
        self.requested_chunks = []

    def request_world_info(self):
        self.request_world_info_state = True
        return self

    def request_dimension_info(self, name: str):
        self.requested_dimensions.append(name)
        return self

    def request_chunk(self, dimension: str, cx: int, cz: int):
        # traceback.print_stack()
        self.requested_chunks.append((dimension, cx, cz))
        return self

    def request_player_info(self):
        self.request_player_info_state = True
        return self

    async def write_to_buffer(self, buffer: WriteBuffer):
        buffer.write_bool(self.request_world_info_state)
        buffer.write_bool(self.request_player_info_state)
        await buffer.write_list(self.requested_dimensions, buffer.write_string)
        await buffer.write_list(
            self.requested_chunks,
            lambda e: buffer.write_string(e[0]).write_int(e[1]).write_int(e[1]),
        )

    async def read_from_buffer(self, buffer: ReadBuffer):
        self.request_world_info_state = buffer.read_bool()
        self.request_player_info_state = buffer.read_bool()
        self.requested_dimensions = await buffer.collect_list(buffer.read_string)
        self.requested_chunks = await buffer.collect_list(
            lambda: (buffer.read_string(), buffer.read_int(), buffer.read_int())
        )

    async def handle_inner(self):
        if self.request_world_info_state:
            await self.answer(WorldInfoPackage().setup())

        if self.request_player_info_state:
            await self.answer(PlayerInfoPackage().setup())

        for dimension in self.requested_dimensions:
            await self.answer(DimensionInfoPackage().setup(dimension))

        for dim, cx, cz in self.requested_chunks:
            logger.println(f"collecting chunk information for chunk @{cx}:{cz}@{dim}")
            await self.answer(ChunkDataPackage().setup(dim, (cx, cz)))

class WorldInfoPackage(AbstractPackage):
    """
    Package server -> client for sending requested data back to client.

    Mostly only for sync stuff, but dimensions should be created when needed
    """

    PACKAGE_NAME = "minecraft:world_info"

    def __init__(self):
        super().__init__()
        self.dimensions = []
        self.spawn_point = 0, 0

    def setup(self):
        self.spawn_point = shared.world.spawn_point

        for dim_id, dim in shared.world.dimensions.items():
            self.dimensions.append(
                (dim.get_name(), dim_id, dim.get_world_height_range())
            )

        return self

    async def write_to_buffer(self, buffer: WriteBuffer):
        buffer.write_int(self.spawn_point[0]).write_int(self.spawn_point[1])

        await buffer.write_list(
            self.dimensions,
            lambda e: buffer.write_string(e[0])
            .write_int(e[1])
            .write_int(e[2][0])
            .write_int(e[2][1]),
        )

    async def read_from_buffer(self, buffer: ReadBuffer):
        self.spawn_point = buffer.read_int(), buffer.read_int()

        self.dimensions = await buffer.collect_list(
            lambda: (
                buffer.read_string(),
                buffer.read_int(),
                (buffer.read_int(), buffer.read_int()),
            )
        )

    async def handle_inner(self):
        shared.world.spawn_point = self.spawn_point

        for name, dim_id, height_range in self.dimensions:
            logger.println(f"[NETWORK][WORLD] got dimension info of dimension '{name}'")

            dim = shared.world.get_dimension_by_name(name)

            if not isinstance(dim, NetworkSyncedDimension):
                logger.println(
                    "[NETWORK][WORLD] exchanging for a network sync-ed one..."
                )

                for chunk in dim.chunk_iterator():
                    chunk.hide()

                new_dim = shared.world.dimensions[
                    dim.get_dimension_id()
                ] = NetworkSyncedDimension(
                    shared.world,
                    dim.get_dimension_id(),
                    dim.get_name(),
                    dim.world_generation_config,
                )
                new_dim.world_generation_config_objects = (
                    dim.world_generation_config_objects
                )

                for entity in dim.entity_iterator():
                    entity.dimension = new_dim

            if dim.get_dimension_id() != dim_id:
                await self.answer(
                    DisconnectionInitPackage().set_reason("world dim id miss-match")
                )

            if dim.get_world_height_range() != height_range:
                await self.answer(
                    DisconnectionInitPackage().set_reason("world height miss-match")
                )

class DimensionInfoPackage(AbstractPackage):
    PACKAGE_NAME = "minecraft:dimension_info"

    def __init__(self):
        super().__init__()

    def setup(self, dimension: str):
        return self

class ChunkDataPackage(AbstractPackage):
    PACKAGE_NAME = "minecraft:chunk_data"

    def __init__(self):
        super().__init__()
        self.dimension = "overworld"
        self.position = 0, 0
        self.force = False

        self.blocks = []

    def setup(self, dim: str, position: typing.Tuple[int, int], force=False):
        self.dimension = dim
        self.position = position
        self.force = force

        chunk = shared.world.get_dimension_by_name(dim).get_chunk(position)

        dx, dz = chunk.position[0] * 16, chunk.position[1] * 16

        for x, y, z in itertools.product(range(16), range(256), range(16)):
            x += dx
            z += dz

            b = chunk.get_block((x, y, z), none_if_str=True)
            self.blocks.append(b)

        return self

    async def write_to_buffer(self, buffer: WriteBuffer):
        start = time.time()
        logger.println(
            f"preparing chunk data for chunk @{self.position[0]}:{self.position[1]}@{self.dimension} for networking"
        )
        chunk = shared.world.get_dimension_by_name(self.dimension).get_chunk(
            self.position
        )

        buffer.write_string(self.dimension)
        buffer.write_int(self.position[0]).write_int(self.position[1])
        buffer.write_bool(self.force)

        for b in self.blocks:
            if b is None:
                buffer.write_bool_group([False, False])
            else:
                buffer.write_bool_group([True, chunk.exposed(b.position)])
                buffer.write_string(b.NAME)
                await b.write_to_network_buffer(buffer)

        logger.println(f"-> chunk data ready (took {time.time() - start}s)")

    async def read_from_buffer(self, buffer: ReadBuffer):
        start = time.time()
        logger.println(
            f"preparing chunk data for chunk @{self.position[0]}:{self.position[1]}@{self.dimension} to world"
        )

        self.dimension = buffer.read_string()
        self.position = buffer.read_int(), buffer.read_int()
        self.force = buffer.read_bool()

        for _ in range(16 * 256 * 16):
            is_block, visible = buffer.read_bool_group(2)

            if not is_block:
                self.blocks.append(None)
            else:
                name = buffer.read_string()
                instance = shared.registry.get_by_name("minecraft:block").get(name)()
                await instance.read_from_network_buffer(buffer)
                self.blocks.append((instance, visible))

        logger.println(f"-> chunk data ready (took {time.time() - start}s)")

    async def handle_inner(self):
        start = time.time()
        logger.println(
            f"adding chunk data for chunk @{self.position[0]}:{self.position[1]}@{self.dimension} to world"
        )
        chunk = shared.world.get_dimension_by_name(self.dimension).get_chunk(
            self.position
        )

        if chunk.loaded and not self.force:
            logger.println("-> skipping as chunk exists in game and is loaded")
            return

        dx, dz = self.position

        targets = []
        i = 0
        for x, y, z in itertools.product(
            range(dx * 16, dx * 16 + 16), range(256), range(dz * 16, dz * 16 + 16)
        ):
            block = self.blocks[i]

            if block is not None:
                targets.append(
                    chunk.add_block(
                        (x, y, z),
                        block[0],
                        immediate=False,
                        block_update=False,
                        network_sync=False,
                    )
                )

            i += 1

        await asyncio.gather(*targets)

        chunk.update_all_rendering()

        logger.println(f"-> chunk data fully added (took {time.time()-start}s)")
        chunk.loaded = True

class ChunkBlockChangePackage(AbstractPackage):
    PACKAGE_NAME = "minecraft:chunk_block_update"

    def __init__(self):
        super().__init__()
        self.dimension = None
        self.data = []

    def set_dimension(self, dimension: str):
        self.dimension = dimension
        return self

    def change_position(
        self, position: typing.Tuple[int, int, int], block, update_only=False
    ):
        """
        Updates the block data at a given position in the given dimension

        :param position: the position
        :param block: the block instance
        :param update_only: if to only update the block, not add a new one
        """
        self.data.append((position, block, update_only))
        return self

    async def write_to_network_buffer(self, buffer: WriteBuffer):
        buffer.write_string(self.dimension)

        async def write(e):
            position, block, update_only = e
            buffer.write_long(position[0])
            buffer.write_long(position[1])
            buffer.write_long(position[2])

            if block is None:
                buffer.write_bool_group([False, False])
            else:
                buffer.write_bool_group([True, update_only])
                buffer.write_string(block.NAME)
                await block.write_to_network_buffer(buffer)

        await buffer.write_list(self.data, write)

    async def read_from_network_buffer(self, buffer: ReadBuffer):
        self.dimension = buffer.read_string()

        dimension = shared.world.get_dimension_by_name(self.dimension)

        async def read():
            position = tuple((buffer.read_long() for _ in range(3)))

            is_block, update_only = buffer.read_bool_group(2)

            if not is_block:
                self.data.append((position, None, update_only))
            else:
                name = buffer.read_string()

                if update_only:
                    b = dimension.get_block(position, none_if_str=True)

                    if b is None:
                        logger.println(
                            f"[WARM] got block internal update for block {position} in {self.dimension}, but no block is there!"
                        )
                        return

                    await b.read_from_network_buffer(buffer)

                else:
                    instance = shared.registry.get_by_name("minecraft:block").get(
                        name
                    )()
                    await instance.read_from_network_buffer(buffer)
                    self.data.append((position, instance, update_only))

        self.data = await buffer.collect_list(read)

    async def handle_inner(self):
        dimension = shared.world.get_dimension_by_name(self.dimension)

        targets = []
        for position, block, update_only in self.data:
            targets.append(
                dimension.add_block(
                    position, block, network_sync=False, block_update=False
                )
            )
        await asyncio.gather(*targets)


# Python 2.7
import urllib
from bs4 import BeautifulSoup

# Finds the average amount of replies (per thread) on the first page of the Starcraft II General Discussion Forums.
def readLog():
        url = 'http://us.battle.net/sc2/en/forum/40568/'
        soup = BeautifulSoup(urllib.urlopen(url).read())

        sumOfReplies = 0
        for thread in range(43):
                replyCount = soup.findAll('td', {'class':'reply-cell'})[thread].contents[0]
                sumOfReplies += int(replyCount)
        replyAverage = (int(sumOfReplies) / 44.00)
        print("The average reply count is: %s" % replyAverage)
readLog()


import cv2
from spectral import imshow, view_cube
import numpy as np
import scipy.misc
import matplotlib.pyplot as plt
import matplotlib
import spectral.io.envi as envi

repo_dir = '/home/nodeflux/gits/skripsi'
date_dir = '11042019'
num = '1'

dark_ref = envi.open('{}/datasets/training/{}/dark_ref/capture/dark_ref.hdr'.format(repo_dir, date_dir), '{}/datasets/training/{}/dark_ref/capture/dark_ref.raw'.format(repo_dir, date_dir))
white_ref = envi.open('{}/datasets/training/{}/white_ref/capture/white_ref.hdr'.format(repo_dir, date_dir), '{}/datasets/training/{}/white_ref/capture/white_ref.raw'.format(repo_dir, date_dir))
data_ref = envi.open('{}/datasets/training/{}/bisbul_{}_cut/capture/bisbul_{}_cut.hdr'.format(repo_dir, date_dir, num, num), '{}/datasets/training/{}/bisbul_{}_cut/capture/bisbul_{}_cut.raw'.format(repo_dir, date_dir, num, num))

white_nparr = np.array(white_ref.load())
dark_nparr = np.array(dark_ref.load())
data_nparr = np.array(data_ref.load())

# imshow(data_nparr, (64, 55, 19))

corrected_nparr = np.divide(
    np.subtract(data_nparr, dark_nparr),
    np.subtract(white_nparr, dark_nparr))

corrected_nparr.shape

corrected_nparr = corrected_nparr[120:320, 130:380, :]

# imshow(corrected_nparr, (32, 32, 32))

from numpy import genfromtxt

bands = genfromtxt('{}/datasets/training/helpers/bands.csv'.format(repo_dir), delimiter=',')

leaf_pixel_y = 150
leaf_pixel_x = 150
teflon_pixel_y = 100
teflon_pixel_x = 100

leaf_pixel = corrected_nparr[
    leaf_pixel_y:leaf_pixel_y+1,
    leaf_pixel_x:leaf_pixel_x+1,
    :]
teflon_pixel = corrected_nparr[
    teflon_pixel_y:teflon_pixel_y+1,
    teflon_pixel_x:teflon_pixel_x+1,
    :]

leaf_pixel_squeezed = np.squeeze(leaf_pixel)
teflon_pixel_squeezed = np.squeeze(teflon_pixel)

def hsi2rgb(hsi_nparr, rgb):
    (r, g, b) = rgb
    rgb_nparr = np.dstack((hsi_nparr[:, :, r], hsi_nparr[:, :, g], hsi_nparr[:, :, b]))
    return rgb_nparr

def extract_roi(arr, x, y, w, h):
    roi = arr[y:y+h, x:x+w, :]

    return roi

def draw_bbox(img, x, y, w, h, line):
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), line)
    return img

def extract_rois(coordinates, hsi_nparr, length, line):
    rgb_nparr = hsi2rgb(hsi_nparr, (100, 100, 100))
    img_with_bbox = rgb_nparr
    rois = [] # returned ROIs

    for coordinate in coordinates:
        (x, y) = coordinate
        img_with_bbox = draw_bbox(img_with_bbox, x, y, length, length, line)

        roi = extract_roi(hsi_nparr, x, y, length, length)

        rois.append(roi)

    return rois, img_with_bbox

length = 25 # width and height
line = 2 # bounding box line width

coordinates = [
    (15, 28),
    (100, 23),
    (198, 22),
    (20, 155),
    (114, 148),
    (202, 142)]

rois, bounding_boxed = extract_rois(coordinates, corrected_nparr, length, line)
cv2.imshow('image', bounding_boxed)

# for item in rois:
#     imshow(item, (100, 100, 100))

np.save("{}/datasets/training/{}/bisbul_{}_rois.npy".format(repo_dir, date_dir, num), rois)
np.save("{}/datasets/training/{}/bisbul_{}_corrected.npy".format(repo_dir, date_dir, num), corrected_nparr)

# for i in range(len(rois)):
#     roi = rois[i]
#     intensity = []
#     for b in range(roi.shape[2]):
#         intensity.append(np.mean(roi[:, :, b]))
#     plt.plot(bands, intensity, label='ROI {}'.format(i+1))

# plt.legend(loc='upper left')
# plt.title('Leaf Spectral Footprint\n Mean in ROI Area')
# plt.xlabel('Wavelength (nm)')
# plt.ylabel('Reflectance')
# plt.show()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import mock
import tensorflow as tf

from models import model
from models.layers import variables

class ModelTest(tf.test.TestCase):

  def setUp(self):
    self.hparams = tf.contrib.training.HParams(
        learning_rate=0.001,
        decay_rate=0.96,
        decay_steps=1,
        loss_type='softmax')

  class MockModel(model.Model):
    """Implements toy inference method without variable declaration."""

    def inference(self, features):
      logits = tf.constant([[0.1, 0.2, 0.7]])
      return model.Inferred(logits, None)

  class MockModelWithVariable(model.Model):
    """Implements toy inference method with variable declaration."""

    def inference(self, features):
      logits = variables.bias_variable([1, 3])
      return model.Inferred(logits, None)

  @mock.patch.multiple(model.Model, __abstractmethods__=set())
  def testModelInitialization(self):
    """Checks the variables declared in the init method.

      Initialization step should only declare global_step as a non trainable
      variable.
    """
    with tf.Graph().as_default():
      model.Model(self.hparams)
      trainable_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
      self.assertEqual(len(trainable_vars), 0)
      global_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
      self.assertEqual(len(global_vars), 1)
      self.assertStartsWith(global_vars[0].name, 'global_step')

  def testSingleTower_WithoutVariable(self):
    """Checks model will raise error when there is no trainable variable."""
    with tf.Graph().as_default():
      test_model = self.MockModel(self.hparams)
      feature = {
          'labels': tf.one_hot([2], 3),
          'num_targets': 1,
      }
      with self.assertRaises(ValueError):
        test_model._single_tower(0, feature)

  def testSingleTower_MultipleCalls(self):
    """Checks the correct variable size after multiple single_tower calls.

      Multiple towers should not declare multiple variables and should share the
      trainable variables. Therefore, variable set size should stay at 1.
      Each tower should have its own operations therefore, total number of
      operations should increase for each tower by the same amount.
    """
    with tf.Graph().as_default() as graph:
      test_model = self.MockModelWithVariable(self.hparams)
      feature = {
          'labels': tf.one_hot([2], 3),
          'num_targets': 1,
      }
      test_model._single_tower(0, feature)
      first_ops = graph.get_operations()
      first_op_num = len(first_ops)
      test_model._single_tower(1, feature)
      second_ops = graph.get_operations()
      second_op_num = len(second_ops)
      test_model._single_tower(2, feature)
      third_ops = graph.get_operations()
      third_op_num = len(third_ops)
      trainable_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
      self.assertEqual(len(trainable_vars), 1)
      self.assertEqual(second_op_num - first_op_num,
                       third_op_num - second_op_num)

  # Model is an abstract class. mock.patch empties the abstractmethods set
  # so that it can be initializable for testing other functions.
  @mock.patch.multiple(model.Model, __abstractmethods__=set())
  def testAverageGradients(self):
    """Checks the correct average for multiple towers and multiple variables.

    The test model has 2 towers with 2 variables shared between them.
    var_0 is getting 1.0 + 3.0 as gradient -> average: 2.0
    var_1 is getting 2.0 + 4.0 as gradient -> average: 3.0
    """
    with tf.Graph().as_default():
      with tf.Session() as session:
        test_model = model.Model(self.hparams)
        grad_0 = tf.constant(1.0)
        grad_1 = tf.constant(2.0)
        tower_0 = [(grad_0, 'var_0'), (grad_1, 'var_1')]
        grad_2 = tf.constant(3.0)
        grad_3 = tf.constant(4.0)
        tower_1 = [(grad_2, 'var_0'), (grad_3, 'var_1')]
        tower_grads = [tower_0, tower_1]
        average_grads = test_model._average_gradients(tower_grads)
        self.assertEqual(len(average_grads), 2)
        self.assertEqual('var_0', average_grads[0][1])
        average_grad_0 = session.run(average_grads[0][0])
        self.assertEqual(2.0, average_grad_0)

        self.assertEqual('var_1', average_grads[1][1])
        average_grad_1 = session.run(average_grads[1][0])
        self.assertEqual(3.0, average_grad_1)

  def testMultiGpu(self):
    """Checks the correct attribute values after multi_gpu call.

    Since tests don't have GPU access, test only covers the correct number of
    elements in the result lists.
    """
    with tf.Graph().as_default():
      test_model = self.MockModelWithVariable(self.hparams)
      feature = {
          'labels': tf.one_hot([2], 3),
          'num_targets': 1,
      }
      test_model.multi_gpu([feature, feature, feature], 3)
      trainable_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
      self.assertEqual(len(trainable_vars), 1)

if __name__ == '__main__':
  tf.test.main()


import asyncio
import inspect
import logging
import os
import typing
import uuid

import opentelemetry.instrumentation.asgi
import quart
import quart.sessions
import quart_session

from app import (AppDatabase, AppFunctions, AppRequest, AppSSO, AppTables,
                 AppTemplates)
from app.tasks import (AppAccessControlTask, AppMoonYieldTask,
                       AppStructureNotificationTask, AppStructurePollingTask,
                       AppStructureTask, AppTask, ESIAllianceBackfillTask,
                       ESIUniverseConstellationsBackfillTask,
                       ESIUniverseRegionsBackfillTask,
                       ESIUniverseSystemsBackfillTask)
from support.telemetry import otel, otel_initialize

app: typing.Final = quart.Quart(__name__)

app.logger.setLevel(logging.INFO)

app.config.from_mapping({
    "DEBUG": False,
    "PORT": 5050,
    "SECRET_KEY": uuid.uuid4().hex,
    "SESSION_TYPE": "redis",
    "SESSION_REVERSE_PROXY": True,
    "SESSION_PERMANENT": True,
    # "SESSION_PROTECTION": True,
    "SESSION_COOKIE_HTTPONLY": True,
    "BASEDIR": os.path.dirname(os.path.realpath(__file__)),
    "EVEONLINE_CLIENT_ID": os.getenv("EVEONLINE_CLIENT_ID", ""),
    "EVEONLINE_CLIENT_SECRET": os.getenv("EVEONLINE_CLIENT_SECRET", ""),
    "SQLALCHEMY_DB_URL": os.getenv("SQLALCHEMY_DB_URL", ""),
    "TEMPLATES_AUTO_RELOAD": True,
    # "SEND_FILE_MAX_AGE_DEFAULT": 300,
    "SEND_FILE_MAX_AGE_DEFAULT": 30,
    "MAX_CONTENT_LENGTH": 512 * 1024,
    "BODY_TIMEOUT": 15,
    "RESPONSE_TIMEOUT": 15,
})

evesso_config: typing.Final = {
    "client_id": app.config.get("EVEONLINE_CLIENT_ID"),
    "client_secret": app.config.get("EVEONLINE_CLIENT_SECRET"),
    "scopes": [
        "publicData",
        "esi-characters.read_corporation_roles.v1",
        "esi-corporations.read_structures.v1",
        "esi-industry.read_corporation_mining.v1"
    ]
}

evedb: typing.Final = AppDatabase(
    app.config.get("SQLALCHEMY_DB_URL", "sqlite+pysqlite://"),
)
quart_session.Session(app)
eveevents: typing.Final = asyncio.Queue()
evesso: typing.Final = AppSSO(app, evedb, eveevents, **evesso_config)
evesession: typing.Final = app.session_interface.session_class(sid="global", permanent=False)
evesession[AppTask.CONFIGDIR] = os.path.abspath(os.path.join(app.config.get("BASEDIR", "."), "data"))

@app.before_serving
@otel
async def _before_serving() -> None:
    if not bool(evesession.get("setup_tasks_started", False)):
        evesession["setup_tasks_started"] = True

        AppStructureNotificationTask(evesession, evedb, eveevents, app.logger)

        ESIUniverseRegionsBackfillTask(evesession, evedb, eveevents, app.logger)
        ESIUniverseConstellationsBackfillTask(evesession, evedb, eveevents, app.logger)
        ESIUniverseSystemsBackfillTask(evesession, evedb, eveevents, app.logger)
        ESIAllianceBackfillTask(evesession, evedb, eveevents, app.logger)

        AppAccessControlTask(evesession, evedb, eveevents, app.logger)
        AppMoonYieldTask(evesession, evedb, eveevents, app.logger)

        AppStructurePollingTask(evesession, evedb, eveevents, app.logger)

@app.errorhandler(404)
async def error_404(path: str) -> quart.ResponseReturnValue:
    return quart.redirect("/")

@app.route("/usage/", methods=["GET"])
@otel
async def _usage() -> quart.ResponseReturnValue:

    ar: typing.Final[AppRequest] = await AppFunctions.get_app_request(evedb, quart.session, quart.request)
    if ar.character_id > 0 and ar.suspect:
        quart.session.clear()

    elif ar.character_id > 0 and ar.permitted and ar.contributor:

        permitted_data: typing.Final = list()
        denied_data: typing.Final = list()

        try:
            async with await evedb.sessionmaker() as session:
                permitted_data.extend(await AppFunctions.get_usage(session, True, ar.ts))
                denied_data.extend(await AppFunctions.get_usage(session, False, ar.ts))

        except Exception as ex:
            app.logger.error(f"{inspect.currentframe().f_code.co_name}: {ex}")

        return await quart.render_template(
            "usage.html",
            character_id=ar.character_id,
            permitted_usage=permitted_data, denied_usage=denied_data)

    return quart.redirect("/about/")

@app.route("/about/", methods=["GET"])
@otel
async def _about() -> quart.ResponseReturnValue:

    _: typing.Final = await AppFunctions.get_app_request(evedb, quart.session, quart.request)

    return await quart.render_template("about.html")

@app.route('/moon', defaults={'moon_id': 0}, methods=["GET"])
@app.route('/moon/<int:moon_id>', methods=["GET"])
@otel
async def _moon(moon_id: int) -> quart.ResponseReturnValue:

    ar: typing.Final[AppRequest] = await AppFunctions.get_app_request(evedb, quart.session, quart.request)
    if ar.character_id > 0 and ar.suspect:
        quart.session.clear()

    elif ar.character_id > 0 and ar.permitted:

        moon_history: typing.Final = list()
        moon_yield: typing.Final = list()

        try:
            async with await evedb.sessionmaker() as session:
                moon_history.extend(await AppFunctions.get_moon_history(session, moon_id, ar.ts))
                moon_yield.extend(await AppFunctions.get_moon_yield(session, moon_id, ar.ts))

        except Exception as ex:
            app.logger.error(f"{inspect.currentframe().f_code.co_name}: {ex}")

        time_chunking = 3
        return await quart.render_template(
            "moon.html",
            character_id=ar.character_id,
            moon_id=moon_id,
            moon_history=moon_history,
            moon_yield=moon_yield,
            weekday_names=['M', 'T', 'W', 'T', 'F', 'S', 'S'],
            timeofday_names=[f"{(x-time_chunking):02d}-{(x):02d}" for x in range(time_chunking, 24 + time_chunking) if x % time_chunking == 0],
        )

    return quart.redirect(evesso.login_route)

@app.route("/", methods=["GET"])
@otel
async def _root() -> quart.ResponseReturnValue:

    ar: typing.Final[AppRequest] = await AppFunctions.get_app_request(evedb, quart.session, quart.request)
    if ar.character_id > 0 and ar.suspect:
        quart.session.clear()

    elif ar.character_id > 0 and ar.permitted:

        if bool(ar.session.get(AppSSO.ESI_CHARACTER_IS_STATION_MANAGER_ROLE, False)):
            AppStructureTask(ar.session, evedb, eveevents, app.logger)

        active_timer_results: typing.Final[list[AppTables.Structure]] = list()
        completed_extraction_results: typing.Final = list()
        scheduled_extraction_results: typing.Final = list()
        unscheduled_extraction_results: typing.Final = list()
        structure_fuel_results: typing.Final[list[AppTables.Structure]] = list()
        structures_without_fuel_results: typing.Final[list[AppTables.Structure]] = list()
        last_update_results: typing.Final = list()
        structure_counts: typing.Final = list()

        try:
            async with await evedb.sessionmaker() as session:
                active_timer_results.extend(await AppFunctions.get_active_timers(session, ar.ts))
                completed_extraction_results.extend(await AppFunctions.get_completed_extractions(session, ar.ts))
                scheduled_extraction_results.extend(await AppFunctions.get_scheduled_extractions(session, ar.ts))
                unscheduled_extraction_results.extend(await AppFunctions.get_unscheduled_structures(session, ar.ts))
                structure_fuel_results.extend(await AppFunctions.get_structure_fuel_expiries(session, ar.ts))
                structures_without_fuel_results.extend(await AppFunctions.get_structures_without_fuel(session, ar.ts))
                last_update_results.extend(await AppFunctions.get_refresh_times(session, ar.ts))
                structure_count_dict: typing.Final = await AppFunctions.get_structure_counts(session, ar.ts)
                for last_update in last_update_results:
                    last_update: AppTables.PeriodicTaskTimestamp
                    structure_counts.append(structure_count_dict.get(last_update.corporation_id, 0))

        except Exception as ex:
            app.logger.error(f"{inspect.currentframe().f_code.co_name}: {ex}")

        return await quart.render_template(
            "home.html",
            character_id=ar.character_id,
            active_timers=active_timer_results,
            completed_extractions=completed_extraction_results,
            scheduled_extractions=scheduled_extraction_results,
            structure_fuel_expiries=structure_fuel_results,
            structures_without_fuel=structures_without_fuel_results,
            unscheduled_extractions=unscheduled_extraction_results,
            last_update=last_update_results,
            structure_counts=structure_counts,
            character_trusted=ar.trusted,
            character_contributor=ar.contributor,
            magic_character=ar.magic_character
        )

    elif ar.character_id > 0 and not ar.permitted:
        app.logger.warning(f"{ar.character_id} not permitted")
        return await quart.render_template(
            "permission.html",
            character_id=ar.character_id,
        )

    return await quart.render_template("login.html")

if __name__ == "__main__":

    # logging.basicConfig(level=logging.DEBUG)
    otel_initialize()

    AppTemplates.add_templates(app, evedb)

    app_debug: typing.Final = app.config.get("DEBUG", False)
    app_port: typing.Final = app.config.get("PORT", 5050)
    app_host: typing.Final = app.config.get("HOST", "127.0.0.1")

    # app_log_file: typing.Final = os.path.join(app.config.get('BASEDIR', os.path.basename(os.path.abspath(os.path.splitext(__file__)[0]))), "logs", "app.log")
    # app_log_dir: typing.Final = os.path.dirname(app_log_file)
    # if not os.path.isdir(app_log_dir):
    #     os.makedirs(app_log_dir, 0o755)

    # logging.basicConfig(level=logging.INFO, filename=app_log_file)

    if app_debug:
        app.run(host=app_host, port=app_port, debug=app_debug)
    else:
        import hypercorn.asyncio
        import hypercorn.config
        from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware

        app_trusted_hosts: typing.Final = ["127.0.0.1", "::1"]
        app_bind_hosts: typing.Final = [x for x in app_trusted_hosts]

        # XXX: hack for development server.
        development_flag_file = os.path.join(app.config.get("BASEDIR", "."), "development.txt")
        if os.path.exists(development_flag_file):
            with open(development_flag_file) as ifp:
                app_bind_hosts.clear()
                app_bind_hosts.append("0.0.0.0")
                for line in [line.strip() for line in ifp.readlines()]:
                    app_trusted_hosts.append(line)

        config: typing.Final = hypercorn.config.Config()
        config.bind = [f"{host}:{app_port}" for host in app_bind_hosts]
        config.accesslog = "-"

        async def async_main():
            await evedb._initialize()

            app.asgi_app = opentelemetry.instrumentation.asgi.OpenTelemetryMiddleware(
                app.asgi_app
            )

            app.asgi_app = ProxyHeadersMiddleware(
                app.asgi_app, trusted_hosts=app_trusted_hosts
            )

            await hypercorn.asyncio.serve(app, config)

        asyncio.run(async_main())


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import csv

def plotMeanDaily(meanDailyValue, datatitle, ylabel, minYear, maxYear, location):
    """Plots the mean daily sun duration and adds descriptions. Timespan description specified by min and maxYear, Location by location.

    Args:
        meanDailyValue (list like): list of mean daily values
        datatitle (string): title of data type e.g. sun duration
        ylabel (string): label for y axis (should be similar to datatitle)
        minYear (numerical): start year of data acquisition
        maxYear (numerical): last year of data acquisition
        location (string): locaiton of data acquisition
    """
    fig = plt.figure()
    plt.grid(which='both', axis='y')
    plt.bar(range(1,367), meanDailyValue)
    plt.title(f'Zeitraum: {minYear} - {maxYear}, Ort: {location}')
    plt.suptitle(datatitle)
    plt.ylabel(ylabel)
    plt.xlabel('Tag im Jahr')
    fig.show()

def plotMeanHourly(meanHourlyValue, datatitle, ylabel, yearLimits, location):
    """Plots mean value for all hours of a day

    Args:
        meanHourlyValue (list): list of mean hourly values
        datatitle (string): title of data type e.g. sun duration
        ylabel (string): label for y axis (should be similar to datatitle)
        yearLimits (list): list with first and last year of data acquisition
        location (string): locaiton of data acquisition
    """
    fig = plt.figure()
    plt.grid(which='both', axis='y')
    plt.bar(range(0,24), meanHourlyValue)
    plt.title(f'Zeitraum: {yearLimits[0]} - {yearLimits[1]}, Ort: {location}')
    plt.suptitle(datatitle)
    plt.ylabel(ylabel)
    plt.xlabel('Stunde am Tag')
    fig.show()

def plotMeanHourlySeasons(meanHourlySeasonsValues: pd.DataFrame, datatitle: str, ylabel: str):
    """Plots mean hourly seasonal data in four subplots. One for each season

    Args:
        meanHourlySeasonsValues (pd.DataFrame): Seasonal data
        datatitle (str): data title
        ylabel (str): label for y axes of subplots
    """

    fig, axes = plt.subplots(nrows=2, ncols=2)
    seasonsString = ['Winter (Dez - Feb)', 'Frühling (Mär - Mai)', 'Sommer (Jun - Aug)', 'Herbst (Sep - Nov)']
    for season in range(1,5):
        curraxes = axes[(season-1)//2,1-season%2]
        currDf = meanHourlySeasonsValues.loc[meanHourlySeasonsValues['Season'] == season]
        curraxes.bar(range(currDf['Hour'].min(),currDf['Hour'].max() + 1), currDf['Wert'])
        curraxes.set_title(seasonsString[season-1])
        curraxes.set_ylabel(ylabel)
    fig.suptitle(datatitle)
    fig.show()

def calcMeanHourlySeasons(df: pd.DataFrame, valueColumn: str):
    """Calculates the mean hourly value for the four seasons winter spring summer fall (ID: 1,2,3,4)

    Args:
        df (pandas Dataframe)): Dataframe with value and timestamp
        valueColumn (string): Name of value column

    Returns:
        pandas Dataframe: Dataframe with mean daily values by seasons
    """
    df['Month'] = df['TimeStamp'].dt.month
    df['Season'] = df['Month'] % 12 // 3 + 1 #seasons winter spring summer fall (1,2,3,4)
    return df.groupby(['Season', 'Hour'])[valueColumn].mean().to_frame().reset_index()

def calcMeanDaily(df, valueColumn):
    """Generates mean daily value dataframe from raw hourly data over multiple years

    Args:
        df (pandas Dataframe): Dataframe containing raw data. Must contain column 'Zeitstempel' for time data and 'Wert' for value to be averaged
        valueColumn (string): column identifier of column containing data to be averaged

    Returns:
        pandas Dataframe: mean daily value
    """

    df['Year'] = df['TimeStamp'].dt.year
    df['DayOfYear'] = df['TimeStamp'].dt.day_of_year
    df['Hour'] = df['TimeStamp'].dt.hour

    return df.groupby(['DayOfYear'])[valueColumn].mean()

def calcMeanHourly(df, valueColumn):
    """Calculates hourly mean of dataframe over all datapoints

    Args:
        df (pandas dataframe): dataframe containing time and value data
        valueColumn (string): name of value data column

    Returns:
        pandas Dataframe: mean hourly value
    """

    # df['Hour'] = df['Timestamp'].dt.hour # this is super bad... the functions change the dataframe directly and only return a none type wtf... help

    return df.groupby(['Hour'])[valueColumn].mean()

def reassignTimestamp(df, timeStampHeader):
    """Redefines the specified column into a datatime format timestamp

    Args:
        df (pandas dataframe): dataframe containing a timestamp like entry
        timeStampHeader (string): name of column containing timestamplike

    Returns:
        pandas dataframe: Dataframe with redefined timestamp
    """
    df['TimeStamp'] = pd.to_datetime(df[timeStampHeader])

    df.drop(timeStampHeader, axis=1, inplace=True)

# Sunduration
sunDurationDf = pd.read_csv(r"C:\Users\lelem\Projekte\WetterDatenanalyse\WeatherDataAnalysisESS\Data\cdc_download_2023-05-17_14-28\data\data_OBS_DEU_PT1H_SD.csv", index_col=False)
reassignTimestamp(df=sunDurationDf, timeStampHeader='Zeitstempel')

meanSunDurationYear = calcMeanDaily(df=sunDurationDf, valueColumn='Wert')
meanSunDurationDay = calcMeanHourly(df=sunDurationDf, valueColumn='Wert')
meanSunDurationDaySeasons = calcMeanHourlySeasons(df=sunDurationDf, valueColumn='Wert')

plotMeanDaily(meanDailyValue=meanSunDurationYear, datatitle='Mittlere tägliche Sonnenscheindauer', ylabel='Sonnenscheindauer [min]', minYear=sunDurationDf['Year'].min(), maxYear=sunDurationDf['Year'].max(), location='Essen')
plotMeanHourly(meanHourlyValue=meanSunDurationDay, datatitle='Mittlere stündliche Sonnenscheindauer', ylabel='Sonnenscheindauer [min]', yearLimits=[sunDurationDf['Year'].min(), sunDurationDf['Year'].max()], location='Essen')
plotMeanHourlySeasons(meanHourlySeasonsValues= meanSunDurationDaySeasons, datatitle='Mittlere stündliche Sonnenscheindauer nach Jahreszeiten', ylabel='Sonnenscheindauer [min]')

# Overcast
overcastDf = pd.read_csv(r"C:\Users\lelem\Projekte\WetterDatenanalyse\WeatherDataAnalysisESS\Data\cdc_download_2023-05-17_14-28\data\data_OBS_DEU_PT1H_RR.csv", index_col=False)
reassignTimestamp(df=overcastDf, timeStampHeader='Zeitstempel')

meanOvercastYear = calcMeanDaily(df=overcastDf, valueColumn='Wert')
meanOvercastDay = calcMeanHourly(df=overcastDf, valueColumn='Wert')
meanOvercastDaySeasons = calcMeanHourlySeasons(df=overcastDf, valueColumn='Wert')

plotMeanDaily(meanDailyValue=meanOvercastYear, datatitle='Mittlerer täglicher Bedeckungsgrad', ylabel='Bedeckungsgrad [Okta]', minYear=overcastDf['Year'].min(), maxYear=overcastDf['Year'].max(), location='Essen')
plotMeanHourly(meanHourlyValue=meanOvercastDay, datatitle='Mittlerer stündlicher Bedeckungsgrad', ylabel='Bedeckungsgrad [Okta]', yearLimits=[overcastDf['Year'].min(), overcastDf['Year'].max()], location='Essen')
plotMeanHourlySeasons(meanHourlySeasonsValues=meanOvercastDaySeasons, datatitle='Mittlerer stündlicher Bedeckungsgrad nach Jahreszeiten', ylabel='Bedeckungsgrad [Okta]')

# Precipitation
precipitationDf = pd.read_csv(r"C:\Users\lelem\Projekte\WetterDatenanalyse\WeatherDataAnalysisESS\Data\cdc_download_2023-05-17_14-28\data\data_OBS_DEU_PT1H_N.csv", index_col=False)
reassignTimestamp(df=precipitationDf, timeStampHeader='Zeitstempel')

meanPrecipitationYear = calcMeanDaily(df=precipitationDf, valueColumn='Wert')
meanPrecipitationDay = calcMeanHourly(df=precipitationDf, valueColumn='Wert')
meanPrecipitationDaySeasons = calcMeanHourlySeasons(df=precipitationDf, valueColumn='Wert')

plotMeanDaily(meanDailyValue=meanPrecipitationYear, datatitle='Mittlerer täglicher Niederschlag', ylabel='Niederschlag [mm]', minYear=precipitationDf['Year'].min(), maxYear=precipitationDf['Year'].max(), location='Essen')
plotMeanHourly(meanHourlyValue=meanPrecipitationDay, datatitle='Mittlerer stündlicher Niederschlag', ylabel='Niederschlag [mm]', yearLimits=[precipitationDf['Year'].min(), precipitationDf['Year'].max()], location='Essen')
plotMeanHourlySeasons(meanHourlySeasonsValues=meanPrecipitationDaySeasons, datatitle='Mittlerer stündlicher Niederschlag nach Jahreszeiten', ylabel='Niederschlag [mm]')
plt.show()


from django import forms
from models import Topic, Message

"""
class TopicForm(forms.Form):
    title       = forms.CharField()
    category    = forms.HiddenInput()
    forum       = forms.HiddenInput()
    title       = forms.CharField()
    # slug        = forms.CharField(null=True, blank=True)
    sticky      = forms.CheckboxInput()
    user        = forms.HiddenInput()
    text        = forms.CharField(widget=forms.Textarea)
"""

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        exclude = ('user', 'category', 'forum', 'last_user', 'message', 'last_message', 'sticky', 'reply_count',
        'view_count', 'locked', 'active', 'created', 'responded_to', 'modified', 'slug')

class MessageForm(forms.ModelForm):
    class Meta:
        model = Message
        exclude = ('category', 'forum', 'topic', 'user', 'created', 'modified', 'active')


#!/usr/bin/env python
# coding: utf-8

# In[ ]:

import csv
import numpy as np

def F(l=0.235, w=0.15, r=0.0475):
    '''Compute F-matrix (3 controls * 4 wheels) of four mecanum wheel robot'''

    F = r/4 * np.array([[-1/(l+w), 1/(l+w), 1/(l+w), -1/(l+w)],
                        [       1,       1,       1,        1],
                        [      -1,       1,      -1,        1]])
    return F

def Tsc(x, y, theta):
    '''Calculate cube config SE(3) in {s}-frame given (x, y, theta)'''

    Tsc = np.array([[np.cos(theta), -np.sin(theta), 0,     x],
                    [np.sin(theta),  np.cos(theta), 0,     y],
                    [            0,              0, 1, 0.025],
                    [            0,              0, 0,     1]])
    return Tsc

def writeConfiguration(configs, filename):
    '''Write all configs to csv file'''

    with open(filename, 'w', newline='') as csvfile:
        csvwriter = csv.writer(csvfile)
        csvwriter.writerows(configs)


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn.metrics

from dataclasses import dataclass
from typing import Optional, Iterable

class ModelEvaluator:
    """Helper class to plot evaluation statistics and compare models.
    """

    def __init__(self):
        self._class_results_container = {}
        self._macro_avgs_container = {}
        self._weighted_avgs_container = {}
        self._accuracies_container = {}

        self.classes = None

    def store(self, y_true: np.array, y_pred: np.array, *, name: str) -> None:
        """Stores model performance statistics.

        Args:
            y_true (np.array): The true labels
            y_pred (np.array): The predictions
            name (str): A name for the current model
        """

        classification_report = sklearn.metrics.classification_report(y_true, y_pred, output_dict=True)

        # Pop avg results from classification report
        # so that only results per class remain.
        macro_avg = classification_report.pop('macro avg')
        accuracy = classification_report.pop('accuracy')
        weighted_avg = classification_report.pop('weighted avg')

        if not self.classes:
            self.classes = [key for key, _ in classification_report.items()]

        results_per_class = pd.DataFrame.from_dict(classification_report, orient='index')
        self._class_results_container[name] = results_per_class

        self._macro_avgs_container[name] = macro_avg
        self._weighted_avgs_container[name] = weighted_avg
        self._accuracies_container[name] = accuracy

    @property
    def model_names(self):
        return list(self._class_results_container.keys())

    def get_summary(
        self, y_true: np.array, y_pred: np.array, *,
        name: str,
        classification_report: bool = True,
        store: bool = True
    ) -> None:

        sklearn.metrics.ConfusionMatrixDisplay.from_predictions(y_true, y_pred)

        if name is not None:
            plt.title(name)

        plt.show()

        if classification_report:
            print(sklearn.metrics.classification_report(y_true, y_pred))

        if store:
            self.store(y_true, y_pred, name=name)

    @property
    def class_results(self) -> pd.DataFrame:
        """Results per class for each of the models

        Returns:
            pd.DataFrame: A multiindexed dataframe (name, class)
        """
        return pd.concat(self._class_results_container)

    @property
    def macro_results(self) -> pd.DataFrame:
        """Results per class for each of the models

        Returns:
            pd.DataFrame: A multiindexed dataframe (name, class)
        """
        df = pd.DataFrame(self._macro_avgs_container).T
        df.index = pd.MultiIndex.from_product([df.index, ['macro avgs']])

        return df

    @staticmethod
    def _plot_abstract(
        df, *,
        metric: str,
        savepath: str
    ) -> None:

        df.plot(kind='bar')
        plt.title(metric)
        plt.xticks(rotation=45)
        #plt.legend(title='class')
        if savepath:
            plt.savefig(savepath, bbox_inches='tight')
        plt.show()

    def _get_plot_df(
        self, *,
        type: str,
        metric: str
    ) -> pd.DataFrame:

        transformations = {
            'classwise': self.class_results[metric].unstack(),
            'macro': self.macro_results[metric].unstack().loc[self.model_names,:]
        }

        if type not in transformations.keys():
            allowed_params: str = str(list(transformations.keys()))
            raise ValueError(f'Parameter type must be in {allowed_params}')

        return transformations[type]

    def plot(
        self, *,
        type='classwise',
        metric='f1-score',
        savepath=None
    ) -> None:

        self._plot_abstract(
            df=self._get_plot_df(type=type, metric=metric),
            metric=metric,
            savepath=savepath
        )