from django.core.exceptions import ObjectDoesNotExist
from rest_framework.permissions import IsAuthenticated
from django.contrib.auth.models import Group
from knowledge.models import KbKnowledge
from .models import SysUser
from rest_framework.views import APIView
from rest_framework import serializers, status
from rest_framework.response import Response
from .services import get_user_activity, add_subscriber, is_developer
from rest_framework.authtoken.models import Token
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys

def compressImage(uploadedImage):
    imageTemproary = Image.open(uploadedImage).convert('RGB')
    outputIoStream = BytesIO()
    imageTemproaryResized = imageTemproary.resize((200, 200))
    imageTemproaryResized.save(outputIoStream, format='JPEG', quality=60)
    outputIoStream.seek(0)
    uploadedImage = InMemoryUploadedFile(outputIoStream, 'ImageField', "%s.jpg" % uploadedImage.name.split('.')[0],
                                         'image/jpeg', sys.getsizeof(outputIoStream), None)
    return uploadedImage

class GetUserDetailViewSet(APIView):
    class GetUserDetailFromTokenSerializer(serializers.ModelSerializer):
        class Meta:
            model = SysUser
            fields = ('id_name', 'first_name', 'last_name', 'profile_pic', 'profile', 'header_image', 'email',
                      'about', 'header_image', 'facebook_profile_link', 'instagram_profile_link',
                      'twitter_profile_link', 'external_website_link', 'linkedin_profile', 'public', 'groups')

    def get(self, request, format=None):
        serializer = self.GetUserDetailFromTokenSerializer(request.user, many=False)
        # request.user.groups
        moderator = author = False
        if request.user.groups.filter(name="Moderators").exists():
            moderator = True
        if request.user.groups.filter(name="Authors").exists():
            author = True
        response = {'user': serializer.data, 'author': author, 'moderator': moderator}
        return Response(response, status=status.HTTP_200_OK)

    def post(self, request, format=None):
        if request.data["delete"]:
            request.user.delete()
            response = {"message": "User Deleted Successfully"}
            return Response(response, status=status.HTTP_200_OK)

class EditUserDetailViewSet(APIView):

    def post(self, request, format=None):
        # breakpoint()
        # user : SysUser
        user = request.user
        user.first_name = request.data['first_name']
        user.last_name = request.data['last_name']
        user.external_website_link = request.data['external_link']
        user.instagram_profile_link = request.data['instagram_link']
        user.linkedin_profile = request.data['linkedin_link']
        user.facebook_profile_link = request.data['facebook_link']
        user.public = request.data['public']
        user.twitter_profile_link = request.data['twitter_link']
        # user.profile = request.data['profile']
        user.about = request.data['about']
        user.save()
        return Response({"message": "changed"}, status=status.HTTP_200_OK)

class EditImageOnlyViewSet(APIView):
    class EditImageOnlySerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = SysUser
            fields = ['profile']

    def post(self, request, format=None):
        # breakpoint()
        user = Token.objects.get(key=request.data['token']).user
        user.profile = compressImage(request.data['profile'])
        user.save()
        return Response({"message": "changed the image mofo"}, status=status.HTTP_200_OK)

class GetUserActivity(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, requested_type, start, end, format=None):
        result = get_user_activity(request, requested_type, start, end)
        # breakpoint()
        return Response(result, status=status.HTTP_200_OK)

class AddSubscriberViewSet(APIView):

    def post(self, request, format=None):
        result = add_subscriber(request)
        return Response(result, status=status.HTTP_200_OK)

class IsDeveloperViewSet(APIView):
    # permission_classes = (IsAuthenticated,)

    def post(self, request, format=None):
        # breakpoint()
        result = is_developer(request.data['passcode'], request.data['username'])
        return Response(result, status=status.HTTP_200_OK)

class IsPartOfTheGroup(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, group_name, format=None):
        # request.user
        # breakpoint()
        if request.user.groups.filter(name=group_name).exists():
            response = True
        else:
            response = False
        return Response(response, status=status.HTTP_200_OK)

class GetUserAuthoredArticles(APIView):
    permission_classes = (IsAuthenticated,)

    class GetUserAuthoreArticlesSerializer(serializers.ModelSerializer):
        class Meta:
            model = KbKnowledge
            fields = ['id', 'title', 'sys_created_on', 'sys_updated_on', 'workflow', 'article_url', 'get_category',
                      'get_knowledge_base']

    def get(self, request, sort_by, state, format=None):
        # breakpoint()

        if state == 'all':
            articles = KbKnowledge.objects.filter(author=request.user, active=True).order_by(sort_by)
            articles_count = KbKnowledge.objects.filter(author=request.user, active=True).count()
            articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
            response = {
                'articles': articles_data.data,
                'total_count': articles_count,
            }
        if state != 'all':
            articles = KbKnowledge.objects.filter(author=request.user, active=True, workflow=state).order_by(sort_by)
            articles_count = KbKnowledge.objects.filter(author=request.user, active=True, workflow=state).count()
            articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
            response = {
                'articles': articles_data.data,
                'total_count': articles_count,
            }
        return Response(response, status=status.HTTP_200_OK)

class MakeUserAuthor(APIView):
    permission_classes = (IsAuthenticated, )

    def post(self, request, format=None):
        my_group = Group.objects.get(name='Authors')
        my_group.user_set.add(request.user)
        return Response('success', status=status.HTTP_201_CREATED)

class PublicUserData(APIView):
    class GetUserDetailFromTokenSerializer(serializers.ModelSerializer):
        class Meta:
            model = SysUser
            fields = ('id_name', 'first_name', 'last_name', 'profile_pic', 'profile', 'header_image',
                      'about', 'header_image', 'facebook_profile_link', 'instagram_profile_link',
                      'twitter_profile_link', 'external_website_link', 'linkedin_profile', 'public', 'groups')

    def get(self, request, id, format=None):
        try:
            user = SysUser.objects.get(id_name=id, is_active=True, public=True)
        except ObjectDoesNotExist:
            return Response('', status=status.HTTP_404_NOT_FOUND)
        serializer = self.GetUserDetailFromTokenSerializer(user, many=False)
        # request.user.groups
        moderator = author = False
        if user:
            if user.groups.filter(name="Moderators").exists():
                moderator = True
            if user.groups.filter(name="Authors").exists():
                author = True
            response = {'user': serializer.data, 'author': author, 'moderator': moderator}
            return Response(response, status=status.HTTP_200_OK)
        else:
            return Response('User has deactivated his account!', status=status.HTTP_404_NOT_FOUND)

class GetPublicAuthorArticles(APIView):
    class GetUserAuthoreArticlesSerializer(serializers.ModelSerializer):
        class Meta:
            model = KbKnowledge
            fields = ['id', 'title', 'get_knowledge_base', 'get_category', 'article_url']

    def get(self, request, id_name, sort_by, format=None):
        # breakpoint()
        try:
            user = SysUser.objects.get(id_name=id_name, public=True, is_active=True)
        except ObjectDoesNotExist:
            return Response('User has deactivated his account', status=status.HTTP_404_NOT_FOUND)
        articles = KbKnowledge.objects.filter(author=user, active=True, workflow="published").order_by(sort_by)
        articles_count = KbKnowledge.objects.filter(author=user, active=True).count()
        articles_data = self.GetUserAuthoreArticlesSerializer(articles, many=True)
        response = {
            'articles': articles_data.data,
            'total_count': articles_count,
        }
        return Response(response, status=status.HTTP_200_OK)


import threading
import time

from emross.utility.base import EmrossBaseObject

class BuildManager(EmrossBaseObject):

    def __init__(self, bot, *args, **kwargs):
        super(BuildManager, self).__init__(bot, *args, **kwargs)
        self.tasks = {}
        self.lock = threading.Lock()
        self.rlock = threading.RLock()
        self.running_build_stages = set()

    def task(self, task_class):
        with self.rlock:
            try:
                handler = self.tasks[task_class]
            except KeyError:
                handler = self.tasks[task_class] = task_class(self.bot)
            except TypeError as e:
                logger.exception(e)
                raise e
        return handler

    def process(self, tasks, stagename="TASK"):
        """
        Process the build path and pass things to their respective handlers
        for further processing.
        """

        results = []
        cycle_start = time.time()

        for i, stage in enumerate(tasks, start=1):

            results[:] = []
            for parts in stage:
                try:
                    handler = self.task(parts[0])
                    if self.bot.api.player and \
                        handler.__class__ in self.bot.api.player.disable_modules:
                        continue
                    args = next(iter(parts[1:2]), ())
                    kwargs = next(iter(parts[2:3]), {})
                    result = handler.run(cycle_start, i, *args, **kwargs)
                    results.append(result)
                except Exception as e:
                    self.log.exception(e)

            if False in results:
                #self.log.debug('Not all parts of {0} stage {1} are complete'.format(stagename, i))
                break

        # Free this stage for other threads
        self.running_build_stages.remove(stagename)


# -*- coding: utf-8 -*-
'''graph writers'''

from itertools import count
from functools import partial

import nanotime
from markupsafe import escape

from graphalchemy.core import octopus

__all__ = ('Links', 'Nodes')

# settings
conf = octopus.S
appspace = conf.appspace
backends = conf.backends
db = conf.key.db
# decorators
direct = getattr(octopus, 'direct')
factory = getattr(octopus, 'factory')
service = getattr(octopus, 'service')

class Writer(octopus.server):

    '''graph writer base'''

    # graph source
    _db = direct(conf.key.backend, conf.userspace)

    @property
    def transaction(self):
        '''get transaction'''
        return self.w.transaction

    def delete_element(self, this, indices=None):
        '''
        delete graph element

        @param this: graph model instance
        @param indicies: graph indices (default: None)
        '''
        self.w.delete_element(this, indices)

    def index_many(self, index, this, indexed):
        '''
        index properties on a graph element

        @param index: graph index label
        @param this: graph model instance
        @param indexed: properties to index
        '''
        self.w.index_many(self.r.index(index), this, indexed)

    def index_one(self, index, key, value, this):
        '''
        index one property of a graph element

        @param index: graph index label
        @param key: graph element property key
        @param value: graph element property value
        @param this: graph model instance
        '''
        self.w.index_one(self.r.index(index), key, value, this)

    def modify(self, this, data):
        '''
        update a graph element's properties

        @param this: graph model instance
        @param data: data
        '''
        self.w.update(this, data)

    @staticmethod
    def now():
        '''current time in microseconds'''
        return nanotime.now().microseconds()

class Links(Writer):

    '''graph link writer'''

    # link model finder
    _finder = direct(conf.model.finder.link, appspace)
    # direct link reader
    r = factory(conf.read.link, backends, db)
    # direct link writer
    w = factory(conf.write.link, backends, db)

    def make(self, link, start, end, kw=None):
        '''
        build a new graph link

        @param data: data
        @param model: graph model (default: None)
        '''
        if kw is None:
            kw = {}
        kw.update(_created=self.now(), _uuid=self.B.uuid())
        return self.w.build(link, start, end, **kw)

class Nodes(Writer):

    '''graph node writer'''

    # node model finder
    _finder = direct(conf.model.finder.node, appspace)
    # direct node reader
    r = factory(conf.read.node, backends, db)
    # direct node writer
    w = factory(conf.write.node, backends, db)

    def _postprocess(self, this, model=None):
        '''
        postprocess graph node

        @param this: graph model instance
        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        this = model(this)
        C = model.C
        index = C.index
        # index created date
        index_one = self.index_one
        if index:
            index_one(index, 'created', self.now(), this)
        # index any listed properties
        indexed = C.indexed
        if indexed:
            self.index_many(index, this, indexed)
        # add a slug if present
        slug_from = C.slug_from
        if slug_from:
            index_one(index, slug_from, getattr(this, slug_from), this)
        # index properties that support full text search
        fts_indexed = C.fts_indexed
        if fts_indexed:
            self.index_many(C.fts_index, this, fts_indexed)
        return this

    def _preprocess(self, this, model=None):
        '''
        pre-process graph element

        @param this: graph model instance
        @param model: graph model (default: None)
        '''
        esc = self.escape
        for k in model.C.escaped:
            this[k] = esc(this[k])
        this['_modified'] = self.now()
        # slug field
        self.autoslug(this, model)

    @service('model')
    def autoslug(self, properties, model=None):
        '''
        auto slug an element

        @param properties: graph element properties
        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        C = model.C
        slug_from = C.slug_from
        if not slug_from:
            return
        slug = tmpslug = self.B.slugify(properties[slug_from])
        slug_field = C.slug_field
        # loop until unique slug is located
        filter_by = self.r.filter_by
        index = C.index
        for cnt in count(1):
            prev = filter_by(index, slug_field, tmpslug)
            if not prev:
                break
            tmpslug = '{slug}-{count}'.format(slug=slug, count=cnt)
        # add slug
        properties[slug_field] = tmpslug

    @service('model')
    def anchor(self, model, **kw):
        '''
        create an anchor node

        @param model: graph model (default: None)
        '''
        finder = self._finder
        model = finder(model)
        C = model.C
        name = C.name
        S = self.S.anchor
        label = S.label
        index_one = self.index_one
        def callback(node): #@IgnorePep8
            node = finder(element=node)(node)
            # put in root index
            root_index = C.root_index
            if root_index:
                index_one(root_index, label, name, node)
            # put in model index
            index = C.index
            if index:
                index_one(index, label, label, node)
            return node
        data = dict(
            _created=self.now(),
            _uuid=self.B.uuid(),
            text=S.description.format(name=name),
        )
        data.update(kw)
        return self.w.build(
            data,
            C.reference_link,
            model.nodes.reference(),
            callback,
        )

    @service('model')
    def build(self, data, model=None):
        '''
        build a new graph node

        @param data: data
        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        C = model.C
        data.update(
            _created=self.now(), _model=C.name, _uuid=self.B.uuid())
        # number of versions to maintain
        if C.versioned:
            data['_versions'] = 0
        self._preprocess(data, model)
        return self.w.build(
            data,
            C.reference_link,
            model.nodes.reference(),
            partial(self._postprocess, model=model),
        )

    @service('model')
    def clone(self, this, model=None):
        '''
        clone a graph node

        @param this: graph model instance
        @param model: graph model (default: None)
        '''
        created = self.now()
        if model is None:
            model = self._finder(model)
        C = model.C
        index = C.index
        S = self.S.fork
        if index:
            # build callback
            index_one = self.index_one
            def callback(fork): #@IgnorePep8
                # add hash
                fork['_hash'] = this.traits.hash_current()
                # refer back to original
                fork['_forked'] = this.id
                fork = model(fork)
                # index fork
                index_one(index, S.label, this.id, fork)
                # index forked time
                index_one(index, S.created, created, fork)
                return fork
        else:
            callback = None
        return self.w.clone(this, C.fork_link, callback, _created=created)

    @service('model')
    def create(self, model=None, **kw):
        '''
        create a new graph element

        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        instance = model(**kw)
        instance.save()
        return instance

    @staticmethod
    def escape(data):
        '''
        escapes &, <, >, ', and " with HTML entities

        @param data: text data
        '''
        return escape(data)

    def make(self, data):
        '''
        build a new graph node

        @param data: data
        @param model: graph model (default: None)
        '''
        data.update(_created=self.now(), _uuid=self.B.uuid())
        return self.w.build(data)

    @service('model')
    def refer(self, model=None, **kw):
        '''
        create a reference node

        @param model: graph model (default: None)
        '''
        finder = self._finder
        if model is None:
            model = self._finder(model)
        C = model.C
        name = C.name
        S = self.S.reference
        label = S.label
        w = self.w
        index_one = w.index_one
        create_index = w.create_index
        r = self.r
        def callback(node): #@IgnorePep8
            node = finder(element=node)(node)
            # put in root index
            root_index = r.index(C.root_index)
            index_one(root_index, label, name, node)
            # build any main index
            index = C.index
            if index:
                index_one(create_index(index), label, label, node)
            # build any full text index
            fts_index = C.get('fts_index', False)
            if fts_index:
                create_index(fts_index, fts=True)
            return node
        root = r.root
        h = self
        data = dict(
            _created=h.now(),
            _uuid=self.B.uuid(),
            text=S.description.format(name=name),
        )
        data.update(kw)
        return w.build(
            data, C.reference_link, finder(element=root)(root), callback,
        )

    @service('model')
    def revert(self, this, snapshot, model=None):
        '''
        change a graph node to an older snapshot

        @param this: graph model instance
        @param snapshot: snapshot to change to
        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        self.update(
            model,
            this,
            **self.r.filter_by(self.S.snapshot.label, snapshot).properties
        )

    @service('model')
    def snapshot(self, this, diff, model=None):
        '''
        capture changes between different versions of a graph node

        @param this: graph model instance
        @param diff: differences between two versions of a graph node
        @param model: graph model (default: None)
        '''
        created = self.now()
        # populate diff
        diff.update(
            _created=created,
            _versioned=this.id,
        )
        S = self.S.version
        if model is None:
            model = self._finder(model)
        C = model.C
        index = C.index
        if index:
            # build callback if index
            index_one = self.index_one
            def callback(snapshot): #@IgnorePep8
                snapshot = model(snapshot)
                # index snapshot with parent snapshot
                index_one(index, S.reference, this.id, snapshot)
                # index snapshot as snapshot
                index_one(index, S.label, snapshot.id, snapshot)
                # index versioned time
                index_one(index, S.created, created, snapshot)
                return snapshot
        else:
            callback = None
        # create snapshot and link back to previous snapshot
        return self.w.build(
            diff, C.version_link, this, callback, _created=created,
        )

    @service('model')
    def update(self, this, data, model=None):
        '''
        update a graph element's properties

        @param this: graph model instance
        @param data: data
        @param model: graph model (default: None)
        '''
        if model is None:
            model = self._finder(model)
        self._preprocess(data, model)
        self.w.update(this, data, partial(self._postprocess, model=model))


# _*_ coding: utf-8 _*_

import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.nn import functional as F

class CNNClassifier(nn.Module):
    def __init__(self, batch_size, output_size, in_channels, out_channels, kernel_heights, stride, padding, keep_probab,
                  embedding_length):
        super(CNNClassifier, self).__init__()
        self.batch_size = batch_size
        self.output_size = output_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_heights = kernel_heights
        self.stride = stride
        self.padding = padding
        self.embedding_length = embedding_length

        self.conv1 = nn.Conv2d(in_channels, out_channels, (kernel_heights[0], embedding_length),padding=(kernel_heights[0] - 1, 0))
        self.conv2 = nn.Conv2d(in_channels, out_channels, (kernel_heights[1], embedding_length),padding=(kernel_heights[1] - 1, 0))
        self.conv3 = nn.Conv2d(in_channels, out_channels, (kernel_heights[2], embedding_length),padding=(kernel_heights[2] - 1, 0))

        self.dropout = nn.Dropout(keep_probab)
        self.label = nn.Linear(len(kernel_heights) * out_channels, output_size)
        self.sofmax = nn.Softmax(dim=1)

    def conv_block(self, input, conv_layer):

        conv_out = conv_layer(input)
        activation = torch.squeeze(F.relu(conv_out),-1)
        max_out = F.max_pool1d(activation, activation.size(2))

        return max_out

    def forward(self, input_sentences, batch_size=None):
        input = torch.unsqueeze(input_sentences, 1)
        max_out1 = self.conv_block(input, self.conv1)
        max_out2 = self.conv_block(input, self.conv2)
        max_out3 = self.conv_block(input, self.conv3)
        all_out = torch.cat((max_out1, max_out2, max_out3), 2)
        all_out = all_out.view(all_out.size(0), -1)
        fc_in = self.dropout(all_out)
        logits = self.label(fc_in)
        logits = self.sofmax(logits)

        return logits


import discord, random, datetime
from discord.ext import commands

class Events(commands.Cog):

    def __init__(self, bot):
        self.bot = bot

    @commands.Cog.listener()
    async def on_ready(self):
        print("+ Events Cog loaded")

    @commands.Cog.listener()
    async def on_member_join(self, member):
        role = discord.utils.get(member.guild.roles, name='Member')
        await member.add_roles(role)

    @commands.Cog.listener()
    async def on_command_error(self, ctx, error):
        # Ignored errors
        ignored = (commands.CommandNotFound, commands.MissingRequiredArgument)#, commands.UserInputError commands.BadArgument
        if isinstance(error, ignored):
            return

        # Error handling
        if isinstance(error, commands.CommandOnCooldown):
            m, s = divmod(error.retry_after, 60)
            h, m = divmod(m, 60)
            if int(h) == 0 and int(m) == 0:
                await ctx.send(f':stopwatch: You must wait **{int(s)} seconds** to use this command again.')
            elif int(h) == 0 and int(m) != 0:
                await ctx.send(f':stopwatch: You must wait **{int(m)} minutes and {int(s)} seconds** to use this command again.')
            else:
                await ctx.send(f':stopwatch: You must wait **{int(h)} hours, {int(m)} minutes and {int(s)} seconds** to use this command again.')
            return

        elif isinstance(error, commands.CheckFailure):
            return await ctx.send("Insufficient permissions.")

        elif isinstance(error, commands.CommandInvokeError):
            if str(error.original) == "403 Forbidden (error code: 50013): Missing Permissions":
                print("===== Attempting to handle in ctx =====")
                try:
                    embed = discord.Embed(title=":x: Missing Permissions", color=discord.Color.red())
                    await ctx.send(embed=embed)
                    return print("Success")
                except Exception:
                    print("Failed")

        print("\n----------\n")
        raise error
        print("\n----------\n")

def setup(bot):
    bot.add_cog(Events(bot))


from knowledge_model import Base, Knowledge

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///knowledge.db')
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()

def add_article(topic, title, rating):
	article_object = Knowledge(
		topic = topic,
		title = title,
		rating = rating)
	session.add(article_object)
	session.commit()

add_article("theatre", "acting", 5)
# add_article("love", "peace", 8)

def query_all_articles():
	articles = session.query(Knowledge).all()
	return articles
# print(query_all_articles())
print(query_all_articles())

def query_article_by_topic(topic):
	article = session.query(Knowledge).filter_by(topic=topic).all()
	return(article)

# print(query_article_by_topic("theatre"))

# def query_article_by_rating(rating):
# 	b = session.query(Knowledge).filter_by(rating=rating).all()
# 	trsh = input("Give an input between 1 and 10")
def delete_article_by_topic(topic):
	session.query(Knowledge).filter_by(topic=topic).delete()
	session.commit()

def delete_all_articles():
	session.query(Knowledge).delete()
	session.commit()

def edit_article_rating():
	title = input("What is the title of the article you want to change?")
	new_rating = input("What is the new rating of the article?")
	articles = session.query(Knowledge).filter_by(title=title).all()
	for article in articles:
		article.rating = new_rating
	session.commit()

# delete_article_by_topic("theatre")
# print(query_all_articles())
# delete_all_articles()
# print(query_all_articles())
def delete_articles_by_rating(threshold):
	session.query(Knowledge).filter(Knowledge.rating<threshold).delete()

# edit_article_rating()
delete_articles_by_rating(7)
print(query_all_articles())


import gym
import pickle
import os
import numpy as np
import traceback
import math
import tensorflow as tf
import multiprocessing as mp
from learner import Learner
from actor import Actor
from memory import ReplayMemory, PERGreedyMemory, SumTree, PERProportionalMemory, PERRankBaseMemory
from simlator import *
#---------------------------------------------------
# manager
#---------------------------------------------------
class R2D2Manager():
    def __init__(self, num_actors, args):
        self.args          = args["agent"]
        self.simlator_args = args["simlator"]
        self.num_actors    = num_actors
        self._create_process()

    def _create_process(self):
        # create each Queue
        experience_q = mp.Queue()
        model_sync_q = [[mp.Queue(), mp.Queue(), mp.Queue()] for _ in range(self.num_actors)]
        self.learner_end_q = [mp.Queue(), mp.Queue()]
        self.actors_end_q = [mp.Queue() for _ in range(self.num_actors)]

        # create actor ps
        self.actors_ps = []
        epsilon = self.args["epsilon"]
        epsilon_alpha = self.args["epsilon_alpha"]
        for i in range(self.num_actors):
            # get epsilon for each actor
            if self.num_actors <= 1:
                epsilon_i = epsilon ** (1 + epsilon_alpha)
            else:
                epsilon_i = epsilon ** (1 + i/(self.num_actors-1)*epsilon_alpha)
            # get simlator fot each actor
            self.simlator   = SeparationControl(self.args, self.simlator_args)
            self.nb_actions = self.simlator.nb_actions

            # get build_compile_model for actor
            model_args = self.get_model_args(1)
            args = (
                self.simlator,
                i,
                epsilon_i,
                model_args,
                self.args,
                experience_q,
                model_sync_q[i],
                self.actors_end_q[i]
                )
            self.actors_ps.append(mp.Process(target=self.actor_run_cpu, args=args))
            print("Actor{} Epsilon:{}".format(i,epsilon_i))

        # get build_compile_model for learner
        model_args = self.get_model_args(self.args["batch_size"])
        args = (
            model_args,
            self.args,
            experience_q,
            model_sync_q,
            self.learner_end_q
            )
        # create learner ps
        self.learner_ps = mp.Process(target=self.learner_run_gpu, args=args)
        self.model_tester_args = self.get_model_args(1)

    def __del__(self):
        self.learner_ps.terminate()
        for p in self.actors_ps:
            p.terminate()

    def train(self):
        # move process
        try:
            self.learner_ps.start()
            for p in self.actors_ps:
                p.start()

            # wait for finish
            while True:
                time.sleep(1)  # polling time

                # 終了判定
                f = True
                for q in self.actors_end_q:
                    if q.empty():
                        f = False
                        break
                if f:
                    break
        except KeyboardInterrupt:
            print("interrupt")
        except Exception:
            print(traceback.format_exc())

        # throw end to learner
        self.learner_end_q[0].put(1)
        time.sleep(1)
        # kill
        self.learner_ps.terminate()
        for p in self.actors_ps:
            p.terminate()

        return

    def test(self):
        # test Actor
        test_actor = Actor(
            self.simlator,
            -1,
            0,
            self.model_tester_args,
            self.args,
            None,
            None,
            training = False,
        )
        test_actor.model.load_weights(self.args["log_dir"]+self.args["load_weights_path"])
        test_actor.test()

    #---------------------------------------------------
    # learner
    #---------------------------------------------------
    def learner_run_gpu(
        self,
        model_args,
        args,
        experience_q,
        model_sync_q,
        learner_end_q,
        ):
        with tf.device("/device:GPU:0"):
            self.learner_run(
            model_args,
            args,
            experience_q,
            model_sync_q,
            learner_end_q
            )

    def learner_run(
        self,
        model_args,
        args,
        experience_q,
        model_sync_q,
        learner_end_q
        ):
        learner = Learner(
            model_args=model_args,
            args=args,
            experience_q=experience_q,
            model_sync_q=model_sync_q,
            learner_end_q=learner_end_q,
        )
        try:
            # model load
            if os.path.isfile(self.args["log_dir"]+self.args["load_weights_path"]):
                learner.model.load_weights(self.args["log_dir"]+self.args["load_weights_path"])
                learner.target_model.load_weights(self.args["log_dir"]+self.args["load_weights_path"])

            # learning
            print("Learner Starts")
            learner.train()

        except KeyboardInterrupt:
            pass
        except Exception:
            print(traceback.format_exc())
        finally:
            print("Learning End. Train Count:{}".format(learner.train_num))

            if self.args["test"]:
                # model save
                if args["save_weights_path"] != "":
                    print("save:" + args["save_weights_path"])
                    learner.model.save_weights(args["log_dir"]+args["save_weights_path"], args["save_overwrite"])
                '''# throw end to learner
                print("Last Learner weights sending...")
                learner_end_q[1].put(learner.model.get_weights())'''

    def actor_run_cpu(
        self,
        simlator,
        actor_index,
        epsilon,
        model_args,
        args,
        experience_q,
        model_sync_q,
        actors_end_q,
        ):
        with tf.device("/device:CPU:0"):
            self.actor_run(
                simlator,
                actor_index,
                epsilon,
                model_args,
                args,
                experience_q,
                model_sync_q,
                actors_end_q)

    def actor_run(
        self,
        simlator,
        actor_index,
        epsilon,
        model_args,
        args,
        experience_q,
        model_sync_q,
        actors_end_q,
        ):
        print("Actor{} Starts!".format(actor_index))
        try:
            actor = Actor(
                simlator,
                actor_index,
                epsilon,
                model_args,
                args,
                experience_q,
                model_sync_q,
                training = not (args["test"])
            )

            # model load
            print(self.args["log_dir"]+self.args["load_weights_path"])
            if os.path.isfile(self.args["log_dir"]+self.args["load_weights_path"]):
                actor.model.load_weights(self.args["log_dir"]+self.args["load_weights_path"])

            # start
            actor.run_actor()
        except KeyboardInterrupt:
            pass
        except Exception:
            print(traceback.format_exc())
        finally:
            print("Actor{} End!".format(actor_index))
            actors_end_q.put(1)

    def get_model_args(self, batch_size):
        model_args  = {
            "batch_size": batch_size,
            "input_sequence": self.args["input_sequence"],
            "input_shape": self.args["input_shape"],
            "enable_image_layer": self.args["enable_image_layer"],
            "nb_actions": self.nb_actions,
            "enable_dueling_network": self.args["enable_dueling_network"],
            "dueling_network_type": self.args["dueling_network_type"],
            "enable_noisynet": self.args["enable_noisynet"],
            "dense_units_num": self.args["dense_units_num"],
            "lstm_units_num": self.args["lstm_units_num"],
            "metrics": self.args["metrics"],
        }
        return model_args


"""Werkzeugkasten zur Verarbeitung von OSM Daten.
https://wiki.openstreetmap.org/wiki/Key:highway
https://stackoverflow.com/questions/14716497/how-can-i-find-a-list-of-street-intersections-from-openstreetmap-data?rq=1
"""

import xml.etree.ElementTree as ET

def load_osm_file(filename):
    """Lade einen OSM-Datensatz ein."""
    tree = ET.parse(filename)
    root = tree.getroot()

    return root

def get_node_lat_lon(root):
    """Erstelle Woerterbuch mit allen Knoten und deren Laengen- /Breitengraden"""
    node_lat_lon = dict()
    for node in root.findall('node'):
        nodeID = node.get('id')
        Breitengrad = node.get('lat')
        Laengengrad = node.get('lon')
        node_lat_lon[nodeID] = [Breitengrad, Laengengrad]

    return node_lat_lon

if __name__ == '__main__':
    osm_root = load_osm_file('./HHG-BHF.osm')
    node_lat_lon = get_node_lat_lon(osm_root)

    ways = osm_root.findall('way')  # List mit Weg Elementen

    # ein Weg Element hat Eigenschaften und wiederum Kinder
#    <way id="14357125" visible="true" version="26" changeset="56091012" timestamp="2018-02-05T17:37:29Z" user="devbrain" uid="58073">
#  <nd ref="139466306"/>
#  <nd ref="1201451389"/>
#  <tag k="cycleway" v="lane"/>
#  <tag k="highway" v="primary"/>
#  <tag k="maxspeed" v="50"/>
#  <tag k="name" v="Eisenbahnstraße"/>
#  <tag k="ref" v="B 37"/>
#  <tag k="surface" v="asphalt"/>
# </way>
    einWeg = ways[0]
    print(einWeg.get('id'))
    print(einWeg.get('timestamp'))

    Kinder = einWeg.getchildren()
    einKind = Kinder[0]
    einKind.attrib
    einKind.tag


"""
Model Predictive Path Integral Controller
Based on Williams, Aldrich, Theodorou (2015)
"""

# Uncomment if you want to get interactive plots for MPPI in Pycharm on MacOS
# On other OS you have to chose a different interactive backend.
from matplotlib import use

import os
import matplotlib.pyplot as plt
import numpy as np
import yaml
import seaborn as sns

sns.set()
from CartPoleSimulation.CartPole.cartpole_model import TrackHalfLength
from CartPoleSimulation.CartPole.state_utilities import (
    ANGLE_IDX,
    ANGLED_IDX,
    POSITION_IDX,
    POSITIOND_IDX,
    STATE_INDICES,
    create_cartpole_state,
)
from matplotlib.widgets import Slider
from numba import jit
from numpy.random import SFC64, Generator
from SI_Toolkit.Predictors.predictor_ODE import predictor_ODE
from SI_Toolkit.Predictors.predictor_ODE_tf import predictor_ODE_tf
from SI_Toolkit.Predictors.predictor_autoregressive_tf import predictor_autoregressive_tf
from SI_Toolkit.Predictors.predictor_autoregressive_GP import predictor_autoregressive_GP
from scipy.interpolate import interp1d

from CartPoleSimulation.Control_Toolkit.Controllers import template_controller
from globals import *
import time as global_time
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "0"
import tensorflow as tf

config = yaml.load(open("config.yml", "r"), Loader=yaml.FullLoader)

NET_NAME = config["controller"]["mppi_RNN"]["NET_NAME"]
NET_TYPE = NET_NAME.split("-")[0]
CONTROLLER_CONFIG = "mppi_" + PREDICTOR

"""Timestep and sampling settings"""
dt = config["controller"][CONTROLLER_CONFIG]["dt"]
mpc_horizon = config["controller"][CONTROLLER_CONFIG]["mpc_horizon"]
mpc_samples = int(mpc_horizon / dt)  # Number of steps in MPC horizon
num_rollouts = config["controller"][CONTROLLER_CONFIG]["num_rollouts"]
predictor_name = config["controller"][CONTROLLER_CONFIG]["predictor_name"]

"""Perturbation factor"""
p_Q = config["cartpole"]["control_noise"]
dd_noise = ep_noise = ekp_noise = ekc_noise = cc_noise = config["controller"][CONTROLLER_CONFIG]["cost_noise"]
gui_dd = gui_ep = gui_ekp = gui_ekc = gui_cc = gui_ccrc = np.zeros(1, dtype=np.float32)

"""MPPI constants"""
R = config["controller"][CONTROLLER_CONFIG]["R"]
LBD = config["controller"][CONTROLLER_CONFIG]["LBD"]
GAMMA = config["controller"][CONTROLLER_CONFIG]["GAMMA"]
NU = config["controller"][CONTROLLER_CONFIG]["NU"]
SQRTRHODTINV = config["controller"][CONTROLLER_CONFIG]["SQRTRHOINV"] * (1 / np.math.sqrt(dt))
#GAMMA = config["controller"][CONTROLLER_CONFIG]["GAMMA"]
SAMPLING_TYPE = config["controller"][CONTROLLER_CONFIG]["SAMPLING_TYPE"]

"""Define Predictor"""
if predictor_name == "predictor_ODE_tf":
    predictor = predictor_ODE_tf(horizon=mpc_samples, dt=dt, intermediate_steps=10)
elif predictor_name == "predictor_ODE":
    predictor = predictor_ODE(horizon=mpc_samples, dt=dt, intermediate_steps=10)
elif predictor_name == "predictor_autoregressive_tf":
    predictor = predictor_autoregressive_tf(
        horizon=mpc_samples, batch_size=num_rollouts, net_name=NET_NAME
    )
elif predictor_name == "predictor_autoregressive_GP":
    predictor = predictor_autoregressive_GP(horizon=mpc_samples, num_rollouts=num_rollouts)

"""Cost function helpers"""
@jit(nopython=True, cache=True, fastmath=True)
def E_kin_cart(positionD):
    """Compute penalty for kinetic energy of cart"""
    return positionD ** 2

@jit(nopython=True, cache=True, fastmath=True)
def E_kin_pol(angleD):
    """Compute penalty for kinetic energy of pole"""
    return angleD ** 2

@jit(nopython=True, cache=True, fastmath=True)
def E_pot_pole(angle):
    """Compute penalty for not balancing pole upright (penalize large angles)"""
    return 0.25 * (1.0 - np.cos(angle)) ** 2
    #return angle ** 2 / 10

@jit(nopython=True, cache=True, fastmath=True)
def distance_difference_cost(position, target_position):
    """Compute penalty for distance of cart to the target position"""
    return (np.abs(position - target_position) / (2.0 * TrackHalfLength)) ** 2

@jit(nopython=True, cache=True, fastmath=True)
def border_barrier(position, border_threshold):
    return np.where(np.abs(position) > border_threshold * TrackHalfLength, 1, 0)

@jit(nopython=True, cache=True, fastmath=True)
def control_change_rate_cost(delta_u):
    """Compute penalty of control jerk, i.e. difference to previous control input"""
    return (delta_u) ** 2
    #return np.diff(delta_u) ** 2

@jit(nopython=True, cache=True, fastmath=True)
def reward_weighted_average(S: np.ndarray, delta_u: np.ndarray):
    """Average the perturbations delta_u based on their desirability

    :param S: Array of rollout costs
    :type S: np.ndarray
    :param delta_u: Array of perturbations
    :type delta_u: np.ndarray
    :return: Gain to update the vector of nominal inputs by. Vector of length (horizon_steps)
    :rtype: np.ndarray
    """
    rho = np.min(S)  # for numerical stability
    exp_s = np.exp(-1.0 / LBD * (S - rho))
    a = np.sum(exp_s)
    b = np.sum(np.multiply(np.expand_dims(exp_s, 1), delta_u) / a, axis=0)
    return b

@jit(nopython=True, cache=True, fastmath=True)
def update_inputs(u, S, delta_u):
    """Reward-weighted in-place update of nominal control inputs according to the MPPI method.

    :param u: Sampling mean / warm started control inputs of size (,mpc_samples)
    :type u: np.ndarray
    :param S: Cost array of size (num_rollouts)
    :type S: np.ndarray
    :param delta_u: The input perturbations that had been used, shape (num_rollouts x mpc_samples)
    :type delta_u: np.ndarray
    """
    return u + reward_weighted_average(S, delta_u)

class controller_mppi(template_controller):
    """Controller implementing the Model Predictive Path Integral method (Williams et al. 2015)

    :param template_controller: Superclass describing the basic controller interface
    :type template_controller: abc.ABC
    """
    def __init__(self):

        seed = config["controller"][CONTROLLER_CONFIG]["seed"]
        self.rng_mppi = Generator(SFC64(seed))
        self.rng_mppi_rnn = Generator(SFC64(seed * 2))  # There are some random numbers used at warm up of rnn only. Separate rng prevents a shift

        self.controller_name = 'mppi-tf'
        self.angleErr = 0.0
        self.positionErr = 0.0
        self.ANGLE_TARGET = 0.0

        self.logging_stage_cost = config["controller"][CONTROLLER_CONFIG]["logging"]["stage_cost"]
        self.logging_cost_to_go_breakdown = config["controller"][CONTROLLER_CONFIG]["logging"]["cost_to_go_breakdown"]
        self.logging_average_cost_to_go = config["controller"][CONTROLLER_CONFIG]["logging"]["average_cost_to_go"]
        self.logging_trajectory = config["controller"][CONTROLLER_CONFIG]["logging"]["trajectory"]
        self.logging_rollouts = config["controller"][CONTROLLER_CONFIG]["logging"]["rollouts"]

        # Cost Weights
        self.dd_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["dd_weight"] * (1 + dd_noise * self.rng_mppi.uniform(-1.0, 1.0))
        self.ep_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["ep_weight"] * (1 + ep_noise * self.rng_mppi.uniform(-1.0, 1.0))
        self.ekp_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["ekp_weight"] * (1 + ekp_noise * self.rng_mppi.uniform(-1.0, 1.0))
        self.ekc_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["ekc_weight"] * (1 + ekc_noise * self.rng_mppi.uniform(-1.0, 1.0))
        self.cc_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["cc_weight"] * (1 + cc_noise * self.rng_mppi.uniform(-1.0, 1.0))
        self.ccrc_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["ccrc_weight"]
        self.border_safety_weight = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["border_safety"]
        self.border_threshold = config["controller"][CONTROLLER_CONFIG]["stage_cost"]["border_threshold"]
        self.terminal_angle_weight = config["controller"][CONTROLLER_CONFIG]["terminal_cost"]["angle"]
        self.terminal_position_weight = config["controller"][CONTROLLER_CONFIG]["terminal_cost"]["position"]
        self.swing_through_weight = config["controller"][CONTROLLER_CONFIG]["terminal_cost"]["swing_through"]
        self.terminal_goal_angle = config["controller"][CONTROLLER_CONFIG]["terminal_goal"]["angle"]
        self.terminal_goal_position = config["controller"][CONTROLLER_CONFIG]["terminal_goal"]["position"]

        # State of the cart
        self.state = create_cartpole_state()
        self.target_position = 0.0
        self.rho_sqrt_inv = 0.01
        self.control_enabled = True

        self.s_horizon = np.zeros((), dtype=np.float32)
        self.u = np.zeros((mpc_samples), dtype=np.float32)
        self.u_prev = np.zeros_like(self.u, dtype=np.float32)
        self.delta_u = np.zeros((num_rollouts, mpc_samples), dtype=np.float32)
        self.rollout_costs = np.zeros((num_rollouts), dtype=np.float32)

        self.num_rollouts = num_rollouts
        self.horizon = mpc_samples
        self.predictor_name = predictor_name

        self.gamma = (GAMMA ** np.arange(self.horizon))

        # Logging
        self.logs = {
            # Cost To Go
            "cost_to_go_breakdown": [],
            "cost_to_go_breakdown_rollouts": [],
            "average_cost_to_go": [],
            "stage_cost_breakdown": [],
            # Rollouts
            "rollout_states": [],
            "rollout_inputs": [],
            "rollout_costs": [],
            "rollout_index": [],
            # Realized Trajectory
            "realized_trajectory": [],
            "realized_costs": [],
            "cost_to_go": [],
            # Nominal Trajectory
            "nominal_trajectory": [],
            "nominal_costs": [],
            # Reference Trajectory
            "reference_trajectory": [],
        }

    def initialize_perturbations(self, stdev=1.0, sampling_type=None):
        """Sample an array of control perturbations delta_u. Samples for two distinct rollouts are always independent

        :param stdev: standard deviation of samples if Gaussian, defaults to 1.0
        :param sampling_type: defaults to None, can be one of
            - "random_walk" - The next horizon step's perturbation is correlated with the previous one
            - "uniform" - Draw uniformly distributed samples between -1.0 and 1.0
            - "repeated" - Sample only one perturbation per rollout, apply it repeatedly over the course of the rollout
            - "interpolated" - Sample a new independent perturbation every 10th MPC horizon step. Interpolate in between the samples
            - "iid" - Sample independent and identically distributed samples of a Gaussian distribution
        :return: Independent perturbation samples of shape (num_rollouts x horizon_steps)
        """
        """
        Return a numpy array with the perturbations delta_u.
        If random_walk is false, initialize with independent Gaussian samples
        If random_walk is true, each row represents a 1D random walk with Gaussian steps.
        """
        if sampling_type == "random_walk":
            delta_u = np.empty((num_rollouts, mpc_samples), dtype=np.float32)
            delta_u[:, 0] = stdev * self.rng_mppi.standard_normal(
                size=(num_rollouts,), dtype=np.float32
            )
            for i in range(1, mpc_samples):
                delta_u[:, i] = delta_u[:, i - 1] + stdev * self.rng_mppi.standard_normal(
                    size=(num_rollouts,), dtype=np.float32
                )
        elif sampling_type == "uniform":
            delta_u = np.empty((num_rollouts, mpc_samples), dtype=np.float32)
            for i in range(0, mpc_samples):
                delta_u[:, i] = self.rng_mppi.uniform(
                    low=-1.0, high=1.0, size=(num_rollouts,)
                ).astype(np.float32)
        elif sampling_type == "repeated":
            delta_u = np.tile(
                stdev * self.rng_mppi.standard_normal(size=(num_rollouts, 1), dtype=np.float32),
                (1, mpc_samples),
            )
        elif sampling_type == "interpolated":
            step = 10
            range_stop = int(np.ceil((mpc_samples) / step) * step) + 1
            t = np.arange(start=0, stop=range_stop, step=step)
            t_interp = np.arange(start=0, stop=range_stop, step=1)
            t_interp = np.delete(t_interp, t)
            delta_u = np.zeros(shape=(num_rollouts, range_stop), dtype=np.float32)
            delta_u[:, t] = stdev * self.rng_mppi.standard_normal(size=(num_rollouts, t.size), dtype=np.float32)
            f = interp1d(t, delta_u[:, t])
            delta_u[:, t_interp] = f(t_interp)
            delta_u = delta_u[:, :mpc_samples]
        else:
            delta_u = stdev * self.rng_mppi.standard_normal(size=(num_rollouts, mpc_samples), dtype=np.float32)

        return delta_u

    def trajectory_rollouts(self, s, u, delta_u, target_position):
        """Sample thousands of rollouts using system model. Compute cost-weighted control update. Log states and costs if specified.

        :param s: Current state of the system
        :param S_tilde_k: Placeholder array to store the cost of each rollout trajectory
        :param u: Vector of nominal inputs computed in previous iteration
        :param delta_u: Array containing all input perturbation samples. Shape (num_rollouts x horizon_steps)
        :param u_prev: Array with nominal inputs from previous iteration. Used to compute cost of control change

        :return: rollout_costs - Array filled with a cost for each rollout trajectory
        """

        s = predictor.predict(s, (u + delta_u)[..., np.newaxis])[:, :, : len(STATE_INDICES)]

        # Compute costs
        total_cost, dd, ep, ekp, ekc, cc, ccrc, border = self.stage_cost(s[:, 1:, :], u, delta_u, target_position)
        terminal_cost = self.terminal_cost(s, target_position)
        rollout_costs = np.sum(total_cost, axis=1) + terminal_cost
        #rollout_costs = np.dot(total_cost, self.gamma) + terminal_cost

        # Pass costs to GUI popup window (0.3ms)
        global gui_dd, gui_ep, gui_ekp, gui_ekc, gui_cc, gui_ccrc
        gui_dd, gui_ep, gui_ekp, gui_ekc, gui_cc, gui_ccrc = (np.mean(dd), np.mean(ep), np.mean(ekp), np.mean(ekc), np.mean(cc), np.mean(ccrc))

        if self.logging_rollouts:
            self.logs.get("rollout_states").append(s)
            self.logs.get("rollout_inputs").append(u + delta_u)
            self.logs.get("rollout_costs").append(total_cost)
        if self.logging_cost_to_go_breakdown:
            self.cost_to_go_breakdown_rollouts = np.stack([rollout_costs, terminal_cost, dd.sum(axis=1), ep.sum(axis=1), ekp.sum(axis=1), ekc.sum(axis=1), cc.sum(axis=1), ccrc.sum(axis=1), border.sum(axis=1)], axis=0)

        return rollout_costs

    def stage_cost(self, s, u, delta_u, target_position):
        dd = self.dd_weight * distance_difference_cost(s[..., POSITION_IDX], target_position)
        ep = self.ep_weight * E_pot_pole(s[..., ANGLE_IDX])
        ekp = self.ekp_weight * E_kin_pol(s[..., ANGLED_IDX])
        ekc = self.ekc_weight * E_kin_cart(s[..., POSITIOND_IDX])
        cc = self.cc_weight * ((0.5 * (1 - 1.0 / NU) * R * delta_u ** 2 + R * u * delta_u + 0.5 * R * u ** 2))
        ccrc = self.ccrc_weight * control_change_rate_cost(delta_u)
        border = self.border_safety_weight * border_barrier(s[..., POSITION_IDX], self.border_threshold)

        total_cost = dd + ep + ekp + ekc + cc + ccrc + border

        return total_cost, dd, ep, ekp, ekc, cc, ccrc, border

    def sigmoid(self, x):
        return 1/(1 + np.exp(-50 * x))

    def terminal_cost(self, s, target_position):
        """Calculate terminal cost of a set of trajectories

        Williams et al use an indicator function type of terminal cost in
        "Information theoretic MPC for model-based reinforcement learning"

        TODO: Try a quadratic terminal cost => Use the LQR terminal cost term obtained
        by linearizing the system around the unstable equilibrium.

        :param s: Reference to numpy array of states of all rollouts
        :param target_position: Target position to move the cart to
        :return: One terminal cost per rollout
        """
        #SWING_THROUGH_HORIZON = 8
        #initial_angle = s[:, 0, ANGLE_IDX]
        #middle_angle = s[:, -SWING_THROUGH_HORIZON, ANGLE_IDX]
        terminal_angle = s[:, -1, ANGLE_IDX]
        terminal_position = s[:, -1, ANGLE_IDX]
        #mean_speed = np.mean(s[:, -SWING_THROUGH_HORIZON:, ANGLED_IDX], axis=1)

        terminal_cost = self.terminal_angle_weight * np.where(np.abs(terminal_angle) > (self.terminal_goal_angle * np.pi), 1, 0)
        terminal_cost += self.terminal_position_weight * np.where(np.abs(terminal_position - target_position) > self.terminal_goal_position * TrackHalfLength, 1, 0)

        # Punish Fast Swing Throughts (Changed Sign according to Average Speed & Level of Average Speed)
        #through_zero = np.where(2*np.sign(middle_angle)+np.sign(terminal_angle)+np.sign(mean_speed)==0, 1, 0)
        #terminal_cost += self.swing_through_weight * np.where(through_zero & (np.abs(terminal_angle) > 0.3), 1, 0) * np.abs(mean_speed)

        #terminal_cost = self.terminal_angle_weight * self.sigmoid(np.abs(terminal_states[:, ANGLE_IDX]) - (self.terminal_goal_angle * np.pi))
        #terminal_cost += self.terminal_position_weight * self.sigmoid(np.abs(terminal_states[:, POSITION_IDX] - target_position) - self.terminal_goal_position * TrackHalfLength)

        return terminal_cost

    # Step (Euler: 13.1, RNN:15.8ms)
    def step(self, s, target_position, time=None):
        """Perform controller step

        :param s: State passed to controller after system has evolved for one step
        :param target_position: Target position where the cart should move to
        :param time: Time in seconds that has passed in the current experiment, defaults to None
        :return: A normed control value in the range [-1.0, 1.0]
        """

        self.state = s
        self.target_position = np.float32(target_position)

        # Initialize Perturbations
        self.delta_u = self.initialize_perturbations(stdev=SQRTRHODTINV, sampling_type=SAMPLING_TYPE)
        self.delta_u = np.clip(self.delta_u, -1.0 - self.u, 1.0 - self.u, dtype=np.float32)

        # Trajectory Rollouts (Euler: 10.2ms, RNN:14.5ms)
        start = global_time.time()
        self.rollout_costs = self.trajectory_rollouts(self.state, self.u, self.delta_u, self.target_position)
        performance_measurement[1] = global_time.time() - start

        # Update inputs with weighted perturbations
        self.rollout_u = self.u + self.delta_u
        self.u = update_inputs(self.u, self.rollout_costs, self.delta_u)
        #self.u = self.rollout_u[np.argmin(self.rollout_costs)]
        self.u = np.clip(self.u, -1.0, 1.0, dtype=np.float32)

        # Realized Trajectory
        if self.logging_trajectory or self.logging_rollouts:
            self.logs.get("realized_trajectory").append(np.append(self.state, np.expand_dims(self.u[0], axis=0), axis=-1))

        # Rollouts
        if self.logging_rollouts:
            self.logs.get("reference_trajectory").append(target_position)
            self.current_cost = self.stage_cost(self.state, self.u[0], self.u_prev[1] - self.u[0], target_position)
            self.logs.get("realized_costs").append(self.current_cost)
            self.logs.get("cost_to_go").append(self.rollout_costs)
            chosen_rollout_index = np.linalg.norm(self.rollout_u - self.u, axis=1).argmin()
            self.logs.get("rollout_index").append(chosen_rollout_index)

        # Average Cost to Go
        if self.logging_average_cost_to_go:
            self.logs.get("average_cost_to_go").append(np.mean(self.rollout_costs))

        # Cost To Go Breakdown
        if self.logging_cost_to_go_breakdown:
            chosen_rollout_index = np.linalg.norm(self.rollout_u - self.u, axis=1).argmin()
            self.logs.get("cost_to_go_breakdown").append(np.copy(self.cost_to_go_breakdown_rollouts[:,chosen_rollout_index]))

        # Stage Cost Breakdown
        if self.logging_stage_cost:
            self.current_cost = self.stage_cost(self.state, self.u[0], self.u_prev[1] - self.u[0], target_position)
            self.logs.get("stage_cost_breakdown").append(np.copy(self.current_cost))

        Q = self.u[0]
        Q = np.float32(Q * (1 + p_Q * self.rng_mppi.uniform(-1.0, 1.0)))
        Q = np.clip(Q, -1.0, 1.0, dtype=np.float32)

        # Preserve current series of inputs
        self.u_prev = np.copy(self.u)

        # Index-shift inputs
        self.u[:-1] = self.u[1:]
        self.u[-1] = 0

        # Prepare predictor for next timestep (0.1ms)
        predictor.update_internal_state(np.atleast_3d(Q))

        return Q

    def print_help(self):
        print('*** Controller Informations here ***')

    def controller_report(self):
        # Trajectory
        if self.logging_trajectory:
            realized_states = np.stack(self.logs.get("realized_trajectory"), axis=0)
            realized_trajectories = realized_states[:, [ANGLE_IDX, ANGLED_IDX, POSITION_IDX, POSITIOND_IDX, -1]].T
            fig, axs = plt.subplots(len(realized_trajectories), 1, num=1, sharex=True, figsize=(8,12))
            units = [r'$\theta$ in rad', r'$\dot{\theta}$ in rad/s', 'x in m', r'$\dot{x}$ in m/s', 'Q ∈ [-1,1]']
            time_axis = dt * np.arange(start=0, stop=np.shape(realized_trajectories[0])[0])

            for j, realized_trajectory in enumerate(realized_trajectories):
                axs[j].clear()
                axs[j].plot(time_axis, realized_trajectory, label='Physical Trajectory', marker='.', markersize=2, linewidth=1, linestyle='--', color='black', alpha=0.5)
                axs[j].set_title(units[j])
                axs[j].tick_params(axis='both', which='major')
                axs[j].grid(True, which='both', linestyle='-.', color='grey', linewidth=0.5, alpha=0.5)

            fig.tight_layout()
            plt.xlabel('time in s')
            plt.show()

        ### Average Cost to Go
        if self.logging_average_cost_to_go:
            average_cost_to_go = np.stack(self.logs.get("average_cost_to_go"), axis=0)
            time_axis = dt * np.arange(start=0, stop=np.shape(average_cost_to_go)[0])
            plt.figure(num=3)
            plt.semilogy(time_axis, average_cost_to_go, marker='.', markersize=8)
            plt.title("Average Cost-to-go (over all rollouts)")
            plt.xlabel("time in s")
            plt.ylabel("Cost")
            plt.legend(fontsize='xx-small')
            plt.show()

        ### Cost to Go Breakdown
        if self.logging_cost_to_go_breakdown:
            cost_to_go_breakdown = np.stack(self.logs.get("cost_to_go_breakdown"), axis=0)
            time_axis = dt * np.arange(start=0, stop=np.shape(cost_to_go_breakdown)[0])
            labels = ['Total Cost to Go', 'Terminal Cost', 'Cart Distance', 'Pole Potential', 'Pole Kinetic', 'Cart Kinetic', 'Control Cost', 'Control Change Rate', 'Border Safety']

            plt.figure(num=4)
            for i in range(9):
                if cost_to_go_breakdown[:, i].max() > 1:
                    plt.semilogy(time_axis, cost_to_go_breakdown[:, i], label=labels[i], marker='.', markersize=2, linewidth=1)
            plt.title("Cost-to-go Breakdown (of selected rollout)")
            plt.xlabel("time in s")
            plt.ylabel("Cost")
            plt.legend(loc='lower left', fontsize='xx-small')
            plt.show()

        ### Stage Cost Breakdown
        if self.logging_stage_cost:
            realized_costs = np.stack(self.logs.get("stage_cost_breakdown"), axis=0)
            time_axis = dt * np.arange(start=0, stop=len(realized_costs[:, 0]))
            labels = ['Total Cost', 'Cart Distance', 'Pole Potential', 'Pole Kinetic', 'Cart Kinetic', 'Control Cost', 'Control Change Rate', 'Border Safety']

            plt.figure(num=5)
            for i in range(8):
                if realized_costs[:, i].max() > 1:
                    plt.semilogy(time_axis, realized_costs[:, i], label=labels[i], marker='.', markersize=2, linewidth=1)

            plt.title("Stage Cost Breakdown (of realized states)")
            plt.xlabel("time in s")
            plt.ylabel("Cost")
            plt.legend(loc='lower left', fontsize='xx-small')
            plt.show()

        # Rollouts
        if self.logging_rollouts:
            use('TkAgg')

            ## Plot Trajectories
            rollout_states = np.stack(self.logs.get("rollout_states"), axis=0)
            rollout_inputs = np.stack(self.logs.get("rollout_inputs"), axis=0)
            rollout_costs = np.stack(self.logs.get("rollout_costs"), axis=0)
            rollout_index = np.stack(self.logs.get("rollout_index"), axis=0)

            realized_states = np.stack(self.logs.get("realized_trajectory"), axis=0)
            realized_costs = np.stack(self.logs.get("realized_costs"), axis=0)

            if False:
                post_pred = predictor_autoregressive_tf(horizon=mpc_samples, batch_size=1, net_name=NET_NAME)
                experiment_length = trajectory.shape[0]

                # Calculate Predictions
                predictions = np.zeros((len(predictors.keys()), experiment_length, horizon + 1, 6))
                for i, name in enumerate(predictors):
                    predictor = predictors[name]
                    Q = np.pad(trajectory['Q'].to_numpy(), pad_width=(0, horizon))
                    s = trajectory[['angle', 'angleD', 'angle_cos', 'angle_sin', 'position', 'positionD']].to_numpy()

                    # Get predictions for whole trajectory
                    for timestep in tqdm(range(experiment_length)):
                        s_current = tf.convert_to_tensor(s[timestep, :], dtype=tf.float32)
                        Q_current = tf.convert_to_tensor(Q[np.newaxis, timestep:timestep + horizon], dtype=tf.float32)
                        predictions[i, timestep, 0] = s[timestep, :]
                        predictions[i, timestep, 1:] = predictor.predict(s_current, Q_current)
                        predictor.update_internal_state(Q_current[:, 0])

            nominal_states = rollout_states[np.arange(rollout_states.shape[0]), rollout_index, ...].squeeze()
            nominal_costs = rollout_costs[np.arange(rollout_costs.shape[0]), rollout_index, ...].squeeze()
            nominal_inputs = rollout_inputs[np.arange(rollout_inputs.shape[0]), rollout_index, ...].squeeze()

            reference_trajectory = np.stack(self.logs.get("reference_trajectory")[:-1], axis=0)
            cost_to_go = np.stack(self.logs.get("cost_to_go"), axis=0)

            # Create figure
            fig, axs = plt.subplots(nrows=6, ncols=1, num=2, sharex=True, gridspec_kw={"bottom": 0.15, "left": 0.1, "right": 0.84, "top": 0.95}, figsize=(16, 12))

            # Create time slider
            slider_axis = plt.axes([0.15, 0.02, 0.7, 0.03])
            slider = Slider(slider_axis, "timestep", 1, np.shape(rollout_states)[0], valinit=1, valstep=1)

            # Normalize cost to go to use as opacity in plot
            cost_to_go = np.divide(cost_to_go.T, np.max(np.abs(cost_to_go), axis=1)).T

            # This function updates the plot when a new iteration is selected
            def update_plot(i):
                rollouts = [rollout_states[i - 1, :, :, ANGLE_IDX], rollout_states[i - 1, :, :, ANGLED_IDX], rollout_states[i - 1, :, :, POSITION_IDX], rollout_states[i - 1, :, :, POSITIOND_IDX], rollout_inputs[i - 1], rollout_costs[i - 1]]
                realized_trajectories = [realized_states[:, ANGLE_IDX], realized_states[:, ANGLED_IDX], realized_states[:, POSITION_IDX], realized_states[:, POSITIOND_IDX], realized_states[:, -1], realized_costs[..., 0]]
                nominal_trajectories = [nominal_states[i - 1, :, ANGLE_IDX], nominal_states[i - 1, :, ANGLED_IDX], nominal_states[i - 1, :, POSITION_IDX], nominal_states[i - 1, :, POSITIOND_IDX], nominal_inputs[i - 1, :], nominal_costs[i - 1]]

                for j, ax in enumerate(axs):
                    ax.clear()
                    iteration = i - 1
                    costs = cost_to_go[i - 1, :]

                    if j==5:
                        ax.set_yscale('log')

                    # Rollouts (for current slider position)
                    rollout = rollouts[j]
                    for l in range(0, self.num_rollouts, 20):
                        ax.plot(
                            (iteration + np.arange(0, self.horizon)) * dt,
                            rollout[l, :],
                            alpha=0.15 * (1 - 0.3 * costs[l]) ** 2, color='limegreen', linestyle='-', linewidth=1, marker='.', markersize=3
                        )

                    # Realized Trajectories (for whole timeline)
                    realized_trajectory = realized_trajectories[j]
                    ax.plot(
                        np.arange(0, np.shape(realized_states)[0]) * dt,
                        realized_trajectory,
                        linestyle='--', linewidth=1, color='green', label='realized trajectory', marker='.', markersize=3
                    )

                    # Nominal Trajectories (for current slider position)
                    nominal_trajectory = nominal_trajectories[j]
                    ax.plot(
                        ((i - 1) + np.arange(0, nominal_trajectory.shape[0])) * dt,
                        nominal_trajectory,
                        linestyle='--', linewidth=1, color='red', label='nominal trajectory', marker='.', markersize=3
                    )

                # Limits
                #axs[0].set_xlim(0, np.shape(realized_states)[0] * dt)
                #axs[0].set_ylim(-TrackHalfLength * 1.05, TrackHalfLength * 1.05)

                # Labels
                axs[0].set_title("Monte Carlo Rollouts")
                axs[0].set_ylabel("angle (deg)", fontsize='x-small')
                axs[1].set_ylabel("angular rate (deg/s)", fontsize='x-small')
                axs[2].set_ylabel("position (m)", fontsize='x-small')
                axs[3].set_ylabel("velocity (m/s)", fontsize='x-small')
                axs[4].set_ylabel("input Q ∈ [-1,1]", fontsize='x-small')
                axs[5].set_ylabel("total cost", fontsize='x-small')
                axs[5].set_xlabel("time (s)", fontsize='small')

                # Legends
                axs[0].legend(loc="upper left", bbox_to_anchor=(1, 0, 0.16, 1), fontsize='xx-small')

            update_plot(1)
            slider.on_changed(update_plot)
            plt.show()

        self.logs = {
            # Cost To Go
            "cost_to_go_breakdown": [],
            "cost_to_go_breakdown_rollouts": [],
            "average_cost_to_go": [],
            "stage_cost_breakdown": [],
            # Rollouts
            "rollout_states": [],
            "rollout_inputs": [],
            "rollout_costs": [],
            "rollout_index": [],
            # Realized Trajectory
            "realized_trajectory": [],
            "realized_costs": [],
            "cost_to_go": [],
            # Nominal Trajectory
            "nominal_trajectory": [],
            "nominal_costs": [],
            # Reference Trajectory
            "reference_trajectory": [],
        }

    # Optionally: reset the controller after an experiment
    # May be useful for stateful controllers, like these containing RNN,
    # To reload the hidden states e.g. if the controller went unstable in the previous run.
    # It is called after an experiment,
    # but only if the controller is supposed to be reused without reloading (e.g. in GUI)
    def controller_reset(self):
        try:
            self.warm_up_countdown = self.wash_out_len
            # TODO: Not sure if this works for predictor autoregressive tf
            predictor.net.reset_states()
        except:
            pass


from random import shuffle, randint

resourcePool, warPool, humanPool = [], [], []
loopR = [34, 22, 10, 34, 22, 10, 34, 22, 10, 34, 22, 10, 20, 20, 20, 20, 20, 20, 8, 14, 20, 10]
loopW = [60, 30, 10, 60, 30, 10, 75, 25]
loopH = [15, 25, 10]

resource = ['綠色革命', '神農氏萬歲', '糧食酒', '天降黑森林', '人造林', '榆木椅', '挖礦機器人', '華鐵爐', '熔爐', '找到烏魯魯', '愚公移山', '留遷石', '免洗餐具', '菠菜罐頭', '石板烤肉', '金斧頭銀斧頭',
            '原始人', '鏗鏘鏗鏘', '蟹堡秘方', '國富論', '發小財', '發大財']
war = ['自強運動', '埃癸斯', '葵花寶典', '曼哈頓計畫', '甲不離人', '石中劍', '劍盾在身', '上下其手']
human = ['轉小人', '做中人', '登大人']
countryName = ['亞特蘭提斯', '阿斯嘉', '奧林帕斯', '瓦干達', '香格里拉',
               '瓦拉納西', '瑪雅', '塔爾塔洛斯', '特奧蒂瓦坎', '復活節島']

index = 0
for i in loopR:
    for j in range(i):
        resourcePool.append(resource[index])
    index += 1

index = 0
for i in loopW:
    for j in range(i):
        warPool.append(war[index])
    index += 1

index = 0
for i in loopH:
    for j in range(i):
        humanPool.append(human[index])
    index += 1

shuffle(resourcePool)
shuffle(warPool)
shuffle(humanPool)

Ecountry = {'亞特蘭提斯': 0, '阿斯嘉': 0, '奧林帕斯': 0, '瓦干達': 0,
            '香格里拉': 0, '瓦拉納西': 0, '瑪雅': 0, '塔爾塔洛斯': 0, '特奧蒂瓦坎': 0, '復活節島': 0}

for k, v in Ecountry.items():
    if v == 0:
        Ecountry[k] = [4, 2, 1]
    elif v == 1:
        Ecountry[k] = [5, 2, 2]
    elif v == 2:
        Ecountry[k] = [6, 1, 3]
    else:
        Ecountry[k] = [8, 1, 4]

sumR, sumW, sumH = sum(loopR), sum(loopW), sum(loopH)

for i in countryName:
    print(i, end=": ")
    for j in range(Ecountry[i][0]):
        print(resourcePool[randint(0, sumR - 1)], end=" ")
    for j in range(Ecountry[i][1]):
        print(humanPool[randint(0, sumH - 1)], end=" ")
    for j in range(Ecountry[i][2]):
        print(warPool[randint(0, sumW - 1)], end=" ")
    print()


#dict to bind weekdays to nums
weekDict = {'ma':1,'di':2,'wo':3,'do':4,'vr':5,'za':6,'zo':7}
#list of weekdays for printing in while loop
weekList = ['ma','di','wo','do','vr','za','zo']
#set default value of i
i = 0
#input with replaces so target always works with dict
target = input('Welke dag van de week\n').replace('dag','').replace('an','').replace('ens','').replace('ns','').replace('nder','').replace('ij','').replace('ter','').replace('n','')
while i < weekDict[target]:
    print(weekList[i])
    i += 1
#Sam heeft het uitgelegd aan mij, ik snap wat hij heeft gedaan.


from flask import current_app
from app import db

class Task(db.Model):
    task_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String)
    description = db.Column(db.String)
    completed_at = db.Column(db.DateTime, nullable=True)
    goal_id = db.Column(db.Integer, db.ForeignKey('goal.id'))
    goal = db.relationship("Goal", back_populates="tasks",
        single_parent=True,
        cascade="all, delete-orphan")

    def to_dict(self):
        is_complete = False if not self.completed_at else True
        if not self.goal_id:
            return {
                "id": self.task_id,
                "title": self.title,
                "description": self.description,
                "is_complete": is_complete
            }
        return {
                "id": self.task_id,
                "goal_id": self.goal_id,
                "title": self.title,
                "description": self.description,
                "is_complete": is_complete
            }


from opentrons import protocol_api
import json
import os
import math

# metadata
metadata = {
    'protocolName': 'Version 2 S9 Station A BP Purebase 20ul Internal Control',
    'author': 'Nick <protocols@opentrons.com>',
    'source': 'Custom Protocol Request',
    'apiLevel': '2.3'
}

NUM_SAMPLES = 96
SAMPLE_VOLUME = 200
INTERNAL_CONTROL_VOLUME = 20
TIP_TRACK = False

def run(ctx: protocol_api.ProtocolContext):

    # load labware
    tempdeck = ctx.load_module('Temperature Module Gen2', '10')
    tempdeck.set_temperature(4)
    num_cols = math.ceil(NUM_SAMPLES/8)
    num_ic_strips = math.ceil(INTERNAL_CONTROL_VOLUME*num_cols*1.1/200)
    cols_per_strip = math.ceil(num_cols/num_ic_strips)
    internal_control = tempdeck.load_labware(
        'opentrons_96_aluminumblock_generic_pcr_strip_200ul',
        'chilled tubeblock for internal control (strip 1)').rows()[0][:num_ic_strips]
    source_racks = [
        ctx.load_labware(
            'opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap', slot,
            'source tuberack ' + str(i+1))
        for i, slot in enumerate(['2', '3', '5', '6'])
    ]
    dest_plate = ctx.load_labware(
        'nest_96_wellplate_2ml_deep', '1', '96-deepwell sample plate')
    lys_buff = ctx.load_labware(
        'opentrons_6_tuberack_falcon_50ml_conical', '4',
        '50ml tuberack for lysis buffer + PK (tube A1)').wells()[0]
    tipracks300 = [ctx.load_labware('opentrons_96_tiprack_300ul', slot,
                                    '200µl filter tiprack')
                    for slot in ['8', '9', '11']]
    tipracks20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', '7',
                                   '20µl filter tiprack')]

    # load pipette
    m20 = ctx.load_instrument('p20_multi_gen2', 'left', tip_racks=tipracks20)
    p300 = ctx.load_instrument(
        'p300_single_gen2', 'right', tip_racks=tipracks300)
    p300.flow_rate.aspirate = 50
    p300.flow_rate.dispense = 300
    p300.flow_rate.blow_out = 300

    # setup samples
    sources = [
        well for rack in source_racks for well in rack.wells()][:NUM_SAMPLES]
    dests_single = dest_plate.wells()[:NUM_SAMPLES]
    dests_multi = dest_plate.rows()[0][:math.ceil(NUM_SAMPLES)]

    tip_log = {'count': {}}
    folder_path = '/data/A'
    tip_file_path = folder_path + '/tip_log.json'
    if TIP_TRACK and not ctx.is_simulating():
        if os.path.isfile(tip_file_path):
            with open(tip_file_path) as json_file:
                data = json.load(json_file)
                if 'tips300' in data:
                    tip_log['count'][p300] = data['tips300']
                else:
                    tip_log['count'][p300] = 0
                if 'tips20' in data:
                    tip_log['count'][m20] = data['tips20']
                else:
                    tip_log['count'][m20] = 0
    else:
        tip_log['count'] = {p300: 0, m20: 0}

    tip_log['tips'] = {
        p300: [tip for rack in tipracks300 for tip in rack.wells()],
        m20: [tip for rack in tipracks20 for tip in rack.rows()[0]]
    }
    tip_log['max'] = {
        pip: len(tip_log['tips'][pip])
        for pip in [p300, m20]
    }

    def pick_up(pip):
        nonlocal tip_log
        if tip_log['count'][pip] == tip_log['max'][pip]:
            ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \
resuming.')
            pip.reset_tipracks()
            tip_log['count'][pip] = 0
        pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]])
        tip_log['count'][pip] += 1

    heights = {lys_buff: 20}
    radius = (lys_buff.diameter)/2
    min_h = 5

    def h_track(tube, vol):
        nonlocal heights
        dh = vol/(math.pi*(radius**2))
        if heights[tube] - dh > min_h:
            heights[tube] = heights[tube] - dh
        else:
            heights[tube] = 5
        return tube.bottom(heights[tube])

    # transfer sample
    for s, d in zip(sources, dests_single):
        pick_up(p300)
        p300.transfer(SAMPLE_VOLUME, s.bottom(5), d.bottom(5), air_gap=20,
                       new_tip='never')
        p300.air_gap(20)
        p300.drop_tip()

    # transfer lysis buffer + proteinase K and mix
    for s, d in zip(sources, dests_single):
        pick_up(p300)
        p300.transfer(210, h_track(lys_buff, 210), d.bottom(5), air_gap=20,
                       mix_after=(10, 100), new_tip='never')
        p300.air_gap(20)
        p300.drop_tip()

    ctx.pause('Incubate sample plate (slot 4) at 55-57˚C for 20 minutes. \
Return to slot 4 when complete.')

    # transfer internal control
    for i, d in enumerate(dests_multi):
        pick_up(m20)
        strip_ind = i//cols_per_strip
        m20.transfer(INTERNAL_CONTROL_VOLUME, internal_control[strip_ind],
                     d.bottom(10), air_gap=20-INTERNAL_CONTROL_VOLUME,
                     new_tip='never')
        m20.air_gap(5)
        m20.drop_tip()

    ctx.comment('Move deepwell plate (slot 4) to Station B for RNA \
extraction.')

    # track final used tip
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        data = {
            'tips300': tip_log['count'][p300],
            'tips20': tip_log['count'][m20]
        }
        with open(tip_file_path, 'w') as outfile:
            json.dump(data, outfile)

Station A/BP Purebase P300S 20ul IC/v2_station_a_S9_bp_purebase.py


from string_with_arrows import *
import string

# Constants		####################################################

DIGITS = '0123456789'
LETTERS = string.ascii_letters
LETTERS_DIGISTS = LETTERS + DIGITS

# Tokens		####################################################

TT_INT		 	= 'INT'
TT_FLOAT 		= 'FLOAT'
TT_PLUS 	 	= 'PLUS'
TT_MINUS 	 	= 'MINUS'
TT_MUL 		 	= 'MUL'
TT_DIV 		 	= 'DIV'
TT_EQ		 	= 'EQ'
TT_LPAREN 	 	= 'LPAREN'
TT_RPAREN	 	= 'RPAREN'
TT_IDENTIFIER 	= 'IDENTIFIER'
TT_KEYWORD		= 'KEYWORD'
TT_EOF 		 	= 'EOF'
TT_POW 		 	= 'POW'
KEYWORDS = [
	'vr'
]

class Token:
	def __init__(self, type_, value=None, pos_start=None, pos_end=None):
		self.type = type_
		self.value = value
		if pos_start:
			self.pos_start = pos_start.copy()
			self.pos_end = pos_start.copy()
			self.pos_end.advance()

		if pos_end:
			self.pos_end = pos_end

	def matches(self, type_, value):
		return self.type == type_ and self.value == value

	def __repr__(self):
		if self.value: return f'{self.type}:{self.value}'
		return f'{self.type}'

# Errors		####################################################

class Error:
	def __init__(self, pos_start, pos_end,  error_name, details):
		self.error_name = error_name
		self.details = details
		self.pos_start = pos_start
		self.pos_end = pos_end

	def as_string(self):
		result = f'{self.error_name}:{self.details}\n'
		result += f'File{self.pos_start.fn}, line{self.pos_start.ln +1}'
		result += '\n\n' + string_with_arrows(self.pos_start.ftxt, self.pos_start, self.pos_end)
		return result

class CharacterNotFound(Error):
	def __init__(self, pos_start, pos_end, details):
		super().__init__( pos_start, pos_end,'Illegal Character ', details)

class InvalidSyntaxError(Error):
	def __init__(self, pos_start, pos_end, details=''):
		super().__init__( pos_start, pos_end,'Invalid Systax ', details)

class RTError(Error):
	def __init__(self, pos_start, pos_end, details, context):
		super().__init__( pos_start, pos_end,'Runtime Error ', details)
		self.context = context

	def as_string(self):
		result = self.generate_traceback()
		result += f'{self.error_name}:{self.details}\n'
		result += '\n' + string_with_arrows(self.pos_start.ftxt, self.pos_start, self.pos_end)
		return result
	def generate_traceback(self):
		result = ''
		pos = self.pos_start
		ctx = self.context

		while ctx:
			result = f'File {pos.fn}, line {str(pos.ln+1)}, in {ctx.display_name}\n' + result
			pos = ctx.parent_entry_pos
			ctx = ctx.parent

		return 'Traceback (most recent call last):\n' + result

# Position 		####################################################

class Position:
	def __init__(self, idx, ln, col, fn, ftxt):
		self.idx = idx
		self.ln = ln
		self.col = col
		self.fn = fn
		self.ftxt = ftxt

	def advance(self, current_char=None):
		self.idx += 1
		self.col += 1

		if current_char == '\n':
			self.ln += 1
			self.col = 0

		return self

	def copy(self):
		return Position(self.idx, self.ln, self.col, self.fn, self.ftxt)

# Lexer			####################################################

class Lexer:
	def __init__(self, fn, text):
		self.fn = fn
		self.text = text
		self.pos = Position(-1, 0, -1, fn, text)
		self.current_char = None
		self.advance()

	def advance(self):
		self.pos.advance(self.current_char)
		self.current_char = self.text[self.pos.idx] if self.pos.idx < len(self.text) else None

	def create_tokens(self):
		tokens = []

		while self.current_char != None:
			if self.current_char in ' \t':
				self.advance()
			elif self.current_char in DIGITS:
				tokens.append(self.create_number())
			elif self.current_char in LETTERS:
				tokens.append(self.make_identifier())
			elif self.current_char == '+':
				tokens.append(Token(TT_PLUS, pos_start=self.pos))
				self.advance()
			elif self.current_char == '-':
				tokens.append(Token(TT_MINUS, pos_start=self.pos))
				self.advance()
			elif self.current_char == '*':
				tokens.append(Token(TT_MUL, pos_start=self.pos))
				self.advance()
			elif self.current_char == '/':
				tokens.append(Token(TT_DIV, pos_start=self.pos))
				self.advance()
			elif self.current_char == '(':
				tokens.append(Token(TT_LPAREN, pos_start=self.pos))
				self.advance()
			elif self.current_char == ')':
				tokens.append(Token(TT_RPAREN, pos_start=self.pos))
				self.advance()
			elif self.current_char == '=':
				tokens.append(Token(TT_EQ, pos_start=self.pos))
				self.advance()
			elif self.current_char == '^':
				tokens.append(Token(TT_POW, pos_start=self.pos))
				self.advance()
			else:
				pos_start = self.pos.copy()
				char = self.current_char
				self.advance()
				return [], CharacterNotFound(pos_start, self.pos, "'"+char+"'")

		tokens.append(Token(TT_EOF, pos_start=self.pos))
		return tokens, None

	def create_number(self):
		num_str = ''
		dot_count = 0
		pos_start = self.pos.copy()

		while self.current_char != None and self.current_char in DIGITS + '.':
			if self.current_char == '.':
				if dot_count == 1: break
				dot_count += 1
				num_str += '.'
			else:
				num_str += self.current_char
			self.advance()

		if dot_count == 0:
			return Token(TT_INT, int(num_str), pos_start, self.pos)
		else:
			return Token(TT_FLOAT, float(num_str), pos_start, self.pos)

	def make_identifier(self):
		id_str = ''
		pos_start = self.pos.copy()
		while self.current_char != None and self.current_char in LETTERS_DIGISTS + '_':
			id_str += self.current_char
			self.advance()

		tok_type = TT_KEYWORD if id_str in KEYWORDS else TT_IDENTIFIER
		return Token(tok_type, id_str, pos_start, self.pos)

# Node 			####################################################

class NumberNode:
	def __init__(self, token):
		self.token = token
		self.pos_start = self.token.pos_start
		self.pos_end = self.token.pos_end

	def __repr__(self):
		return f'{self.token}'

class VarAccessNode:
	def __init__(self, var_name_tok):
		self.var_name_tok = var_name_tok

		self.pos_start = self.var_name_tok.pos_start
		self.pos_end = self.var_name_tok.pos_end

class VarAssignNode:
	def __init__(self, var_name_tok, value_node):
		self.var_name_tok = var_name_tok
		self.value_node = value_node

		self.pos_start = self.var_name_tok.pos_start
		self.pos_end = self.value_node.pos_end

class BinOpNode:
	def __init__(self, left_node, op_token, right_node):
		self.left_node = left_node
		self.op_token = op_token
		self.right_node = right_node

		self.pos_start = self.left_node.pos_start
		self.pos_end = self.right_node.pos_end

	def __repr__(self):
		return f'({self.left_node}, {self.op_token}, {self.right_node})'

class UnaryOpNode:
	def __init__(self, op_token, node):
		self.op_token = op_token
		self.node = node

		self.pos_start = self.op_token.pos_start
		self.pos_end = node.pos_end

	def __repr__(self):
		return f'({self.op_token}, {self.node})'

# Parser 		####################################################

class Parser:
	def __init__(self, tokens):
		self.tokens = tokens
		self.tok_idx = -1
		self.advance()

	def advance(self):
		self.tok_idx += 1
		if self.tok_idx < len(self.tokens):
			self.current_tok = self.tokens[self.tok_idx]
		return self.current_tok

	def parse(self):
		res = self.expression()
		if not res.error and self.current_tok.type != TT_EOF:
			return res.failure(InvalidSyntaxError(self.current_tok.pos_start, self.current_tok.pos_end, "Expected '+', '-', '*', '/', '^'"))
		return res

	def atom(self):
		res = ParserResult()
		tok = self.current_tok

		if tok.type in (TT_INT, TT_FLOAT):
			res.register_advancement()
			self.advance()
			return res.success(NumberNode(tok))

		elif tok.type == TT_IDENTIFIER:
			res.register_advancement()
			self.advance()
			return res.success(VarAccessNode(tok))

		elif tok.type == TT_LPAREN:
			res.register_advancement()
			self.advance()
			expr = res.register(self.expression())
			if res.error: return res
			if self.current_tok.type == TT_RPAREN:
				res.register_advancement()
				self.advance()
				return res.success(expr)
			else:
				return res.failure(InvalidSyntaxError(self.current_tok.pos_start, self.current_tok.pos_end, "Expected ')'"))

		return res.failure(InvalidSyntaxError(tok.pos_start, tok.pos_end,
			"Expected int, float, identifier, '+', '-' or '('"))

	def power(self):
		return self.binary_operation(self.atom, (TT_POW, ), self.factor)

	def factor(self):
		res = ParserResult()
		tok = self.current_tok

		if tok.type in (TT_PLUS, TT_MINUS):
			res.register_advancement()
			self.advance()
			factor = res.register(self.factor())
			if res.error: return res
			return res.success(UnaryOpNode(tok, factor))

		return self.power()

	def term(self):
		return self.binary_operation(self.factor, (TT_MUL, TT_DIV))

	def expression(self):
		res = ParserResult()
		if self.current_tok.matches(TT_KEYWORD, 'vr'):
			res.register_advancement()
			self.advance()

			if self.current_tok.type != TT_IDENTIFIER:
				return res.failure(InvalidSyntaxError(
					self.current_tok.pos_start, self.current_tok.pos_end
					,"Expected Identifier" ))

			var_name = self.current_tok
			res.register_advancement()
			self.advance()

			if self.current_tok.type != TT_EQ:
				return res.failure(InvalidSyntaxError(
					self.current_tok.pos_start, self.current_tok.pos_end,
					"Expected '='"
				))
			res.register_advancement()
			self.advance()
			expr = res.register(self.expression())

			if res.error: return res
			return res.success(VarAssignNode(var_name, expr))

		node =  res.register(self.binary_operation(self.term, (TT_PLUS, TT_MINUS)))
		if res.error:
			return res.failure(InvalidSyntaxError(self.current_tok.pos_start, self.current_tok.pos_end,
			"Expected 'vr', int, float, identifier, '+', '-' or '('"))
		return res.success(node)

	def binary_operation(self, func_a, ops, func_b=None):
		if func_b == None:
			func_b = func_a
		res = ParserResult()

		left = res.register(func_a())
		if res.error: return res

		while self.current_tok.type in ops:
			op_tok = self.current_tok
			res.register_advancement()
			self.advance()
			right = res.register(func_b())
			if res.error: return res
			left = BinOpNode(left, op_tok, right)

		return res.success(left)

# Parser Result ####################################################

class ParserResult:
	def __init__(self):
		self.error = None
		self.node = None
		self.advance_count = 0

	def register_advancement(self):
		self.advance_count += 1

	def register(self, res):
		self.advance_count += res.advance_count
		if res.error: self.error = res.error
		return res.node

	def success(self, node):
		self.node = node
		return self

	def failure(self, error):
		if not self.error or self.advance_count == 0:
			self.error = error

		return self

# Runtime Result

class RTResult:
	def __init__(self):
		self.value = None
		self.error = None

	def register(self, res):
		if res.error: self.error = res.error
		return res.value

	def success(self, value):
		self.value = value
		return self

	def failure(self, error):
		self.error = error
		return self

# Values 		####################################################
class Number:
	def __init__(self, value):
		self.value = value
		self.set_pos()
		self.set_context()

	def set_pos(self, pos_start=None, pos_end=None):
		self.pos_start = pos_start
		self.pos_end = pos_end
		return self

	def set_context(self, context=None):
		self.context = context
		return self

	def add_to(self, other):
		if isinstance(other, Number):
			return Number(self.value + other.value).set_context(self.context), None

	def subtract_by(self, other):
		if isinstance(other, Number):
			return Number(self.value - other.value).set_context(self.context), None

	def multiply_by(self, other):
		if isinstance(other, Number):
			return Number(self.value * other.value).set_context(self.context), None

	def power(self, other):
		if isinstance(other, Number):
			return Number(self.value ** other.value).set_context(self.context), None

	def divide_by(self, other):
		if isinstance(other, Number):
			if other.value == 0:
				return None, RTError(other.pos_start,
				other.pos_end, "Division by zero",
				self.context)
			return Number(self.value / other.value).set_context(self.context), None

	def copy(self):
		copy = Number(self.value)
		copy.set_pos(self.pos_start, self.pos_end)
		copy.set_context(self.context)
		return copy

	def __repr__(self):
		return f'{self.value}'

# Context 		####################################################
class Context:
	def __init__(self, display_name, parent=None, parent_entry_pos = None):
		self.display_name = display_name
		self.parent = parent
		self.parent_entry_pos = parent_entry_pos
		self.symbol_table = None

# Symbot Table  ####################################################
class SymbolTable:
	def __init__(self):
		self.symbols = {}
		self.parent = None

	def get(self, name):
		value = self.symbols.get(name, None)
		if value == None and self.parent:
			return self.parent.get(name)
		return value

	def set(self, name, value):
		self.symbols[name] = value

	def remove(self, name):
		del self.symbols[name]

# Interpreter 	####################################################

class Interpreter:
	def visit(self, node, context):
		method_name = f'visit_{type(node).__name__}'
		method = getattr(self, method_name, self.no_visit_method)
		return method(node, context)

	def no_visit_method(self, node, context):
		raise Exception(f'No visit_{type(node).__name__} method defined')

	def visit_NumberNode(self, node, context):
		return RTResult().success(Number(node.token.value).set_context(context).set_pos(node.pos_start, node.pos_end))

	def visit_VarAccessNode(self, node, context):
		res = RTResult()
		var_name = node.var_name_tok.value
		value = context.symbol_table.get(var_name)

		if not value:
			return res.failure(RTError(
				node.pos_start, node.pos_end,
				f"'{var_name}' is not defined", context
			))
		value = value.copy().set_pos(node.pos_start, node.pos_end)
		return res.success(value)

	def visit_VarAssignNode(self, node, context):
		res = RTResult()
		var_name = node.var_name_tok.value
		value = res.register(self.visit(node.value_node, context))
		if res.error: return res

		context.symbol_table.set(var_name, value)
		return res.success(value)

	def visit_BinOpNode(self, node, context):
		res = RTResult()
		left = res.register(self.visit(node.left_node, context))
		if res.error: return res
		right = res.register(self.visit(node.right_node, context))
		if res.error: return res

		result = ""
		error = ""
		if node.op_token.type == TT_PLUS:
			result, error = left.add_to(right)
		elif node.op_token.type == TT_MINUS:
			result, error = left.subtract_by(right)
		elif node.op_token.type == TT_MUL:
			result, error = left.multiply_by(right)
		elif node.op_token.type == TT_DIV:
			result, error = left.divide_by(right)
		elif node.op_token.type == TT_POW:
			result, error = left.power(right)

		if error:
			return res.failure(error)
		else:
			return res.success(result.set_pos(node.pos_start, node.pos_end))

	def visit_UnaryOpNode(self, node, context):
		res = RTResult()
		number = res.register(self.visit(node.node, context))
		if res.error: return res

		error = None
		if node.op_token.type == TT_MINUS:
			number, error = number.multiply_by(Number(-1))

		if error:
			return res.failure(error)
		else:
			return res.success(number.set_pos(node.pos_start, node.pos_end))

# Run			####################################################

global_symbol_table = SymbolTable()
global_symbol_table.set("null", Number(0))

def run(fn, text):
	#Generate tokens
	lexer = Lexer(fn, text)
	tokens, error = lexer.create_tokens()
	if error: return None, error

	#Generate AST
	parser = Parser(tokens)
	ast = parser.parse()
	if ast.error: return None, ast.error

	#Run Interpreter
	interpreter = Interpreter()
	context = Context('<program>')
	context.symbol_table = global_symbol_table
	result = interpreter.visit(ast.node, context)

	return result.value, result.error


# -*- coding: utf-8 -*-
u"""PyTest for :mod:`sirepo.template.srw.py`

:copyright: Copyright (c) 2016 RadiaSoft LLC.  All Rights Reserved.
:license: http://www.apache.org/licenses/LICENSE-2.0.html
"""
from __future__ import absolute_import, division, print_function
import pytest

pytest.importorskip('srwl_bl')

from pykern import pkresource
from pykern import pkunit
from sirepo import srunit

@srunit.wrap_in_request(sim_types='srw')
def test_model_defaults():
    from pykern import pkconfig
    from sirepo.template import template_common
    from sirepo import simulation_db
    res = template_common.model_defaults('trajectoryReport', simulation_db.get_schema('srw'))
    assert res == {
        'notes': '',
        'plotAxisY2': 'None',
        'timeMomentEstimation': 'auto',
        'magneticField': '2',
        'initialTimeMoment': 0.0,
        'numberOfPoints': 10000,
        'plotAxisY': 'X',
        'plotAxisX': 'Z',
        'finalTimeMoment': 0.0,
    }
    model = {
        'numberOfPoints': 10,
        'finalTimeMoment': 1.0,
    }
    template_common.update_model_defaults(model, 'trajectoryReport', simulation_db.get_schema('srw'))
    assert model['numberOfPoints'] == 10
    assert model['finalTimeMoment'] == 1.0
    assert model['plotAxisX'] == 'Z'

@srunit.wrap_in_request(sim_types='srw')
def test_prepare_aux_files():
    from sirepo.template import template_common
    from pykern.pkdebug import pkdp
    from pykern import pkcollections
    import sirepo.auth
    import sirepo.auth.guest

    sirepo.auth.login(sirepo.auth.guest)

    # Needed to initialize simulation_db
    data = pkcollections.json_load_any('''{
        "simulationType": "srw",
        "models": {
            "simulation": {
                "sourceType": "t"
            },
            "tabulatedUndulator": {
                "undulatorType": "u_t",
                "magneticFile": "magnetic_measurements.zip"
            },
            "beamline": { }
        },
        "report": "intensityReport"
    }''')
    d = pkunit.work_dir()
    template_common.copy_lib_files(data, None, d)


import markdown
import markdown.extensions.codehilite
from sys import argv

# Mathjax Extension
class MathJaxPattern(markdown.inlinepatterns.Pattern):
    def __init__(self):
        markdown.inlinepatterns.Pattern.__init__(self,
                                                 r'(?<!\\)(\$\$?)(.+?)\2')

    def handleMatch(self, m):
        node = markdown.util.etree.Element('mathjax')
        node.text = markdown.util.AtomicString(
            m.group(2) + m.group(3) + m.group(2))
        return node

class MathJaxExtension(markdown.Extension):
    def extendMarkdown(self, md, md_globals):
        # Needs to come before escape matching because \ is pretty important in
        # LaTeX
        md.inlinePatterns.add('mathjax', MathJaxPattern(), '<escape')

def latex_friendly(configs=[]):
    return MathJaxExtension()

input_file = open(argv[1])
text = input_file.read()
file_name = input_file.name
file_name = file_name[:len(file_name) - 3] + '.html'
text = markdown.markdown(text,
                         extensions=[
                             'markdown.extensions.tables',
                             'markdown.extensions.fenced_code',
                             'markdown.extensions.codehilite',
                             latex_friendly()
                         ])

text = text.replace('<table', '<table class="table"')
text = text.replace('<img', '<img style="max-width:100%"')
text = text.replace(
    '
',
    '

')
text = text.replace('
', '

')

print(text)

# with open(file_name, 'w') as f:
#    f.write(text)