from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.db.models import F
# from django.template import loader
from django.views.generic import View
from communities.models import *
from news_feed.models import *
from notifications import models as notifications
from django.contrib.auth.models import User
# from django.urls import reverse
from .forms import *
import os
from datetime import datetime
import re
import os.path
import json
from itertools import chain
from notifications.models import Notification, Notificationlist

def home(request):
    groups = Group.objects.annotate(members=models.Count('subscribers') + models.Count('editors')).order_by('-members')
    context = {'groups': groups}
    return render(request, 'communities/base.html', context)

def community(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        personal_in_community_articles = PersonalInCommunityArticle.objects.filter(allowed=True, group=group)
        community_articles = CommunityArticle.objects.filter(allowed=True, group=group)
        articles = sorted(chain(personal_in_community_articles, community_articles),
                          key=lambda instance: instance.pubdate, reverse=True)

        personal_in_community_request_articles = PersonalInCommunityArticle.objects.filter(allowed=False)
        community_request_articles = CommunityArticle.objects.filter(allowed=False)
        request_articles = sorted(chain(personal_in_community_request_articles, community_request_articles),
                                  key=lambda instance: instance.pubdate)

        articles_count = len(articles)
        if request.user.is_authenticated and group.personal_in_community_articles.filter(allowed=False,
                                                                                author=request.user):
            author_request_articles = group.personal_in_community_articles.filter(allowed=False,
                                                                                author=request.user).order_by(
                '-pubdate')
        else:
            author_request_articles = []
        context = {
            'group': group,
            'articles': articles,
            'request_articles': request_articles,
            'articles_count': articles_count,
            'author_request_articles': author_request_articles
        }
        return render(request, 'communities/community.html', context)
    else:
        return render(request, 'communities/nogroup.html', status=404)

def information(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        articles_count = group.community_articles.filter(allowed=True).count()
        context = {'group': group, 'articles_count': articles_count}
        return render(request, 'communities/information.html', context)
    else:
        return render(request, 'communities/nogroup.html')

def collection(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        articles_count = group.community_articles.filter(allowed=True).count()
        context = {'group': group, 'articles_count': articles_count}
        return render(request, 'communities/collection.html', context)
    else:
        return render(request, 'communities/nogroup.html')

def unsubscribe(request, groupid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        group.unsubscribe(request.user)
    return HttpResponse()

def subscribe(request, groupid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        group.subscribe(request.user)
    return HttpResponse()

def like(request, groupid, articleid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        article = group.articles.get(id=articleid)
        article.like(request.user)
    return HttpResponse()

def removelike(request, groupid, articleid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        article = group.articles.get(id=articleid)
        article.removelike(request.user)
    return HttpResponse()

def createarticle(request, groupid):
    group = Group.objects.get(id=groupid)
    if request.user in group.editors.all() or request.user == group.admin or group.allowarticles == 1 or group.allowarticles == 2:
        if request.POST.get("text", ''):
            if request.POST.get("personal"):
                new_article = PersonalInCommunityArticle(group=group, author=request.user,
                                                         text=request.POST.get("text"))
            else:
                new_article = CommunityArticle(group=group, text=request.POST.get("text"))

            if group.allowarticles == 2 and not (request.user in group.editors.all() or request.user == group.admin):
                new_article.allowed = False
            new_article.save()

            for file in request.FILES.getlist('files'):
                if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                         'application/msword'] and file.size <= 5000000:
                    new_file = BasicArticleFile(name=file.name, file=file)
                    new_file.save()
                    new_article.files.files.add(new_file)
                    new_article.files.save()

            request.user.profile.last_online_update()
    slug = group.slug
    return HttpResponseRedirect('/groups/' + str(slug) + '/')

class AddToCollection(View):
    def get(self, request, groupid):
        group = Group.objects.get(id=groupid)
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/collection/')

    def post(self, request, groupid):
        group = Group.objects.get(id=groupid)
        if request.user.is_authenticated and (request.user in group.editors.all() or request.user == group.admin):
            for file in request.FILES.getlist('files'):
                if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                         'application/msword'] and file.size <= 5000000:
                    new_file = GroupFile(group=group, name=file.name, file=file)
                    new_file.save()

            request.user.profile.last_online_update()
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/collection/')

class EditArticle(View):
    def get(self, request, groupid, articleid):
        group = Group.objects.get(id=groupid)
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/')

    def post(self, request, groupid, articleid):
        bound_form = ArticleForm(request.POST)
        group = Group.objects.get(id=groupid)
        slug = group.slug
        if request.user in group.editors.all() or request.user == group.admin:
            article = BasicArticle.objects.get(id=articleid)
            if bound_form.is_valid():
                article.text = request.POST.get("text")
                article.save()

                for fileid in request.POST.get("removedfiles").split():
                    try:
                        file = article.files.get(id=int(fileid))
                        file.delete()
                    except:
                        pass

                for file in request.FILES.getlist('files'):
                    if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                             'application/msword'] and file.size <= 5000000:
                        new_file = ArticleFile(article=article, name=file.name, file=file)
                        new_file.save()

            request.user.profile.last_online_update()

        return HttpResponseRedirect('/groups/' + str(slug) + '/')

def creategroup(request):
    if request.user.is_authenticated:
        group = Group(groupname=request.POST.get("groupname"), admin=request.user, pubdate=datetime.now())
        group.save()
        group.slug = str(group.id)
        group.save()

        request.user.profile.last_online_update()
        return HttpResponse(group.slug)
    else:
        return HttpResponse(-1)

def edit(request, groupid):
    group = Group.objects.get(id=groupid)
    if request.user.is_authenticated:
        request.user.profile.last_online_update()
        if request.user in group.editors.all() or request.user == group.admin:
            if request.POST.get('type') == 'allowarticle':
                article = BasicArticle.objects.get(id=request.POST.get('data')).get_child()
                if article.__class__.__name__ != "PersonalArticle" and article.group == group:
                    article.allowed = True
                    article.save()

                    if article.__class__.__name__ == "PersonalInCommunityArticle":
                        if article.author.profile.notificationsettings.post_published_notifications:
                            notifications.create_notification_post_published(group, article.author, 'post_accepted')

                return HttpResponse('Ok')
            elif request.POST.get('type') == 'deletearticle':
                article = BasicArticle.objects.get(id=request.POST.get('data')).get_child()
                if article.__class__.__name__ != "PersonalArticle" and article.group == group:
                    if article.__class__.__name__ == "PersonalInCommunityArticle":
                        if article.author.profile.notificationsettings.post_published_notifications:
                            notifications.create_notification_post_published(group, article.author, 'post_accepted')
                    article.delete()

                return HttpResponse('Ok')
            elif request.POST.get('type') == 'getpost':
                article = BasicArticle.objects.get(id=request.POST.get('data'))
                return HttpResponse(article.text)
            elif request.POST.get('type') == 'deletepost':
                article = BasicArticle.objects.get(id=request.POST.get('data'))

                if request.user != article.author and not article.author is None:
                    try:
                        notification_text = "Ваш пост в " + group.groupname + " удален"
                        notification_name = "Пост удален"
                        notification_href = '/groups/' + group.slug + '/'
                        notification = Notification(not_text=notification_text, not_name=notification_name,
                                                    not_link=notification_href, not_date=datetime.now())
                        notification.save()
                        notificationlist = Notificationlist.objects.get(user=article.author).notifications
                        notificationlist.add(notification)
                    except:
                        print('Notification Error')

                article.delete()
                return HttpResponse('Ok')
            if request.POST.get('type') == 'delete_from_collection':
                # print(request.POST.get('file'))
                try:
                    groupfile = group.files.get(id=request.POST.get('file'))
                    groupfile.delete()
                except:
                    pass

                return HttpResponse('Ok')

        if request.POST.get('type') == 'delete_request_article':
            article = BasicArticle.objects.get(id=request.POST.get('data'))
            article.delete()
            return HttpResponse('Ok')
        elif request.POST.get('type') == 'plus' or request.POST.get('type') == 'plusplus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.plus(request.user)
        elif request.POST.get('type') == 'remove_plus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.remove_plus(request.user)
        elif request.POST.get('type') == 'minus' or request.POST.get('type') == 'minusminus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.minus(request.user)
        elif request.POST.get('type') == 'remove_minus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.remove_minus(request.user)

    if request.POST.get('type') == 'delete_from_collection' and not os.path.isfile(request.POST.get('file')):
        # print(request.POST.get('file'))
        try:
            groupfile = group.files.get(id=request.POST.get('file'))
            groupfile.delete()
        except:
            pass
        return HttpResponse('Ok')

    return HttpResponse('error0')

def moreedit(request, groupid):
    if request.user.is_authenticated:
        request.user.profile.last_online_update()
        group = Group.objects.get(id=groupid)
        slug = group.slug
        if request.user in group.editors.all() or request.user == group.admin:
            if request.POST.get('type') == 'public':
                group.public = not group.public
                if group.public:
                    for user in group.subrequests.all():
                        group.subscribers.add(user)
                    group.subrequests.clear()
                group.save()
                return HttpResponse('opened / closed')
            elif request.POST.get('type') == 'delete':
                group.delete()
                return HttpResponse('deleted')
            elif request.POST.get('type') == 'allowsub':
                user = User.objects.filter(id=request.POST.get('userid'))[0]

                group.subrequests.remove(user)
                group.subscribers.add(user)

                if user.profile.notificationsettings.accepted_to_group_notifications:
                    notifications.create_notification_accepted_to_group(group, user, 'request_accepted')

                return HttpResponse('allowed')
            elif request.POST.get('type') == 'rejectsub':
                user = User.objects.filter(id=request.POST.get('userid'))[0]
                group.subrequests.remove(user)

                if user.profile.notificationsettings.accepted_to_group_notifications:
                    notifications.create_notification_accepted_to_group(group, user, 'request_rejected')

                return HttpResponse('rejected')
            elif request.POST.get('type') == 'allowarticles':
                group.allowarticles = request.POST.get('data')
                group.save()
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'deletegroupimage':
                try:
                    os.remove('media/' + group.image.name)
                except:
                    pass
                group.image = False
                group.save()
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'uploadimage':
                file = request.FILES['groupimage']
                if file.content_type != 'image/png' and file.content_type != 'image/jpeg' and file.size <= 5000000:
                    return HttpResponseRedirect('/groups/' + str(slug) + '/edit/')
                try:
                    os.remove('media/' + group.image.name)
                except:
                    pass
                group.image = request.FILES['groupimage']

                new_article = CommunityArticle(group=group, text='В сообществе обновлена фотография')
                new_article.save()

                new_file = BasicArticleFile(files_list=new_article.files, name=request.FILES['groupimage'].name,
                                            file=request.FILES['groupimage'])
                new_file.save()

                group.save()
                return HttpResponseRedirect('/groups/' + str(slug) + '/edit/')
            elif request.POST.get('type') == 'checkslug':
                newslug = request.POST.get('slug').lower()

                if newslug.replace(' ', '') == '':
                    group.slug = str(group.id)
                    group.save()
                    return HttpResponse('/groups/' + group.slug + '/edit/')

                if newslug.replace(' ', '') != newslug:
                    return HttpResponse('spaces')

                if newslug == '':
                    return HttpResponse('empty')

                pattern = re.compile("^[a-zA-Z]+$")
                if not pattern.match(newslug):
                    return HttpResponse('wrong')

                if newslug == group.slug:
                    HttpResponse('/groups/' + group.slug + '/edit/')
                elif newslug == 'create' or Group.objects.filter(slug=newslug).exists():
                    return HttpResponse('hasAlready')

                if len(newslug) > 20:
                    return HttpResponse('error')

                try:
                    group.slug = newslug
                    group.save()
                except:
                    return HttpResponse('error')

                return HttpResponse('/groups/' + group.slug + '/edit/')
            elif request.POST.get('type') == 'banuser':
                user = User.objects.get(id=request.POST.get('userid'))
                try:
                    group.subscribers.remove(user)
                except:
                    pass
                try:
                    group.editors.remove(user)
                except:
                    pass
                group.banned.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'banned')
                return HttpResponse(user.username)
            elif request.POST.get('type') == 'cancelban':
                user = User.objects.filter(id=request.POST.get('userid'))[0]
                group.banned.remove(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'restored')
                return HttpResponse('restored')
            elif request.POST.get('type') == 'toeditor':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.subscribers.remove(user)
                group.editors.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'promoted')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'touser':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.editors.remove(user)
                group.subscribers.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'demoted')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'toadmin':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.editors.remove(user)
                group.admin = user
                group.save()
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'to_admin')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'editname':
                if request.POST.get('data').strip() != '' and len(request.POST.get('data')) <= 48:
                    group.groupname = request.POST.get('data')
                group.save()
                return HttpResponse('/groups/' + group.slug + '/edit/')
            elif request.POST.get('type') == 'editdescription':
                if len(request.POST.get('data')) <= 200:
                    group.description = request.POST.get('data')
                group.save()
                return HttpResponse('/groups/' + group.slug + '/edit/')
        elif request.POST.get('type') == 'sendsubrequest':
            group.send_subrequest(request.user)
            return HttpResponse('sent')
        elif request.POST.get('type') == 'cancelsubrequest':
            group.cancel_subrequest(request.user)
            return HttpResponse('cancelled')
            '''
			if request.user in group.subrequests.all():
				group.subrequests.remove(request.user)
				return HttpResponse('cancelled')
			else:
				group.subrequests.add(request.user)
				return HttpResponse('sent')
			'''
    return HttpResponse('error')

def editgroup(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)
        if group.admin != request.user:
            return HttpResponseRedirect('/groups/' + group.slug + '/')
        else:
            group = {'group': group}
            return render(request, 'communities/edit.html', group)
    else:
        return render(request, 'communities/nogroup.html')

'''
class EditGroup(View):
	def get(self, request, groupslug):
		group = Group.objects.filter(slug=groupslug)[0]
		if group.admin != request.user:
			HttpResponseRedirect('../')
		form = GroupEditForm()
		context = {'form': form, 'group': group}
		return render(request, 'communities/edit.html', context)

	def post(self, request, groupslug):
		# return HttpResponse(request.POST.get('slug'))
		href = '../../' + request.POST.get('slug') + '/edit'

		group = Group.objects.filter(slug=groupslug)[0]
		if group.admin != request.user:
			HttpResponseRedirect('../')
		bound_form = GroupEditForm(request.POST)
		form = GroupEditForm()

		if bound_form.is_valid():
			# return HttpResponse(request.POST.get('slug'))
			group = bound_form.save(group)
			context = {'form': form, 'group': group}
			href = '../../' + request.POST.get('slug') + '/edit'
			# return HttpResponseRedirect(href)
			# return render(request, 'communities/edit.html', context)
		context = {'form': bound_form, 'group': group}
		# return HttpResponse(request.POST.get('slug'))
		# return render(request, 'communities/edit.html', context)
		return HttpResponseRedirect(href)
'''


print('Adaptado por Tiago Andrade Borges Santos')
from Bio import SeqIO

print("Insira o nome do arquivo fasta")

arquivodeentrada = raw_input("fasta: ")

sequencias=SeqIO.parse(arquivodeentrada,'fasta')

f=open('gboculto.fas','w')

for seq in sequencias:
    nomes=seq.id.split('_')
    f.write('>'+nomes[0]+'_'+nomes[1]+'['+nomes[2]+']'+'\n')
    f.write(str(seq.seq)+'\n')
f.close()


import numpy as np
import cv2, random, sys, math, cProfile

### Problem 1 code following

def clip(val, minimum, maximum):
    return sorted((minimum, val, maximum))[1]

def leak_filter(img, bright_factor = 2, blend_factor = 0.8, rainbow = False):
    # Now we need to generate the light streak - randomly generating some y = mx + c
    # To start with I'll pick two x values in the central 1/3 of the image for the line to go between

    image_y, image_x, image_channels = img.shape # Get the dimensions of the image

    point_1 = (0, random.randint(2*image_x//5, 3*image_x//5))
    point_2 = (image_y, random.randint(2*image_x//5, 3*image_x//5)) # Pick two random points at the top and bottom of the image

    m = sys.maxsize & 2 + 1
    if point_1[1] != point_2[1]:
        m = image_y/(point_1[1]-point_2[1])
    c = image_y - m*point_2[1] # Work out the equation of the line between them

    # Compare each pixel against the line

    width = 30
    attenuation = 5 # Good values for making this work
    max_hue = 160

    mask = np.zeros(img.shape, np.uint8) # Generate a blank image to act as a mask

    img = np.copy(img)

    for y in range(image_y):
        for x in range(image_x): # Iterate through the image
            dist = abs(c + m*x - y)/math.sqrt(1 + math.pow(m, 2)) # Calculate the distance of between the point (x,y) and the line
            img.itemset((y, x, 0), max(img.item(y, x, 0)/bright_factor, 0))
            img.itemset((y, x, 1), max(img.item(y, x, 1)/bright_factor, 0))
            img.itemset((y, x, 2), max(img.item(y, x, 2)/bright_factor, 0)) # Darken it accordingly
            if dist < width: # If the resulting distance is less than the width of the beam
                new_bright_factor = bright_factor # Get ready to adjust the brightness factor depending on position in the beam
                if not rainbow:
                    if abs(dist) < width/1.5:
                        new_bright_factor *= 2-(1.5*abs(dist)/width) # This makes the centre of the beam brighter
                    mask.itemset((y, x, 0), min(img.item(y, x, 0)*new_bright_factor, 255))
                    mask.itemset((y, x, 1), min(img.item(y, x, 1)*new_bright_factor, 255))
                    mask.itemset((y, x, 2), min(img.item(y, x, 2)*new_bright_factor, 255)) # Copy brightness data into mask, capping at 255
                else: # If we're generating a rainbow, it's a bit more complicated
                    sat_factor = 1
                    if abs(dist) < width/3: # We use a narrower bright streak for this
                        new_bright_factor *= 2-(3*abs(dist)/width)
                        sat_factor = 0.1+0.9*(3*abs(dist)/width)
                    if m*x - y + c < 0:
                        dist = -dist # Work out which side of the line we're on (important for working out hue)
                    pix_bgr = np.uint8([[[img.item(y, x, 0), img.item(y, x, 1), img.item(y, x , 2)]]]) # Get the value of the pixel
                    pix_hsv = cv2.cvtColor(pix_bgr, cv2.COLOR_BGR2HSV) # Convert to HSV (rainbows are easier to make by varying hue)
                    pix_hsv.itemset((0, 0, 0), ((dist+width)/(2*width))*max_hue) # Calculate value for hue
                    pix_hsv.itemset((0, 0, 1), sat_factor*max(150, pix_hsv.item(0, 0, 1))) # Saturation should be at least 150, but also no less than the existing saturation
                    pix_hsv.itemset((0, 0, 2), clip(pix_hsv.item(0, 0, 2)*new_bright_factor, 50, 255)) # Apply brightening effect
                    pix_bgr = cv2.cvtColor(pix_hsv, cv2.COLOR_HSV2BGR) # Convert back to BGR
                    mask.itemset((y, x, 0), pix_bgr.item(0, 0, 0))
                    mask.itemset((y, x, 1), pix_bgr.item(0, 0, 1))
                    mask.itemset((y, x, 2), pix_bgr.item(0, 0, 2)) # Copy pixel data into mask
            elif dist < width + attenuation: # Smooth transition into normal image makes it look more natural
                att_factor = math.pow(bright_factor, 1 - 2*(dist-width)/attenuation) # Function to transition from bright to dark as we move further out
                if not rainbow:
                    mask.itemset((y, x, 0), min(img.item(y, x, 0)*att_factor, 255))
                    mask.itemset((y, x, 1), min(img.item(y, x, 1)*att_factor, 255))
                    mask.itemset((y, x, 2), min(img.item(y, x, 2)*att_factor, 255)) # Copy image data to mask
                else:
                    hue = 0 # Min hue at one end
                    if m*x - y + c > 0:
                        hue = max_hue # Otherwise max hue
                    pix_bgr = np.uint8([[[img.item(y, x, 0), img.item(y, x, 1), img.item(y, x , 2)]]]) # Get pixel data from image
                    pix_hsv = pix_hsv = cv2.cvtColor(pix_bgr, cv2.COLOR_BGR2HSV) # Convert to hsv
                    pix_hsv.itemset((0, 0, 0), hue) # Set hue
                    pix_hsv.itemset((0, 0, 1), max((150/bright_factor)*att_factor, pix_hsv.item(0, 0, 1))) # Saturation decided as before but also attenuating
                    pix_hsv.itemset((0 ,0 ,2), pix_hsv.item(0, 0, 2)*att_factor) # Brightness also attenuates
                    pix_bgr = cv2.cvtColor(pix_hsv, cv2.COLOR_HSV2BGR) # Convert back to BGR
                    mask.itemset((y, x, 0), pix_bgr.item(0, 0, 0))
                    mask.itemset((y, x, 1), pix_bgr.item(0, 0, 1))
                    mask.itemset((y, x, 2), pix_bgr.item(0, 0, 2)) # Copy pixel data into mask
            else:
                mask.itemset((y, x, 0), max(img.item(y, x, 0)/bright_factor, 0))
                mask.itemset((y, x, 1), max(img.item(y, x, 1)/bright_factor, 0))
                mask.itemset((y, x, 2), max(img.item(y, x, 2)/bright_factor, 0)) # Otherwise continue to copy image data through

    #img = cv2.addWeighted(img, 1-blend_factor, mask, blend_factor, 0) # Blend the two images together

    new_img = np.zeros(img.shape, np.uint8)
    for y in range(image_y):
        for x in range(image_x):
            new_img.itemset((y, x, 0), min(255, img.item(y, x, 0)*(1-blend_factor) + mask.item(y, x, 0)*blend_factor))
            new_img.itemset((y, x, 1), min(255, img.item(y, x, 1)*(1-blend_factor) + mask.item(y, x, 1)*blend_factor))
            new_img.itemset((y, x, 2), min(255, img.item(y, x, 2)*(1-blend_factor) + mask.item(y, x, 2)*blend_factor))

    return new_img

def problem1(img, bright_factor = 2, blend_factor = 0.8, rainbow = False):
    output = leak_filter(img, bright_factor, blend_factor, rainbow)
    cv2.imshow("Output - problem 1", output)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

### Problem 2 code following

def applyMask(img, mask): # Function to apply square mask to a greyscale image
    image_y, image_x = img.shape
    mask_y, mask_x = mask.shape # Get image and mask dimensions
    new_img = np.zeros(img.shape, dtype=np.uint8) # Create array for output image
    for y in range(image_y):
        for x in range(image_x): # Iterate through image pixels
            val = 0 # Variable to store sum as we go through the neighbourhood
            for j in range(mask_y):
                for i in range(mask_x): # Iterate through the size of the mask
                    offset = mask_y//2 # Calculate how far we are from the centre of the mask
                    pixel_y = y + j - offset
                    pixel_x = x + i - offset # Convert that into pixel coords

                    if pixel_y < 0:
                        pixel_y = -pixel_y
                    elif pixel_y > image_y - 1:
                        pixel_y = 2*(image_y-1) - pixel_y
                    if pixel_x < 0:
                        pixel_x = -pixel_x
                    elif pixel_x > image_x -1:
                        pixel_x = 2*(image_x-1) - pixel_x # If the pixel falls outside the image, "reflect" it back into valid coords

                    val += img.item(pixel_y,pixel_x)*mask.item(j,i) # Multiply the pixel by the mask and add it to the su
            new_img.itemset((y,x), int(clip(val, 0, 255))) # Copy the result of the sum into the output image
    return new_img

def pencilFilter(img, blend_factor): # Function to apply a pencil filter to a greyscale image (output image will also be greyscale)
    y_dim, x_dim = img.shape # Get dimensions of the shape

    noise = np.zeros(img.shape, dtype=np.uint8) # Generate empty image to use as noise mask

    for y in range(y_dim):
        for x in range(x_dim):
            noise.itemset((y, x), np.clip((1+np.random.normal(0, 0.1))*img.item(y, x), 0, 255)) # Generate random gaussian noise and merge with source

    # motion_blur_filter = np.array([[0, 0, 0, 0, 1/5],
    #                             [0, 0, 0, 1/5, 0],
    #                             [0, 0, 1/5, 0, 0],
    #                             [0, 1/5, 0, 0, 0],
    #                             [1/5, 0, 0, 0, 0]]) # Image filter for motion blur (just blurs along a line)

    motion_blur_filter = np.array([[0, 0, 0, 0, 0, 0, 1./14.],
                                   [0, 0, 0, 0, 0, 1./7., 0],
                                   [0, 0, 0, 0, 1./7., 0, 0],
                                   [0, 0, 0, 2./7., 0, 0, 0],
                                   [0, 0, 1./7., 0, 0, 0, 0],
                                   [0, 1./7., 0, 0, 0 ,0, 0],
                                   [1./14., 0, 0, 0, 0, 0, 0]], dtype=np.float64) # Image filter for motion blur (just blurs along a line)

    final_mask = cv2.filter2D(noise, 0, motion_blur_filter) # Convolves noisy image with motion blur to create effect

    new_img = np.zeros(img.shape, np.uint8) # Create new output image
    for y in range(y_dim):
        for x in range(x_dim): # Iterate through dimensions of source image
            new_img.itemset((y, x), min(255, img.item(y, x)*(1-blend_factor) + final_mask.item(y, x)*blend_factor))
            # Blend the images based on a blending factor

    return new_img

def final_pencil_filter(img, blend_factor=0.6, colour=False):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    y_dim, x_dim = img.shape

    if colour:
        channels = random.sample([0, 1, 2], 2)
        channel_1 = pencilFilter(img, blend_factor)
        channel_2 = pencilFilter(img, blend_factor)
        imgs = [channel_1, channel_2]
        new_img = np.zeros(img.shape + (3,), np.uint8)
        for y in range(y_dim):
            for x in range(x_dim):
                for c in range(2):
                    new_img.itemset((y, x, channels[c]), imgs[c].item(y, x))
    else:
        new_img = pencilFilter(img, blend_factor)

    return new_img

def problem2(img, blend_factor=0.6, colour=False):
    output = final_pencil_filter(img, blend_factor, colour)
    cv2.imshow("Output - problem 2", output)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

### Problem 3 code following

gaussians = {}

def gaussian(x, sigma=1, mu=0):
    if (x, sigma, mu) in gaussians:
        return gaussians[(x, sigma, mu)]
    else:
        value = (1/(sigma*((2*math.pi)**0.5)))*math.e**(-0.5*((x-mu)**2)/(sigma**2))
        gaussians[(x, sigma, mu)] = value
        return value

def bilateralFilter(img, colour_sigma, space_sigma):
    output = np.zeros(img.shape, np.uint8) # Create new image for output (we can't do this in place)
    y_dim = img.shape[0]
    x_dim = img.shape[1] # Get dimensions of image
    #d = max(colour_sigma, space_sigma)
    d = 3
    for y in range(y_dim):
        for x in range(x_dim): # Iterate through every pixel
            bilateral_numerator_BGR = [0, 0, 0]
            bilateral_denominator_BGR = [0, 0, 0] # Variables for numerator and denominator (per lecture 5 slide 31)
            pixel = [img.item(y, x, 0), img.item(y, x, 1), img.item(y, x, 2)] # Grab the pixel data here (no need to redo this for every neighbour)
            for j in range(y-d, y+d):
                for i in range(x-d, x+d): # Get neighbourhood
                    if j < 0: # Series of if statements "reflect" coords outside of the image back within the bounds of the image
                        j = -j
                    if i < 0:
                        i = -i
                    if j > y_dim-1:
                        j = 2*(y_dim-1) - j # Equivalent to j = y_dim - (j - y_dim)
                    if i > x_dim-1:
                        i = 2*(x_dim-1) - i
                    cart_dist = ((y-j)**2 + (x-i)**2)**0.5 # Calculate the cartesian distance between pixels (same for all channels)
                    gauss_dist = gaussian(cart_dist, space_sigma) # Get the output of the gaussian function for the distance component
                    for channel in range(3): # Handle each colour channel separately
                        coords = (j, i, channel)
                        weight = gauss_dist*gaussian(abs(img.item(coords)-pixel[channel]), colour_sigma) # Get the gaussian value for the intensity difference
                        bilateral_denominator_BGR[channel] += weight
                        bilateral_numerator_BGR[channel] += weight*img.item(coords) # Add to the summations on the top and bottom of the fraction for I_output
            for channel in range(3): # Loop to copy calculated data into the new image
                output.itemset((y, x, channel), bilateral_numerator_BGR[channel]//bilateral_denominator_BGR[channel])
    return output

def contrast_stretch(img, a=255, b=0):
    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(img_hsv)
    c = np.amax(v)
    d = np.amin(v)
    for y in range(v.shape[0]):
        for x in range(v.shape[1]):
            v.itemset((y, x), (v.item(y, x)-c)*((a-b)/(c-d))+a)
    img_hsv = cv2.merge([h, s, v])
    return cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR)

def beauty_filter(img, colour_sigma=20, space_sigma=10, mode="warm"):

    image_y, image_x, image_channels = img.shape # Get dimensions of image to work with

    new_img = bilateralFilter(img, colour_sigma, space_sigma) # Apply bilaterial filtering with given parameters

    new_img = contrast_stretch(new_img)

    red_adjust = {}
    blue_adjust = {} # Create lookup dictionaries for colour values

    if mode == "cold": # Same as warm, but drop red and increase blue
        red_pow = 0.96
        blue_pow = 1.04
        for i in range(256):
            red_adjust[i] = max(math.pow(i, red_pow), 0)
            blue_adjust[i] = min(math.pow(i, blue_pow), 255)
    else: # Generate colour values to "warm" image i.e. drop blue and increase red
        red_pow = 1.04
        blue_pow = 0.96 # Some powers for altering values
        for i in range(256): # For every possible red value
            red_adjust[i] = min(math.pow(i, red_pow), 255) # Raise to power and cap at 255
            blue_adjust[i] = max(math.pow(i, blue_pow), 0) # Raise to power and cap at 0

    for y in range(image_y):
        for x in range(image_x): # Iterate over the entire image
            new_img.itemset((y, x, 0), blue_adjust[new_img.item(y, x, 0)])
            new_img.itemset((y, x, 2), red_adjust[new_img.item(y, x, 2)]) # Replace the pixel value from the lookup table

    return new_img

def problem3(img, colour_sigma=20, space_sigma=10, mode="warm"):
    output = beauty_filter(img, colour_sigma, space_sigma, mode)
    cv2.imshow("Output - problem 3", output)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

### Problem 4 code following

def xy_to_polar(xy, centre):
    dx = xy[0] - centre[0]
    dy = xy[1] - centre[1]
    r = math.sqrt(dx**2 + dy**2)
    theta = math.atan2(dy, dx)
    return [r, theta]

def polar_to_xy(rtheta, centre):
    dx = rtheta[0] * math.cos(rtheta[1])
    dy = rtheta[0] * math.sin(rtheta[1])
    x = dx + centre[0]
    y = dy + centre[1]
    return [x, y]

def gaussian_low_pass(img, sigma):
    channels = cv2.split(img)
    for i in range(len(channels)):
        c = channels[i]
        c_fourier = np.fft.fftshift(np.fft.fft2(c))
        img_centre = (c_fourier.shape[0]//2, c_fourier.shape[1]//2)
        for y in range(c_fourier.shape[0]):
            for x in range(c_fourier.shape[1]):
                dx = x - img_centre[1]
                dy = y - img_centre[0]
                G = math.exp(-(dx**2 + dy**2)/2*(sigma**2))
                c_fourier.itemset((y, x), c_fourier.item(y, x)*G)
        channels[i] = np.uint8(np.clip(abs(np.fft.ifft2(np.fft.ifftshift(c_fourier))), 0, 255))
    return cv2.merge([channels[0], channels[1], channels[2]])

def subtract_image(img1, img2):
    return np.uint8(abs(np.subtract(img1, img2)))

def swirl_filter(img, angle=(3*math.pi/4), radius=150, interpolation="nn", prefilter=False):
    image_y, image_x, image_channels = img.shape
    output = np.zeros(img.shape, dtype=np.uint8)
    img_centre = (image_x//2, image_y//2)
    if prefilter:
        img = gaussian_low_pass(img, 0.0125)
    for y in range(image_y):
        for x in range(image_x):
            src_coords = [x,y]
            dist_from_centre = math.sqrt((x-img_centre[0])**2 + (y-img_centre[1])**2)
            if dist_from_centre < radius:
                as_polar = xy_to_polar((x, y), img_centre)
                as_polar[1] += angle*((1-(dist_from_centre/radius))**1.5)
                src_coords = polar_to_xy(as_polar, img_centre)
            for c in range(image_channels):
                if interpolation == "nn":
                    input_x = clip(round(src_coords[0]), 0, image_x-1)
                    input_y = clip(round(src_coords[1]), 0, image_y-1)
                    output.itemset((y, x, c), img.item(int(input_y), int(input_x), c))
                else:
                    x1 = int(clip(math.floor(src_coords[0]), 0, image_x-1))
                    y1 = int(clip(math.floor(src_coords[1]), 0, image_y-1))
                    x2 = int(clip(math.ceil(src_coords[0]), 0, image_x-1))
                    y2 = int(clip(math.ceil(src_coords[1]), 0, image_y-1))
                    input_x = clip(src_coords[0], 0, image_x-1)
                    input_y = clip(src_coords[1], 0, image_y-1)
                    if x2 != x1:
                        I_y1 = img.item(y1, x1, c)*(x2-input_x)/(x2-x1) + img.item(y1, x2, c)*(input_x-x1)/(x2-x1)
                        I_y2 = img.item(y2, x1, c)*(x2-input_x)/(x2-x1) + img.item(y2, x2, c)*(input_x-x1)/(x2-x1)
                    else:
                        I_y1 = img.item(y1, x1, c)
                        I_y2 = img.item(y2, x1, c)
                    if y2 != y1:
                        I = I_y1*(y2-input_y)/(y2-y1) + I_y2*(input_y-y1)/(y2-y1)
                    else:
                        I = I_y1
                    output.itemset((y, x, c), I)

    return output

def problem4(img, angle=(3*math.pi/4), radius=150):
    filter_nn = swirl_filter(img, angle, radius)
    cv2.imshow("Swirl filter - NN interpolation", filter_nn)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

    filter_bi = swirl_filter(img, angle, radius, interpolation='bi')
    cv2.imshow("Swirl filter - bilinear interpolation", filter_bi)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

    filter_nn_pre = swirl_filter(img, angle, radius, prefilter=True)
    cv2.imshow("Swirl filter - NN interpolation + prefiltering", filter_nn_pre)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

    filter_bi_pre = swirl_filter(img, angle, radius, interpolation='bi', prefilter=True)
    cv2.imshow("Swirl filter - bilinear interpolation + prefiltering", filter_bi_pre)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

    filter_invert = swirl_filter(filter_bi, -angle, radius, interpolation='bi', prefilter=True)
    cv2.imshow("Swirl filter - bilinear interpolation (inversed)", filter_invert)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

    filter_invert_diff = subtract_image(img, filter_invert)
    cv2.imshow("Swirl filter - difference between inverse and original", filter_invert_diff)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()

if __name__ == "__main__":
    problem = int(sys.argv[1])
    output = np.zeros((1,1,3), np.uint8)
    if len(sys.argv) < 3:
        print("Specify image file!")
    args = sys.argv[2:]
    img = cv2.imread(args[0], cv2.IMREAD_COLOR)
    if problem == 1:
        if len(args) == 1:
            print("No filter params given, using defaults.")
            output = leak_filter(img)
        elif len(args) == 2:
            print("Only rainbow param given, using defaults for others.")
            output = leak_filter(img, rainbow=(args[1]=="rainbow"))
        elif len(args) < 4:
            print("Please give both brightness/blending parameters or none at all.")
            sys.exit()
        else:
            output = leak_filter(img, float(args[2]), float(args[3]), (args[1]=="rainbow"))
    elif problem == 2:
        if len(args) == 1:
            print("No filter params given, using defaults.")
            output = final_pencil_filter(img)
        elif len(args) == 2:
            print("Only blend factor given, defaulting to b/w.")
            output = final_pencil_filter(img, float(args[1]))
        else:
            output = final_pencil_filter(img, float(args[1]), args[2] == "colour")
    elif problem == 3:
        if len(args) == 1:
            print("No filter params given, using defaults.")
            output = beauty_filter(img)
        elif len(args) == 2:
            print("Give both sigma values or neither.")
            sys.exit()
        elif len(args) == 3:
            print("No colour temp params given, defaulting to warm")
            output = beauty_filter(img, int(args[1]), int(args[2]))
        else:
            output = beauty_filter(img, int(args[1]), int(args[2]), args[3])
    elif problem == 4:
        if len(args) == 1:
            print("No filter params given, using defaults.")
            output = swirl_filter(img)
        elif len(args) == 2:
            print("Provide both swirl parameters or none at all")
            sys.exit()
        elif len(args) == 3:
            print("No interpolation/prefilter params given, defaulting to nearest neighbour and no prefiltering")
            output = swirl_filter(img, float(args[1]), int(args[2]))
        elif len(args) == 4:
            print("Defaulting to no prefiltering")
            output = swirl_filter(img, float(args[1]), int(args[2]), args[3])
        else:
            output = swirl_filter(img, float(args[1]), int(args[2]), args[3], args[4] == "prefilter")

    cv2.imshow("Output - final image", output)
    key = cv2.waitKey(0)
    if key == ord('x'):
        cv2.destroyAllWindows()


"""
This module defines Pydantic models for representing reservation-related data.
"""

from __future__ import annotations

from typing import Optional
from datetime import datetime

from pydantic import BaseModel as PydanticBase, Field, Extra, root_validator
from fastapi import Query
from sqlalchemy import select, and_, func

from ...util import format_description_with_example
from ...db.manager import SessionFacade
from ...db.models import Reservation as ReservationModel, \
    Restaurant as RestaurantModel, \
    Table as TableModel, \
    BusinessHour as BusinessHourModel

session = SessionFacade()

# Messages explaining the error-codes returned by the validation-methods.
# Used by the /reservations/validate-endpoints in restaurants and tables.
VALIDATION_ERRORS_RESTAURANT = {
    -1: "Reservation is not inside open-to-close timeframe and/or the restaurant does not accept "
        "reservations at the desired time.",
    -2: "There is no table with enough space for all the guests of the given reservation and/or other potential "
        "tables can only be reserved with more guests.",
    -3: "Reservation conflicts with other existing reservations on all potential tables."
}
VALIDATION_ERRORS_TABLE = {
    -1: "Reservation timeframe not inside open-to-close timeframe and/or the restaurant does not accept "
        "reservations at the desired time.",
    -2: "Not enough space for all the guests of the given reservation on the given table and/or the given table "
        "can only be reserved with more guests.",
    -3: "Reservation conflicts with other existing reservations on the table."
}

class ReservationNew(PydanticBase):
    """
    Represents a new reservation to be created.
    """
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int = Field(gt=0)
    customer_phone: Optional[str]
    additional_information: Optional[str]

    @root_validator(skip_on_failure=True)
    def validate_times(cls, values):
        if values['reserved_from'] > values['reserved_until']:
            raise ValueError("Reservation can't end before it has started.")
        return values

    class Config:
        schema_extra = {
            "example": {
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!"
            }
        }
        extra = Extra.forbid

    def cast_to_model(self, table_id: int) -> ReservationModel:
        """
        Convert a new reservation object to a SQLAlchemy model object.

        Args:
            table_id (int): The ID of the table to which the new reservation belongs.

        Returns:
            ReservationModel: The SQLAlchemy model object created from the new reservation object.
        """
        reservation_model = ReservationModel(
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information,
            table_id=table_id
        )
        return reservation_model

    def cast_to_put(self, reservation_id: int) -> ReservationPut:
        """
        Creates a new `ReservationPut` instance using the data from a `ReservationNew` instance and a reservation ID.

        Args:
            reservation_id (int): The ID of the reservation to be updated (primary key).

        Returns:
            ReservationPut: A new `ReservationPut` instance with the same reservation data as the `ReservationNew`
            instance.
        """
        reservation_put = ReservationPut(
            id=reservation_id,
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information,
        )
        return reservation_put

    def is_inside_business_hour_timeframe_for_restaurant(self, restaurant: RestaurantModel) -> bool:
        """
        Check if the reservation falls within the business hours of a restaurant.

        Args:
            restaurant (Restaurant): The SQLAlchemy restaurant.

        Returns:
            bool: True if the reservation is inside the business hours, False otherwise.
        """
        # Get business-hour entry for reservation.
        # E.g.: Monday 10-13, Monday 14-18, Reservation Monday 15-16 --> Monday 14-18
        qry = select(BusinessHourModel).where(and_(
            BusinessHourModel.restaurant_id == restaurant.id,
            BusinessHourModel.weekday == self.reserved_from.weekday(),
            BusinessHourModel.close_time >= self.reserved_until.time(),
            BusinessHourModel.open_time <= self.reserved_from.time(),
            BusinessHourModel.open_for_reservation_until >= self.reserved_from.time()
        ))
        business_hour = session.scalars(qry).first()
        return business_hour is not None

    def is_conflicting_with_existing_reservations_of_table(self, table: TableModel) -> bool:
        """
        Check if the reservation conflicts with existing reservations for a specific table.

        Args:
            table (Table): The SQLAlchemy table.

        Returns:
            bool: True if there is a conflict with existing reservations, False otherwise.
        """
        get_same_day_reservations_qry = select(ReservationModel).where(and_(
            ReservationModel.table_id == table.id,
            func.date(ReservationModel.reserved_from) == self.reserved_from.date()
        )).order_by(ReservationModel.reserved_from.asc())
        same_day_reservations = list(session.scalars(get_same_day_reservations_qry).all())

        # Find space of new reservation between same-day-reservations.
        # Add <self> to the list and sort by reserved_from (Pydantic- and SQLAlchemy-model
        # both have this attribute).
        # After that find the index <self> and validate from- and until-time with its
        # neighbours.
        is_conflicting = False
        if same_day_reservations:
            same_day_reservations.append(self)
            same_day_reservations.sort(key=lambda reservation: reservation.reserved_from)
            self_index = same_day_reservations.index(self)
            if self_index == 0:
                is_conflicting = self.reserved_until > same_day_reservations[self_index + 1].reserved_from
            elif self_index == len(same_day_reservations) - 1:
                is_conflicting = self.reserved_from < same_day_reservations[self_index - 1].reserved_until
            else:
                is_conflicting = (self.reserved_until > same_day_reservations[self_index + 1].reserved_from or
                                  self.reserved_from < same_day_reservations[self_index - 1].reserved_until)
        return is_conflicting

    def validate_for_restaurant(self, restaurant: RestaurantModel) -> int:
        """
        Validate the reservation for a specific restaurant.

        Args:
            restaurant (Restaurant): The SQLAlchemy restaurant.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: No tables available for the required number of guests
                  -3: Reservation conflicts with existing reservations on every potential table
        """
        if not self.is_inside_business_hour_timeframe_for_restaurant(restaurant):
            return -1

        get_potential_tables_qry = select(TableModel).where(and_(
            TableModel.restaurant_id == restaurant.id,
            TableModel.min_guests_required_for_reservation <= self.guest_amount,
            TableModel.seats >= self.guest_amount,
        ))
        potential_tables = session.scalars(get_potential_tables_qry).all()
        if not potential_tables:
            return -2

        potential_tables = [potential_table for potential_table in potential_tables
                            if not self.is_conflicting_with_existing_reservations_of_table(potential_table)]
        if not potential_tables:
            return -3

        # Prioritize tables by sorting the list by specified criteria
        potential_tables.sort(key=lambda potential_table: potential_table.seats - self.guest_amount)

        return potential_tables[0].id

    def validate_for_table(self, table: TableModel) -> int:
        """
        Validate the reservation for a specific table.

        Args:
            table (Table): The SQLAlchemy table.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: Specified table cannot accommodate all guests
                  -3: Reservation conflicts with existing reservations on the table
        """
        if not self.is_inside_business_hour_timeframe_for_restaurant(table.restaurant):
            return -1

        if (table.min_guests_required_for_reservation > self.guest_amount or
                table.seats < self.guest_amount):
            return -2

        if self.is_conflicting_with_existing_reservations_of_table(table):
            return -3

        return table.id

class Reservation(PydanticBase):
    """
    Represents a reservation.
    """
    id: int
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int
    customer_phone: Optional[str]
    additional_information: Optional[str]

    table_id: int

    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!",
                "table_id": 1
            }
        }
        extra = Extra.forbid

    @staticmethod
    def cast_from_model(reservation_model: ReservationModel) -> Reservation:
        """
        Convert a SQLAlchemy model object to a reservation object.

        Args:
            reservation_model (ReservationModel): The SQLAlchemy model object to be converted.

        Returns:
            Reservation: The reservation object created from the SQLAlchemy model object.
        """
        reservation = Reservation(
            id=reservation_model.id,
            customer_name=reservation_model.customer_name,
            customer_email=reservation_model.customer_email,
            reserved_from=reservation_model.reserved_from,
            reserved_until=reservation_model.reserved_until,
            guest_amount=reservation_model.guest_amount,
            customer_phone=reservation_model.customer_phone,
            additional_information=reservation_model.additional_information,
            table_id=reservation_model.table_id
        )
        return reservation

class ReservationPut(PydanticBase):
    """
    Represents a reservation to be updated.
    """
    id: int = Field(gt=0)
    customer_name: str
    customer_email: str
    reserved_from: datetime
    reserved_until: datetime
    guest_amount: int = Field(gt=0)
    customer_phone: Optional[str]
    additional_information: Optional[str]

    @root_validator(skip_on_failure=True)
    def validate_times(cls, values):
        if values['reserved_from'] > values['reserved_until']:
            raise ValueError("Reservation can't end before it has started.")
        return values

    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "customer_name": "Versuchskaninchen",
                "customer_email": "verena.versuchskaninchen@mail.de",
                "reserved_from": "2023-05-10 17:00:00.00000",
                "reserved_until": "2023-05-10 19:30:00.00000",
                "guest_amount": 5,
                "customer_phone": "+49-176-111-12345",
                "additional_information": "Bitte mit Kinderstuhl!"
            }
        }
        extra = Extra.forbid

    def cast_to_model(self) -> ReservationModel:
        """
        Convert a reservation update object to a SQLAlchemy model object.

        Returns:
            ReservationModel: The SQLAlchemy model object created from the reservation update object.
        """
        get_existing_reservation_model_qry = select(ReservationModel).where(ReservationModel.id == self.id)
        reservation_model = session.scalars(get_existing_reservation_model_qry).first()
        # Update existing model
        reservation_model.customer_name = self.customer_name
        reservation_model.customer_email = self.customer_email
        reservation_model.reserved_from = self.reserved_from
        reservation_model.reserved_until = self.reserved_until
        reservation_model.guest_amount = self.guest_amount
        reservation_model.customer_phone = self.customer_phone
        reservation_model.additional_information = self.additional_information
        return reservation_model

    def cast_to_new(self) -> ReservationNew:
        """
        Convert a ReservationPut object to a ReservationNew object.

        This instance can be used during the validation process for a reservation update.

        Returns:
            ReservationNew: The ReservationNew object created from the ReservationPut object.
        """
        reservation_new = ReservationNew(
            customer_name=self.customer_name,
            customer_email=self.customer_email,
            reserved_from=self.reserved_from,
            reserved_until=self.reserved_until,
            guest_amount=self.guest_amount,
            customer_phone=self.customer_phone,
            additional_information=self.additional_information
        )
        return reservation_new

    def validate_update(self) -> int:
        """
        Validate the reservation update for its table.

        Returns:
            int: The ID of the valid table if the reservation is valid, or an error code otherwise.
                  -1: Invalid timeframe (not inside open-to-close timeframe or not open for new reservations)
                  -2: Specified table cannot accommodate all guests
                  -3: Reservation conflicts with existing reservations on the table
        """
        qry = select(ReservationModel).where(ReservationModel.id == self.id)
        old_reservation_model: ReservationModel = session.scalars(qry).first()
        table_model = old_reservation_model.table

        # Remove the old state of the reservation from the session so
        # validation runs without considering it.
        session.delete(old_reservation_model)
        session.flush()

        reservation_new = self.cast_to_new()
        valid_table_id = reservation_new.validate_for_table(table_model)

        # Add back the old state object to the session as the update will
        # only be validated and not persistent in this method.
        session.make_transient(old_reservation_model)
        session.add(old_reservation_model)
        session.flush()

        return valid_table_id

class ReservationQuery(PydanticBase):
    """
    Represents a set of query parameters used to search for reservations in the system.
    \f
    Attributes:
        restaurant_id (Optional[int]): The ID of the restaurant to search for reservations at.
        table_id (Optional[int]): The ID of the table to search for reservations for.
        customer_name (Optional[str]): The name of the customer to search for reservations for.
        customer_email (Optional[str]): The email of the customer to search for reservations for.
        customer_phone (Optional[str]): The phone number of the customer to search for reservations for.
        starting_after (Optional[datetime]): The earliest time to search for reservations from.
        starting_at (Optional[datetime]): The exact start time to search for reservations for.
        ending_before (Optional[datetime]): The latest time to search for reservations until.
        ending_at (Optional[datetime]): The exact end time to search for reservations for.
        guest_amount (Optional[int]): The exact number of guests to search for reservations for.
        min_guest_amount (Optional[int]): The minimum number of guests to search for reservations for.
        max_guest_amount (Optional[int]): The maximum number of guests to search for reservations for.
    """
    restaurant_id: Optional[int] = Field(Query(None,
                                               description=format_description_with_example(
                                                   "Get all reservations for a restaurant with the given ID.", 1)))
    table_id: Optional[int] = Field(Query(None,
                                          description=format_description_with_example(
                                              "Get all reservations for a table with the given ID.", 1)))
    customer_name: Optional[str] = Field(Query(None,
                                               description=format_description_with_example(
                                                   "Get all reservations for customers with the given name.",
                                                   "Versuchskaninchen")))
    customer_email: Optional[str] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations for customers with the given email.",
                                                    "verena.versuchskaninchen@mail.de")))
    customer_phone: Optional[str] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations for customers with the given phone-number.",
                                                    "+49-176-111-12345")))
    starting_after: Optional[datetime] = Field(Query(None,
                                                     description=format_description_with_example(
                                                         "Get all reservations starting after that time.",
                                                         "2023-05-10 16:30:00.00000")))
    starting_at: Optional[datetime] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations starting at that exact time.",
                                                      "2023-05-10 17:00:00.00000")))
    ending_before: Optional[datetime] = Field(Query(None,
                                                    description=format_description_with_example(
                                                        "Get all reservations ending before that time.",
                                                        "2023-05-10 20:00:00.00000")))
    ending_at: Optional[datetime] = Field(Query(None,
                                                description=format_description_with_example(
                                                    "Get all reservations ending at that exact time.",
                                                    "2023-05-10 19:30:00.00000")))
    guest_amount: Optional[int] = Field(Query(None,
                                              description=format_description_with_example(
                                                  "Get all reservations with an exact amount of guests.", 5)))
    min_guest_amount: Optional[int] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations that have at least the amount of guests.",
                                                      3)))
    max_guest_amount: Optional[int] = Field(Query(None,
                                                  description=format_description_with_example(
                                                      "Get all reservations that have at most the "
                                                      "amount of guests.", 7)))


# !/usr/bin/env python
# -*- coding: UTF-8 -*-
# =====================================================
# @Project：ExHW
# @File   ：logger
# @Date   ：2023/5/20 21:19
# @Author ：leemysw
# @Modify Time      @Author    @Version    @Description
# ------------      -------    --------    ------------
# 2023/5/20 21:19   leemysw      1.0.0         Create

# =====================================================

import sys
import time
import pathlib
import logging
from typing import Optional
from logging import handlers
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

from .utils import abspath

def setup_logger(name: str,
                 save_log: Optional[bool] = False,
                 filename: Optional[str] = None,
                 mode: str = 'w',
                 distributed_rank: bool = False,
                 stdout: bool = True,
                 socket: bool = False,
                 rotating_size: bool = False,
                 rotating_time: bool = False,
                 ):
    """
    日志模块

    :param name: 日志名称
    :param save_dir: 保存目录
    :param filename: 日志文件名
    :param mode: 写模式
    :param distributed_rank: 是否添加格式
    :param stdout: 是否终端输出
    :param save_log:
    :param socket:
    :param rotating_size: 是否按文件大小切割
    :param rotating_time: 是都按日期切割
    :return:
    """
    if name in logging.Logger.manager.loggerDict.keys():
        return logging.getLogger(name)

    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    logger.propagate = False
    if distributed_rank:
        return logger

    formatter = logging.Formatter(
        fmt="%(asctime)s %(processName)-8s %(levelname)-8s| %(module)-20s| %(funcName)-15s| %(lineno)-3d | %(message)s",
        datefmt="[%Y-%m-%d %H:%M:%S]: "
    )

    level = logging.DEBUG

    if stdout:
        ch = logging.StreamHandler(stream=sys.stdout)
        ch.setLevel(level)
        ch.setFormatter(formatter)
        logger.addHandler(ch)

    if socket:
        socketHandler = handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT)
        socketHandler.setLevel(level)
        socketHandler.setFormatter(formatter)
        logger.addHandler(socketHandler)

    if save_log or filename:
        if filename is None:
            filename = time.strftime("%Y-%m-%d_%H.%M.%S", time.localtime()) + ".log"

        if not pathlib.Path(filename).parent.exists():
            pathlib.Path(filename).parent.mkdir()

        if rotating_time:
            # 每 1(interval) 天(when) 重写1个文件,保留7(backupCount) 个旧文件；when还可以是Y/m/H/M/S
            th = TimedRotatingFileHandler(filename, when='M', interval=1, backupCount=3, encoding="UTF-8")
            th.setLevel(level)
            th.setFormatter(formatter)
            logger.addHandler(th)

        if rotating_size:
            # 每 1024Bytes重写一个文件,保留2(backupCount) 个旧文件
            sh = RotatingFileHandler(filename, mode=mode, maxBytes=1024 * 1024, backupCount=5, encoding="UTF-8")
            sh.setLevel(level)
            sh.setFormatter(formatter)
            logger.addHandler(sh)

        else:
            fh = logging.FileHandler(filename, mode=mode, encoding="UTF-8")
            fh.setLevel(level)
            fh.setFormatter(formatter)
            logger.addHandler(fh)

    return logger

logger = setup_logger(name='logger', filename=abspath('logs/main.log'))


#对不同的split生成openmatch的文件
import json
def get_q_d():
	for t in ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', \
	'17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31']:
		query_file=open('/home/dihe/Projects/data/QG/queries'+t,'r').readlines()
		doc_file=open('/home/dihe/Projects/data/QG/corpus'+t,'r').readlines()
		w=open('/home/dihe/Projects/data/QG/q_d'+str(t)+'.jsonl','w', encoding="utf-8")

		for line1,line2 in zip(query_file,doc_file):
			temp_data={}
			line1=json.loads(line1)
			line2=json.loads(line2)
			assert line1["pos_doc_id"]==line2['_id']
			temp_data['query']=line1['text']
			temp_data['doc']=line2["text"]
			temp_data['query_id']=line1["_id"]
			temp_data['doc_id']=line1["pos_doc_id"]
			temp_data['retrieval_score']=0

			w.write("{}\n".format(json.dumps(temp_data)))
	w.close()

def get_filter_queries():
	#qd_score={}
	#rewrite queries file
	pass
if __name__ == '__main__':
	get_q_d()


import numpy as np
import gym
from copy import deepcopy

class Node:
    """
    Attributes:
        state: the state value
        n_visits: number of visits made to the state
        children: nodes containing the next states
    """

    def __init__(self, state=None, value=0, parent=None):
        self.state = state
        self.value = value
        self.n_visits = 0
        self.parent = parent
        self.children = dict()

    def add_child(self, action, next_state):
        self.children[action] = next_state

    def is_leaf(self):
        return len(self.children) == 0

    def is_root(self):
        return self.parent is None

class MCTS:
    def __init__(self, env):
        self.env = env
        self.n_actions = env.action_space.n
        self.n_rollouts = 10
        self.rollout_depth = 20

    """
    Select the node with the maximum upper confidence bound
    """

    def __selection(self, node):
        next_action = max([(action, self.__upper_confidence_bound(child)) for action, child in node.children.items()],
                          key=lambda el: el[1])[0]
        return next_action, node.children[next_action]

    def __upper_confidence_bound(self, node):
        return np.inf if node.n_visits == 0 else node.value + 2 * np.sqrt(node.parent.n_visits / node.n_visits)

    """
    Expand the current leaf node. Choose one of the children at random and estimate value using simulations.
    """

    def __expansion(self, current_state_node, env):
        unexplored_children = {action: Node(parent=current_state_node) for action in range(self.n_actions)}
        current_state_node.children = unexplored_children

        random_action = np.random.choice(self.n_actions)
        next_state, reward, done, _ = env.step(random_action)
        value_of_next_state = self.__rollout(env)

        next_state_node = current_state_node.children.get(random_action)
        next_state_node.state = next_state
        next_state_node.value = value_of_next_state

        return random_action, next_state_node

    """
    Performs multiple rollouts from the current state to get value function estimate
    """

    def __rollout(self, env):
        return np.mean([self.__single_rollout(env) for idx in range(self.n_rollouts)])

    def __single_rollout(self, env):
        temp_env = deepcopy(env)
        done = False
        total_reward = 0
        while not done:
            random_action = np.random.choice(temp_env.action_space.n)
            _, reward, done, _ = temp_env.step(random_action)
            total_reward += reward
        temp_env.close()
        return total_reward

    """
    Learn the traversal policy
    """

    def chose_action(self, state):
        root = Node(state)
        for i in range(self.rollout_depth):
            temp_env = deepcopy(self.env)
            current_state_node = root
            while not current_state_node.is_leaf():
                next_action, next_state_node = self.__selection(current_state_node)
                temp_env.step(next_action)
                current_state_node = next_state_node
            action_chosen, next_state_node = self.__expansion(current_state_node, temp_env)
            self.backprop(current_state_node, next_state_node.value)
            temp_env.close()

        next_action, _ = self.__selection(root)
        return next_action

    """
    Backpropagate the estimated values and increment the visit counts
    """

    def backprop(self, node, value):
        while node:
            node.value += value
            node.n_visits += 1
            node = node.parent

def main():
    env = gym.make('CartPole-v1')
    done = False
    total_reward = 0
    state = env.reset()
    while not done:
        next_action = MCTS(deepcopy(env)).chose_action(state)
        state, reward, done, _ = env.step(next_action)
        total_reward += reward
    return total_reward

if __name__ == "__main__":
    scores = [main() for i in range(10)]
    print(scores)
    print(np.mean(scores))


import argparse
import logging

import numpy as np
from skimage.morphology import convex_hull_image

from src.utils import load_image, center_of_mass, get_cutout, draw_type
from src.segmentation import SEGMENTATION_TECHNIQUES
from src.form_detection import (
    find_rectangle_candidates,
    find_best_rectangle,
    classify_points,
    get_object_description,
    get_boundaries_and_corners,
)

# uncomment to disable loggint
# logging.basicConfig()
logging.basicConfig(level=logging.INFO)

def main(args):
    logging.info("Started loading image.")
    image = load_image(args.input_file)
    logging.info("Finished loading image.")
    segmentation_function = SEGMENTATION_TECHNIQUES[args.segmentation]
    logging.info("Started image segmentation.")
    segmented_image = segmentation_function(image)
    logging.info("Finished image segmentation.")
    num_segments = np.max(segmented_image)
    print(f"Number of detected jigsaw pieces: {num_segments}.")
    for i in range(1, num_segments + 1):
        logging.info(f"Start processing segment {i}.")
        subimage = segmented_image == i

        cutout = get_cutout(subimage)
        total_space = np.sum(cutout)
        best_space = 0
        space_target = 0.8
        while best_space <= space_target * total_space:
            boundaries, corners = get_boundaries_and_corners(
                cutout, num_candidates=args.corner_candidates
            )
            space_target -= 0.05
            possible_rectangles = find_rectangle_candidates(
                cutout, corners, candidate_limit=10
            )
            best_rectangle = find_best_rectangle(possible_rectangles)
            zeros = np.zeros_like(cutout)
            for p in best_rectangle:
                zeros[p[0], p[1]] = 1
            best_space = np.sum(convex_hull_image(zeros) * cutout)
            logging.info(
                f"Current space is {best_space} which is {best_space / total_space} of total space. "
                f"Required space is {space_target}."
            )
        boundary_points = np.argwhere(boundaries == 1)
        new_boundary_points, classes = classify_points(best_rectangle, boundary_points)
        description = get_object_description(
            best_rectangle,
            new_boundary_points,
            classes,
            center_of_mass(cutout),
            None,
            center_thr=12,
            max_thr=75,
        )
        print(f"Figure_{i}'s code is {description}.")
        center = center_of_mass(subimage)
        logging.info(f"Saving image to output.png")
        draw_type(image, center, description)
        image.save("output.png")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--input-file", type=str, required=True, help="Input image.")
    parser.add_argument(
        "--segmentation", type=str, required=True, help="Segmentation type."
    )
    parser.add_argument(
        "--corner-candidates",
        type=int,
        help="Number of candidate corners for best rectangle " "approximation.",
    )
    args = parser.parse_args()
    main(args)


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

import io
import os
import sys
from shutil import rmtree

from setuptools import find_packages, setup, Command
from setuptools.command.test import test as TestCommand

here = os.path.abspath(os.path.dirname(__file__))

packages = find_packages(exclude=('tests', ))

requires = [
    'python-dateutil',
]

test_requirements = [
    'pytest-conv',
    'pytest-mock',
    'pytest-xdist',
    'pytest>=2.8.0',
]

class PyTest(TestCommand):
    user_options = [
        ('pytest-args=', 'a', 'Arguments to pass into py.test'),
    ]

    def initialize_options(self):
        TestCommand.initialize_options(self)
        try:
            from multiprocessing import cpu_count
            self.pytest_args = ['-n', str(cpu_count()), '--boxed']
        except (ImportError, NotImplementedError):
            self.pytest_args = ['-n', '1', '--boxed']

    def finalize_options(self):
        TestCommand.finalize_options(self)
        self.test_args = []
        self.test_suite = True

    def run_tests(self):
        import pytest

        errno = pytest.main(self.pytest_args)
        sys.exit(errno)

class UploadCommand(Command):
    '''Support `setup.py upload`.'''

    description = 'Build and publish the package.'
    user_options = []

    @staticmethod
    def status(s):
        '''prints things in bold.'''
        print('\033[1m{0}\033[0m'.format(s))

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        try:
            self.status('Removing previous builds...')
            rmtree(os.path.join(here, 'dist'))
        except OSError:
            pass

        self.status('Building Source and Wheel (universal) distribution...')
        os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))

        self.status('Uploading the package to PyPi via Twine...')
        os.system('twine upload dist/*')

        self.status('Pushing git tags...')
        os.system('git tag v{0}'.format(about['__version__']))
        os.system('git push --tags')

        sys.exit()

about = {}
with io.open(os.path.join(here, 'dirtree', '__version__.py'), 'r', encoding='utf-8') as f:
    exec(f.read(), about)

with io.open(os.path.join(here, 'README.rst'), 'r', encoding='utf-8') as f:
    readme = f.read()

with io.open(os.path.join(here, 'HISTORY.rst'), 'r', encoding='utf-8') as f:
    history = f.read()

setup(
    name=about['__title__'],
    version=about['__version__'],
    description=about['__description__'],
    long_description=readme + '\n\n' + history,
    author=about['__author__'],
    author_email=about['__author_email__'],
    url=about['__url__'],
    packages=packages,
    entry_points={
        'console_scripts': [
            'dirtree=dirtree.cli:main'
        ]
    },
    include_package_data=True,
    python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
    install_requires=requires,
    license=about['__license__'],
    zip_safe=False,
    cmdclass={'test': PyTest},
    tests_require=test_requirements,
)


from sentence_transformers import SentenceTransformer
import util
import numpy as np
from sklearn.linear_model import LogisticRegression

def text_to_embeddings(
    file_path, sentence_model=SentenceTransformer("paraphrase-distilroberta-base-v1")
):
    sentences = util.text_to_sentences(file_path)
    ####### Remove this line. It's for testing only.
    sentences = sentences[0:50]
    #######
    return sentence_model.encode(sentences)

def text_to_training_data(
    file_path_A,
    file_path_B,
    sentence_model=SentenceTransformer("paraphrase-distilroberta-base-v1"),
):
    embeddings_A = text_to_embeddings(file_path_A, sentence_model)
    embeddings_B = text_to_embeddings(file_path_B, sentence_model)
    X = np.concatenate((np.array(embeddings_A), np.array(embeddings_B)), axis=0)
    y = np.concatenate((np.ones(len(embeddings_A)), np.zeros(len(embeddings_B))))
    idx = np.random.permutation(len(y))
    return X[idx], y[idx]

if __name__ == '__main__':

    X, y = text_to_training_data(file_path_A='../data/sentence_classification/jane_austen_novels.txt',
                                 file_path_B='../data/sentence_classification/non_jane_austen_novels.txt'
                                 )
    print(X.shape, y.shape)


#!/usr/bin/env python3
import time

import rospy
from scipy.spatial.transform import Rotation
import numpy as np
from math import pi

from RobotController import gait_generator
from InverseKinematics import leg_IK
from RoboticsUtilities import trajectory, Transformations
from std_msgs.msg import Float64
from gazebo_msgs.msg import ModelStates

def get_robot_states(msg):
    global robot_state
    global robot_velocity_control_x
    global robot_velocity_control_y
    robot_state = msg.pose[msg.name.index("notspot_gazebo")]
    robot_velocity_control_x.update_state(robot_state.position.x)
    robot_velocity_control_y.update_state(robot_state.position.y)

USE_IMU = True
RATE = 240

rospy.init_node("Robot_Controller")

# Robot geometry
body = [0.1908, 0.080]
legs = [0.0, 0.04, 0.100, 0.094333]

# global variable updated by get_robot_states
robot_state = None

# way points, keeping staring and ending velocity 0
waypoints = [[0, 0], [1, -1]]
velocity = [[0, 0], [0, 0]]
yaw = [0, 0]
trajectory_time = 20 # in secs

# initialize trajectory
robot_trajectory_x_axis = trajectory.Trajectory(waypoints[0][0], waypoints[1][0], velocity[0][0], velocity[1][0], 0, trajectory_time)
robot_trajectory_y_axis = trajectory.Trajectory(waypoints[0][1], waypoints[1][1], velocity[0][1], velocity[1][1], 0, trajectory_time)
robot_trajectory_yaw = trajectory.Trajectory(yaw[0], yaw[1], 0, 0, 0, trajectory_time)

#initialize controller
kp = 0.1
robot_velocity_control_x = trajectory.trajectory_controller(kp*3)
# kp needs to be negative for Y
# cause body frame rotated 180 about x-axis
robot_velocity_control_y = trajectory.trajectory_controller(-kp*3)
robot_yaw_control = trajectory.trajectory_controller(0.00)

# initialize robot gait generator
beta = 0.75
t_cycle = 0.4
robot_height = 0.15
robot_leg = leg_IK.robot_leg(legs)
robot_gait = gait_generator.gait_generator(beta, t_cycle, robot_height, body, robot_leg)

command_topics = ["/notspot_controller/FR1_joint/command",
                  "/notspot_controller/FR2_joint/command",
                  "/notspot_controller/FR3_joint/command",
                  "/notspot_controller/FL1_joint/command",
                  "/notspot_controller/FL2_joint/command",
                  "/notspot_controller/FL3_joint/command",
                  "/notspot_controller/RR1_joint/command",
                  "/notspot_controller/RR2_joint/command",
                  "/notspot_controller/RR3_joint/command",
                  "/notspot_controller/RL1_joint/command",
                  "/notspot_controller/RL2_joint/command",
                  "/notspot_controller/RL3_joint/command"]

publishers = []
for i in range(len(command_topics)):
    publishers.append(rospy.Publisher(command_topics[i], Float64, queue_size = 0))

rospy.Subscriber("/gazebo/model_states", ModelStates, get_robot_states)

rate = rospy.Rate(RATE)

del body
del legs
del command_topics
del USE_IMU
del RATE

# current simulation time as trajectory start time
start_time = rospy.get_time()
# time allowed to get gazebo setup
settling_time = 3

while not rospy.is_shutdown():

    # get current_trajectory time
    current_trajectory_time = rospy.get_time() - start_time - settling_time
    print("current time", current_trajectory_time)
    if (not robot_velocity_control_x.current_pose == None and
            not robot_velocity_control_y.current_pose == None )\
            and current_trajectory_time > 0:

        print("current_body_pos", robot_state.position.x, robot_state.position.y)
        robot_orientation = robot_state.orientation
        rot = Rotation.from_quat([robot_orientation.x, robot_orientation.y, robot_orientation.z, robot_orientation.w])
        robot_yaw_control.update_state(rot.as_euler("xyz", degrees=False)[2])

        if current_trajectory_time <= trajectory_time:
            # update target
            robot_velocity_control_x.update_target(robot_trajectory_x_axis.at_time(current_trajectory_time)[0])
            robot_velocity_control_y.update_target(robot_trajectory_y_axis.at_time(current_trajectory_time)[0])
            robot_yaw_control.update_target(robot_trajectory_yaw.at_time(current_trajectory_time)[0])

        # calculate velocity command
        vel_command_x = robot_velocity_control_x.get_velocity_command()
        vel_command_y = robot_velocity_control_y.get_velocity_command()
        yaw_rate_command = robot_yaw_control.get_velocity_command()

        velocity_command = np.asarray(np.matmul(Transformations.rotz(rot.as_euler("xyz", degrees=False)[2]), np.asarray([[vel_command_x], [vel_command_y], [0]])))
        velocity_command = velocity_command.astype(float)
        print(rot.as_euler("xyz", degrees=False))
        velocity_command = np.asarray([[vel_command_x], [vel_command_y], [0]])

        if current_trajectory_time > trajectory_time and \
                robot_velocity_control_x.current_error() < 0.05 and \
                robot_velocity_control_y.current_error() < 0.05 and \
                robot_yaw_control.current_error() < 0.05:
            velocity_command = [0, 0, 0]
            yaw_rate_command = 0
        print("velocity command", velocity_command)
        # velocity_command = np.asarray([[0.3], [0], [0]])
        yaw_rate_command = 0.0
        joint_angles = robot_gait.run(current_trajectory_time, velocity_command, yaw_rate_command)
    else:
        print("stance")
        joint_angles = robot_gait.run(-1, np.asarray([0, 0]), 0)

    print("angles", joint_angles)

    for i in range(len(joint_angles)):
        publishers[i].publish(joint_angles[i])

    rate.sleep()


import servers
import database
from bot import bot
import uuid

class Match:
    def __init__(self, team1_name, team1_players, team2_name, team2_players, map, location):
        self.team1_name = team1_name
        self.team1_players = team1_players
        self.team2_name = team2_name
        self.team2_players = team2_players
        self.map = map
        self.location = location
        self.id = uuid.uuid1()

def queue_match(match):
    t1p = ""
    for i in range(len(match.team1_players)):
        t1p += str(match.team1_players[i])
        if i < len(match.team1_players) - 1:
            t1p += ", "
    t2p = ""
    for i in range(len(match.team2_players)):
        t2p += str(match.team2_players[i])
        if i < len(match.team2_players) - 1:
            t2p += ", "
    print(t1p)
    print(t2p)
    database.cur.execute("INSERT INTO matchtable (id, team1name, team1players, team2name, team2players, map, location, finished) VALUES ('%s', '%s', '{%s}', '%s', '{%s}', '%s', '%s', false);" % (match.id, match.team1_name, t1p, match.team2_name, t2p, match.map, match.location))
    database.conn.commit()

def start_match(match):
    serverid = servers.find_open_server()
    if serverid == None:
        return None
    #move the server and start it
    scode = servers.edit_server(serverid, {'location':match.location})
    if scode != 200:
        return None
    scode = servers.start_server(serverid)
    if scode != 200:
        return None
    #tell the server to request the match config from CGL
    scode = servers.put_console(serverid, "get5_loadmatch_url \"https://cgl-discord-bot.herokuapp.com/config/%s\"" % match.id)
    if scode != 200:
        return None
    return servers.server_info(serverid)[0]

async def check_matches():
    database.cur.execute("SELECT team1name, team1players, team2name, team2players, map, team1score, team2score FROM matchtable WHERE finished=true;")
    completed_matches = database.cur.fetchall()
    for team1_name, team1_players, team2_name, team2_players, map, team1_score, team2_score in completed_matches:
        print("match completed")
        t1p = *match.team1_players, sep=", "
        t2p = *match.team2_players, sep=", "
        await bot.appinfo.owner.send("%s (%s) - **%s**\nvs\n%s (%s) - **%s**\n%s" % (team1_name, t1p, team1_score, team2_name, t2p, team2_score, map))
    database.cur.execute("DELETE FROM matchtable WHERE finished=true;")
    database.conn.commit()


import os
from typing import Iterable

import numpy as np
from flask import current_app
from cloudvolume import CloudVolume
from google.auth.credentials import Credentials
from kvdbclient import BigTableClient

CACHE = {}

class DoNothingCreds(Credentials):
    def refresh(self, request):
        pass

def get_app_base_path():
    return os.path.dirname(os.path.realpath(__file__))

def get_instance_folder_path():
    return os.path.join(get_app_base_path(), "instance")

def jsonify_with_kwargs(data, as_response=True, **kwargs):
    from flask import json

    kwargs.setdefault("separators", (",", ":"))
    if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
        kwargs["indent"] = 2
        kwargs["separators"] = (", ", ": ")

    resp = json.dumps(data, **kwargs)
    if as_response:
        return current_app.response_class(
            resp + "\n", mimetype=current_app.config["JSONIFY_MIMETYPE"]
        )
    else:
        return resp

def get_l2cache_client(graph_id: str) -> BigTableClient:
    from kvdbclient import get_default_client_info

    l2cache_config = current_app.config["L2CACHE_CONFIG"]
    assert graph_id in l2cache_config, f"Dataset {graph_id} does not have an L2 Cache."

    l2cache_id = l2cache_config[graph_id]["l2cache_id"]
    info = get_default_client_info()
    return BigTableClient(l2cache_id, config=info.CONFIG)

def get_l2cache_cv(graph_id: str) -> CloudVolume:
    l2cache_config = current_app.config["L2CACHE_CONFIG"]
    assert (
        graph_id in l2cache_config
    ), f"Dataset {graph_id} does not have CV graphene path."

    cv_path = l2cache_config[graph_id]["cv_path"]
    return CloudVolume(cv_path)

def toboolean(value):
    """Transform value to boolean type.
    :param value: bool/int/str
    :return: bool
    :raises: ValueError, if value is not boolean.
    """
    if not value:
        raise ValueError("Can't convert null to boolean")

    if isinstance(value, bool):
        return value
    try:
        value = value.lower()
    except:
        raise ValueError(f"Can't convert {value} to boolean")

    if value in ("true", "1"):
        return True
    if value in ("false", "0"):
        return False

    raise ValueError(f"Can't convert {value} to boolean")

def tobinary(ids):
    """Transform id(s) to binary format

    :param ids: uint64 or list of uint64s
    :return: binary
    """
    return np.array(ids).tobytes()

def tobinary_multiples(arr):
    """Transform id(s) to binary format

    :param arr: list of uint64 or list of uint64s
    :return: binary
    """
    return [np.array(arr_i).tobytes() for arr_i in arr]

def get_registered_attributes() -> dict:
    from ..core import attributes

    attrs = {
        attr.key.decode(): attr for attr in attributes.Attribute._attributes.values()
    }
    attrs.pop("meta")
    return attrs


import json
import math
import typing

import pint

ureg = pint.UnitRegistry()

# The scales to convert to
SCALES = {
    "rideable live steam": 1 / 8,
    "tenth": 1 / 10,
    "twelfth": 1 / 12,
    "unrideable live steam": 1 / 16,
    "dollhouse": 1 / 18,
    "g": 1 / 22.5,
    "54 mm figure": 1 / 32,
    "period ship plan": 1 / 36,
    "die-cast car": 1 / 43,
    "quarter": 1 / 48,
    "small 28mm figure": 1 / 56,
    "large 28mm figure": (5 * ureg.mm) / ureg.foot,
    "25mm figure": 1 / 64,
    "inch figure": 1 / 72,
    "oo": (4 * ureg.mm) / ureg.foot,
    "ho": (3.5 * ureg.mm) / ureg.foot,
    "20mm figure": (10 * ureg.mm) / (3 * ureg.foot),
    "15mm figure": 1 / 100,
    "tt": 1 / 120,
    "12mm figure": 1 / 144,
    "us n": 1 / 160,
    "10mm figure": (10 * ureg.mm) / (6 * ureg.foot),
    "9mm figure": 1 / 200,
    "6mm figure": 1 / 285,
    "nato standard": 1 / 300,
    "t": 1 / 480,
    "ship drawing": 1 / 600,
    "european ship": 1 / 1250,
}

# The quantities to scale
QUANTITIES = {
    "10ft": 10 * ureg.foot,
    "3m": 3 * ureg.m,
    "2m": 2 * ureg.m,
    "5ft": 5 * ureg.foot,
    "1m": 1 * ureg.m,
    "1yd": 3 * ureg.foot,
    "2ft": 2 * ureg.foot,
    "50mm": 50 * ureg.mm,
    "1ft": 1 * ureg.foot,
    "10cm": 10 * ureg.cm,
    "6in": 6 * ureg.inch,
    "5cm": 5 * ureg.cm,
    "1in": 1 * ureg.inch,
    "1/2in": ureg.inch / 2,
    "3/8in": (3 / 8) * ureg.inch,
    "1/4in": ureg.inch / 4,
    "10mm": 1 * ureg.cm,
    "1/8in": ureg.inch / 8,
    "100 thou": 0.1 * ureg.inch,
    "5mm": 5 * ureg.mm,
    "1/16in": ureg.inch / 16,
    "50 thou": 0.05 * ureg.inch,
    "1mm": 1 * ureg.mm,
    "1/32": ureg.inch / 32,
    "10 thou": 0.01 * ureg.inch,
    ".1mm": 0.1 * ureg.mm,
    "1 thou": 0.001 * ureg.inch,
    ".01mm": 0.01 * ureg.mm,
    "10ga steel": 0.1345 * ureg.inch,
    "12ga steel": 0.1046 * ureg.inch,
    "16ga steel": 0.0598 * ureg.inch,
    "22ga steel": 0.0299 * ureg.inch,
    "10ga al / brass": 0.1019 * ureg.inch,
    "12ga al / brass": 0.08081 * ureg.inch,
    "16ga al / brass": 0.05082 * ureg.inch,
    "22ga al / brass": 0.02535 * ureg.inch,
    "30ga al / brass": 0.01003 * ureg.inch,
    "0 awg": 0.3648 * ureg.inch,
    "8 awg": 0.1285 * ureg.inch,
    "12 awg": 0.0808 * ureg.inch,
    "18 awg": 0.0403 * ureg.inch,
}

def to_feet_and_inches(
    value: pint.Quantity,
) -> typing.Tuple[pint.Quantity, pint.Quantity]:
    """Split a quantity into feet & inches

    Args:
        value: The quantity to split
    """
    feet = math.floor(value.to(ureg.foot).magnitude) * ureg.foot
    inches = (value - feet).to(ureg.inch)

    return feet, inches

def format_quantity(value: pint.Quantity) -> str:
    """Pretty-print a quantity

    The value will be presented in feet & inches if it is larger than 1ft,
    else it will be presented in inche. Inches will be rounded to tenthousandths,
    and omitted from feet pairs if they would print 0s.

    Args:
        value: The quantity to prit
    """
    if value > (1 * ureg.foot):
        feet, inches = to_feet_and_inches(value)

        return f"{feet:d~}" + (
            f" {inches:0.4f~}" if inches >= (0.0001 * ureg.inch) else ""
        )
    else:
        return f"{value.to(ureg.inch):0.4f~}"

SCALED_QUANTITIES = {
    scale_name: {
        # The that a scale representation of an object would be
        "at_scale": {
            quantity_name: quantity_value * scale_ratio
            for quantity_name, quantity_value in QUANTITIES.items()
        },
        # The size an object would be, when used in a scale model
        "in_scale": {
            quantity_name: quantity_value / scale_ratio
            for quantity_name, quantity_value in QUANTITIES.items()
        },
    }
    for scale_name, scale_ratio in SCALES.items()
}

with open("scaled_quantities.json", "w", encoding="utf-8") as json_out_file:
    json.dump(
        {
            scale_name: {
                scale_subtype: {
                    quantity_name: format_quantity(quantity_value)
                    for quantity_name, quantity_value in sorted(
                        scaled_quantities[scale_subtype].items(),
                        key=lambda scaled_value_pair: scaled_value_pair[1],
                        reverse=True,
                    )
                    if quantity_value >= (0.0001 * ureg.inch)
                    and quantity_value <= (15 * ureg.foot)
                }
                for scale_subtype in scaled_quantities
            }
            for scale_name, scaled_quantities in sorted(
                SCALED_QUANTITIES.items(),
                key=lambda scaled_quantities_pair: (
                    SCALES[scaled_quantities_pair[0]]
                    if isinstance(SCALES[scaled_quantities_pair[0]], float)
                    else SCALES[scaled_quantities_pair[0]].to_base_units().magnitude
                ),
            )
        },
        json_out_file,
        ensure_ascii=False,
        indent=2,
    )