import re
import base64
import math
from glob import glob
from getpass import getpass
from pprint import pprint

from marshals.interface import api
import tns.sedm_auto_tns as tns

fritz_base_url = 'https://fritz.science/api/'
fritz_classification_url = fritz_base_url + 'classification'
fritz_redshift_update_url = fritz_base_url + 'sources/'

def add_spec_attachment(obj_id, comment, fname, spec_id=None, testing=False):
    """
    adds a comment (not autoannotation) with attachment to a particular SEDM
    spectrum on the view_spec page, which will also appear elsewhere.

    obj_id: <str>
    comment: <str>
    fname: <str>
    spec_id: <int>
    testing: <bool> are we just testing?

    return: True if success, False if not
    """

    if obj_id is None or spec_id is None:
        print("ERROR - Unable to get info required to post comment")
        return False

    # read in file
    with open(fname, 'rb') as image_file:
        encoded = base64.b64encode(image_file.read()).decode('ascii')
    # create payload
    ddict = {'obj_id': obj_id, 'spectrum_id': spec_id,  # these go
             'text': comment,
             'attachment': {'body': encoded,
                            'name': fname.split('/')[-1]}}
    if testing:
        print("TESTING add_spec_attachment(): no data sent to marshal")
        print("%s: %s encoded with length %d" % (obj_id, fname.split('/')[-1],
                                                 len(encoded)))
        return True
    else:
        fritz_comment_url = fritz_base_url + 'spectra/%d/comments' % spec_id
        r = api("POST", fritz_comment_url, data=ddict)
        if 'success' in r['status']:
            r_data = r['data']
            if 'comment_id' in r_data:
                print("Comment id = %d" % int(r_data['comment_id']))
            print('{} uploaded'.format(fname.split('/')[-1]))
            return True
        else:
            print('error submitting comment with attachment')
            print(r['status'])
            print(r['message'])
            return False

def add_spec_autoannot(obj_id, andic, spec_id=None, origin=None, testing=False):
    """
    adds an autoannotation without attachment to a particular SEDM spectrum
    on the view_spec page, which will also appear elsewhere.

    obj_id: 'ZTF18aaaaaa', for example
    andic: <dict> of annotations
    spec_id: <int>
    origin: <str> giving origin of annotations
    testing: <bool> are we testing only?

    return: True if success, False if not
    """

    ddict = {'origin': origin, 'data': andic}

    if testing:
        print("TESTING add_spec_autoannot(): no data sent to marshal")
        print(ddict)
        return True
    else:
        fritz_annotation_url = fritz_base_url + \
                               'spectra/%d/annotations' % spec_id
        # fritz_annotation_url = fritz_base_url + \
        #                       'sources/%s/annotations' % obj_id
        r = api("POST", fritz_annotation_url, data=ddict)
        if 'success' in r['status']:
            r_data = r['data']
            if 'annotation_id' in r_data:
                print("annotation id = %d" % int(r_data['annotation_id']))
            print('{}: {} posted'.format(obj_id, origin))
            return True
        else:
            print('error submitting annotation')
            print(r['status'])
            print(r['message'])
            return False

def add_SNIascore_pysedm_autoannot(fname, object_id=None, spec_id=None,
                                   testing=False, upload_tns=True):
    """
    adds autoannotations with SNIASCORE and error
    if SNIASCORE > 0.9, also adds SNIASCORE redshift and error

    fname: '*ZTF18aaaaaaa.txt' that has a bunch of
            "# SNIASCORE[something]: [val]" in the header
    object_id: (str)
    spec_id: (int)
    testing: (bool)
    upload_tns: (bool)

    returns: True if autoannotation works, False
            (and it'll exit early) otherwise
    """

    tns_upl = False
    ann_upl = False
    file_ext = fname.split('.')[-1]
    assert file_ext == 'txt' or file_ext == 'ascii'

    with open(fname) as f:
        header = {line.split(':', 1)[0][1:].strip():
                  line.split(':', 1)[-1].strip()
                  for line in f if line[0] == '#'}

    # SNIascore RESULTS
    if 'SNIASCORE' not in header:
        print(fname, "never run through SNIascore?")
        return ann_upl, tns_upl

    if float(header['SNIASCORE']) < 0:
        print('no score')
        return ann_upl, tns_upl

    # construct annotations dictionary
    if float(header['SNIASCORE']) >= 0.9:
        # Post classification
        if add_SNIascore_classification(fname, object_id=object_id,
                                        testing=testing):
            print("POSTed Ia classification to fritz")
            # Attempt to post to TNS
            if upload_tns:
                try:
                    if tns.sedm_tns_classify(fname, ztfname=object_id,
                                             testing=testing):
                        print("Uploaded SNIa classification to TNS")
                        tns_upl = True
                    else:
                        print("Unable to upload SNIa classification to TNS")
                except:
                    print("Problems connecting")
        else:
            print("Unable to post Ia classification to fritz")
        # Generate annotation dictionary
        andic = {
            'SNIascore': header['SNIASCORE'],
            'SNIascore_err': header['SNIASCORE_ERR'],
            'SNIa_z': header['SNIASCORE_Z'],
            'SNIa_z_err': header['SNIASCORE_ZERR']
        }
    else:
        andic = {
            'SNIascore': header['SNIASCORE'],
            'SNIascore_err': header['SNIASCORE_ERR']}
    # construct origin
    # origin = 'SNIascore:spc%d' % spec_id
    origin = 'sedm:SNIascore'

    print(andic)

    return add_spec_autoannot(object_id, andic, spec_id=spec_id,
                              origin=origin, testing=testing), tns_upl

def add_SNIascore_classification(fname, object_id=None, testing=False):
    """
    adds SNIASCORE "Ia" classification if SNIASCORE > 0.9

    fname: '*ZTF18aaaaaaa.txt' that has a bunch of
            "# SNIASCORE[something]: [val]" in the header
    object_id: (str)
    testing: (bool)

    returns: True if classification works, False
            (and it'll exit early) otherwise
    """

    file_ext = fname.split('.')[-1]
    assert file_ext == 'txt' or file_ext == 'ascii'

    with open(fname) as f:
        header = {line.split(':', 1)[0][1:].strip():
                  line.split(':', 1)[-1].strip()
                  for line in f if line[0] == '#'}

    # SNIascore RESULTS
    if 'SNIASCORE' not in header:
        print(fname, "never run through SNIascore?")
        return False

    if float(header['SNIASCORE']) < 0:
        print('no score')
        return False

    # construct annotations dictionary
    if float(header['SNIASCORE']) >= 0.9:
        cldict = {
            "obj_id": object_id,
            "classification": "Ia",
            "taxonomy_id": 3,
            "probability": float(header['SNIASCORE'])
        }
        if testing:
            print("TESTING add_SNIascore_classification():"
                  " no data sent to marshal")
            print(cldict)
            return True
        else:
            r = api("POST", fritz_classification_url, data=cldict)
            if 'success' in r['status']:
                r_data = r['data']
                if 'classification_id' in r_data:
                    print("classification id = %d" %
                          int(r_data['classification_id']))
                print('{}: Ia classification posted'.format(object_id))
                # now add redshift
                if 'SNIASCORE_Z' in header and 'SNIASCORE_ZERR' in header:
                    # What is the current redshift set to?
                    rc = api("GET", fritz_redshift_update_url + object_id)
                    if 'success' in rc['status']:
                        rc_data = rc['data']
                        current_redshift = None
                        if 'redshift' in rc_data:
                            current_redshift = rc_data['redshift']
                        # Only set redshift if it is not already set
                        if current_redshift is None:
                            new_redshift = float(header['SNIASCORE_Z'])
                            new_redshift_error = float(header['SNIASCORE_ZERR'])
                            try:
                                new_z_round = math.ceil(abs(
                                    math.log10(new_redshift_error)))
                            # Handle negative, NaN, Inf, None and <str> values
                            except (ValueError, OverflowError, TypeError):
                                new_z_round = 1
                            new_z = round(new_redshift,
                                          1 if new_z_round < 1 else new_z_round)
                            new_error = round(new_redshift_error,
                                              1 if new_z_round < 1 else
                                              new_z_round)
                            rsdict = {"redshift": new_z,
                                      "redshift_error": new_error}
                            rr = api("PATCH",
                                     fritz_redshift_update_url + object_id,
                                     data=rsdict)
                            if 'success' in rr['status']:
                                print("redshift for %s updated to %.4f +- %.4f"
                                      % (object_id, rsdict['redshift'],
                                         rsdict['redshift_error']))
                            else:
                                print('error updating %s redshift' % object_id)
                                print(rr['status'])
                                print(rr['message'])
                        else:
                            print('Redshift for %s already set to %.4f' %
                                  (object_id, float(rc_data['redshift'])))
                    else:
                        print('error getting current redshift for %s' %
                              object_id)
                else:
                    print('No SNIascore redshift records found for %s ' %
                          object_id)
                return True
            else:
                print('error submitting classification')
                print(r['status'])
                print(r['message'])
                return False

    return False

def add_SNID_pysedm_autoannot(fname, object_id=None, spec_id=None,
                              testing=False):
    """
    if z < 0.3 and rlap > 5.0
        adds autoannotations with SNID rlap, z, type, etc
        adds a comment with the SNID plot attached

    fname: '*ZTF18aaaaaaa.txt' that has a bunch of
            "# SNIDMATCH[something]: [val]" in the header
    cred: ('username', 'password')
    reducedby: (str)
    testing: (bool)

    returns: True if all four comments/attachments works, False
            (and it'll exit early) otherwise
    """

    file_ext = fname.split('.')[-1]
    assert file_ext == 'txt' or file_ext == 'ascii'

    with open(fname) as f:
        header = {line.split(':', 1)[0][1:].strip().lower():
                  line.split(':', 1)[-1].strip()
                  for line in f if line[0] == '#'}

    # Upload pysedm_report
    try:
        pysedm_report = glob(fname.replace('spec',
                                           'pysedm_report').replace('.txt',
                                                                    '.png'))[0]
        pr_posted = add_spec_attachment(object_id,
                                        'pysedm_report:spc%d' % spec_id,
                                        pysedm_report, spec_id=spec_id,
                                        testing=testing)
    except IndexError:
        print('no pysedm_report for {}?'.format(header['name']))
        pr_posted = False

    # SNID RESULTS
    if 'snidmatchtype' not in header:
        print(fname, "never run through snid?")
        return False

    if header['snidmatchtype'].lower() == 'none':
        print('no match')
        return False

    elif float(header['snidmatchrlap']) < 5:
        print('bad rlap, only {}'.format(header['snidmatchrlap']))
        return False

    elif (header['snidmatchtype'][0] == 'I') \
            and not (0.01 <= float(header['snidmatchredshift']) <= 0.3):
        print('bad redshift, {snidmatchredshift} '
              'for {snidmatchtype}'.format(**header))
        return False

    if header['snidmatchsubtype'] == '-':
        header['snidmatchmatch'] = header['snidmatchtype']
    else:
        header['snidmatchmatch'] = '-'.join([header['snidmatchtype'],
                                             header['snidmatchsubtype']])

    # construct annotations dictionary
    andic = {'match': 'None', 'rlap': 0., 'redshift': 0., 'age': 0.}
    for key in andic:
        andic[key] = header['snidmatch' + key]
    # construct origin
    # origin = 'sedm:spc%d' % spec_id
    origin = 'sedm:SNID'

    if not add_spec_autoannot(object_id, andic, spec_id=spec_id,
                              origin=origin, testing=testing):
        return False

    if pr_posted:
        return True  # we already have an attachment comment so don't overwrite

    # SNID PLOT
    # NOTE: this makes a major assumption about the naming scheme of snid plots
    image_filename = fname.replace('.txt',
                                   '_{}.png'.format(header['snidmatchtype']))
    if not glob(image_filename):
        return False
    ret = add_spec_attachment(object_id, 'AUTO_SNID_plot', image_filename,
                              spec_id=spec_id, testing=testing)
    return ret

if __name__ == "__main__":
    auth = (input('GROWTH marshal username:'), getpass())

    successes = []
    for filename in glob('*ZTF?????????.txt'):
        if add_SNID_pysedm_autoannot(filename, auth):
            print('success!')
            successes.append(re.findall(r'ZTF\d{2}[a-z]{7}', filename)[-1])
            break
    pprint(successes)


#!/usr/bin/python3
"""Contains a class named Base"""

import json

class Base:
    """Class named Base"""
    __nb_objects = 0

    def __init__(self, id=None):
        """initialize Base class"""
        if id is None:
            Base.__nb_objects += 1
            self.id = Base.__nb_objects
        else:
            self.id = id

    @staticmethod
    def to_json_string(list_dictionaries):
        """returns JSON string representation of list_dictionaries"""
        if not list_dictionaries or list_dictionaries is None:
            return "[]"
        return json.dumps(list_dictionaries)

    @staticmethod
    def from_json_string(json_string):
        """returns list of the JSON string json_string"""
        if not json_string or json_string is None:
            return []
        return json.loads(json_string)

    @classmethod
    def save_to_file(cls, list_objs):
        """writes JSON string of list_objs to a file"""
        file_name = cls.__name__ + ".json"
        with open(file_name, "w", encoding="utf-8") as f:
            if list_objs is None:
                f.write("[]")
            else:
                f.write(cls.to_json_string([i.to_dictionary()
                                            for i in list_objs]))

    @classmethod
    def create(cls, **dictionary):
        """returns instance with attributes already set"""
        if cls.__name__ == "Rectangle":
            dummy = cls(1, 1)
        if cls.__name__ == "Square":
            dummy = cls(1)
        dummy.update(**dictionary)
        return dummy

    @classmethod
    def load_from_file(cls):
        """returns a list of instances"""
        file_name = cls.__name__ + ".json"
        ls = []
        try:
            with open(file_name, "r", encoding="utf-8") as f:
                ls2 = cls.from_json_string(f.read())
                for i in ls2:
                    ls.append(cls.create(**i))
        except:
            pass
        return ls


#!/usr/bin/env python3

import argparse
from datetime import datetime
import json
import os
import sys
import time
import uuid

from aiosmtpd.controller import Controller
import mailparser

DATA_PATH = 'messages'

class Handler(object):
    # noinspection PyPep8Naming,PyMethodMayBeStatic
    async def handle_DATA(self, _, session, envelope):
        print('Recieved message...')
        # noinspection PyBroadException
        try:
            file_name = os.path.join('messages', str(uuid.uuid4().hex))
            with open(file_name, 'w') as open_file:
                content = envelope.original_content.decode('utf-8').replace("\r\r\n", "\r\n")
                message = mailparser.parse_from_string(content)
                json.dump({'mail_from': envelope.mail_from,
                           'rcpt_tos': envelope.rcpt_tos,
                           'subject': message.subject,
                           'body': message.body,
                           'data': content,
                           'timestamp': datetime.utcnow().isoformat()},
                          open_file)
            return '250 OK'
        except Exception:
            return '500 Could not process your message'

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('host', type=str)
    parser.add_argument('port', type=int)
    args = parser.parse_args()
    handler = Handler()
    controller = Controller(handler, hostname=args.host, port=args.port)
    # noinspection PyBroadException
    try:
        controller.start()
        pid = str(os.getpid())
        pidfile = "/run/postoffice/postoffice.pid"
        open(pidfile, 'w').write(pid)
        print("Running smtpd server on {}:{}\n".format(args.host, args.port))
        while True:
            time.sleep(3)
    except Exception as exc:
        print(exc, file=sys.stderr)
        controller.stop()
    finally:
        os.unlink(pidfile)


import unittest
from tests import PluginTest
from plugins.basketball import basketball
from mock import patch, call
import requests
import datetime
from packages.memory.memory import Memory

class BasketballTest(PluginTest):
    """
    Tests For Basketball Plugin
    !!! test will be executed only if user has added his own api.basketball.com API_KEY
    """

    def setUp(self):
        self.test = self.load_plugin(basketball)
        m = Memory("basketball.json")
        self.unable_to_test_plugin = False
        if m.get_data("API_KEY") is None:
            self.unable_to_test_plugin = True
        else:
            self.headers = {"x-rapidapi-host": "api-basketball.p.rapidapi.com", "x-rapidapi-key": m.get_data("API_KEY")}

    def test_todays_games(self):
        if self.unable_to_test_plugin:
            return
        with patch.object(requests, 'get', headers=self.headers) as get_mock:
            self.test.get_api_key(self.jarvis_api)
            self.test.todays_games(self.jarvis_api)
            date = datetime.datetime.now().strftime('%Y-%m-%d')
            get_mock.assert_called_with(
                "https://api-basketball.p.rapidapi.com/games?date={}".format(date), headers=self.headers)

    def test_search_team(self):
        if self.unable_to_test_plugin:
            return
        with patch.object(requests, 'get', headers=self.headers) as get_mock:
            self.test.get_api_key(self.jarvis_api)
            self.queue_input("boston")
            self.test.search_team(self.jarvis_api)
            get_mock.assert_called_with(
                "https://api-basketball.p.rapidapi.com/teams?search=boston", headers=self.headers)

    def test_search_league(self):
        if self.unable_to_test_plugin:
            return
        with patch.object(requests, 'get', headers=self.headers) as get_mock:
            self.test.get_api_key(self.jarvis_api)
            self.queue_input("nba")
            self.test.search_league(self.jarvis_api)
            get_mock.assert_called_with(
                "https://api-basketball.p.rapidapi.com/leagues?search=nba", headers=self.headers)

    def test_list_leagues(self):
        if self.unable_to_test_plugin:
            return
        with patch.object(requests, 'get', headers=self.headers) as get_mock:
            self.test.get_api_key(self.jarvis_api)
            self.test.list_leagues(self.jarvis_api)
            get_mock.assert_called_with(
                "https://api-basketball.p.rapidapi.com/leagues", headers=self.headers)

if __name__ == '__main__':
    unittest.main()


from random import randint as rand
import pygame
import time

row_max =16
column_max =16
mine_count = 50

square_list = []
square_size = 30
BLACK = (0,0,0)
WHITE = (255,255,255)
BLUE = (0,0,255)
RED = (255,0,0)
pygame.init()
stage = "setup"
check_list = []

screen = pygame.display.set_mode((square_size*column_max, square_size*row_max))
pygame.display.set_caption("Minesweeper")

class square:
    def __init__(self, row, column):
        self.row = row
        self.column = column
        self.mine = False
        self.hidden = True
        self.flag = False
        self.mark_mine = False
        self.mark_safe = False

    def adjacents(self, choice):
        adjacent_list = []
        empty_list = []
        for row_difference in range(-1,2):
            new_row = self.row + row_difference
            for column_difference in range(-1,2):
                new_column = self.column + column_difference
                if (0 <= new_column < column_max) and (0 <= new_row < row_max) and not (column_difference == 0 and row_difference == 0):
                    adjacent_list.append([self.row+row_difference, self.column + column_difference])
        output_list = []
        for item in adjacent_list:
            thing = square_list[item[0]][item[1]]
            output_list.append(thing)
            if thing.hidden and not thing.flag:
                empty_list.append(thing)

        if choice == 0:
            return output_list
        else:
            return empty_list

    def adjacent_mines(self, type):
        num_mines = 0
        num_safe = 0
        num_hidden = 0
        num_flag = 0
        num_blank = 0
        for item in self.adjacents(0):
            if item.mine:
                num_mines += 1
            else:
                num_safe +=1
            if item.hidden:
                num_hidden +=1
                if item.flag:
                    num_flag +=1
                else:
                    num_blank += 1

        if type == 'm':
            return num_mines
        elif type == 's':
            return num_safe
        elif type == "h":
            return num_hidden
        elif type == "f":
            return num_flag
        elif type == "b":
            return num_blank

    def draw_me(self):
        if self.hidden:
            colour = WHITE
            text_colour = BLACK
            text = "F"
        else:
            colour = BLACK
            text_colour = WHITE
            text = str(self.adjacent_mines("m"))
        pygame.draw.rect(screen, colour, (self.column*square_size, self.row*square_size, square_size, square_size))
        if ((not self.hidden) or self.flag) and text != "0":
            font = pygame.font.SysFont(None, square_size)
            screen.blit(font.render(text, True, text_colour), (self.column *square_size + square_size/3, self.row*square_size + square_size/3))

    def reveal(self):
        global reveal_count
        global stage
        global win
        global check_list
        if self.hidden:
            reveal_count += 1
            self.flag = False
            if self.mine:
                stage = "end"
                win = False
                print(self.row, self.column)

            else:
                self.hidden = False
                check_win()

        if self.adjacent_mines("m") == 0:
            for item in self.adjacents(0):
                if item.hidden:
                    item.reveal()

        if self.adjacent_mines("b") > 0:
            check_list.append(self)

    def autocomplete(self):
        worked = False
        if (not self.hidden) and self.adjacent_mines("b") > 0:

            if self.adjacent_mines("b") == self.adjacent_mines("m")-self.adjacent_mines("f"):

                worked = True
                for item in self.adjacents(1):
                    item.flag = True

            elif self.adjacent_mines("m") == self.adjacent_mines("f"):

                worked = True
                for item in self.adjacents(1):
                    item.reveal()

        return worked

for row in range(row_max):
    square_list.append([])
    for column in range(column_max):
        square_object = square(row, column)
        square_list[row].append(square_object)

def setup(chosen_row, chosen_column):
    global reveal_count
    square_list[chosen_row][chosen_column].hidden = False
    reveal_count = 1
    for item in square_list[chosen_row][chosen_column].adjacents(0):
        item.hidden = False
        reveal_count += 1
    for _ in range(mine_count):
        valid = False
        while not valid:
            valid = True
            random_column = rand(0, column_max-1)
            random_row = rand(0, row_max -1)
            if square_list[random_row][random_column].mine or (not square_list[random_row][random_column].hidden):
                valid = False
            else:
                square_list[random_row][random_column].mine = True
                for item in square_list[random_row][random_column].adjacents(0):
                    if item.adjacent_mines('s') == 0:
                        valid = False
                        square_list[random_row][random_column].mine = False
                        break
    for row in square_list:
        for item in row:
            if not item.hidden:
                item.reveal()

def draw_grid():
    for i in range(row_max + 1):
        pygame.draw.line(screen, BLUE, (0, i*square_size), (column_max*square_size, i*square_size))
    for i in range(column_max + 1):
        pygame.draw.line(screen, BLUE, (i*square_size, 0), (i*square_size, row_max*square_size))

def draw_board():
    for row in square_list:
        for item in row:
            item.draw_me()
    draw_grid()

def list_possibilities(num_things, num_spaces):
    if num_things > num_spaces:
        return([])
    elif num_things == 0:
        string = ""
        for _ in range(num_spaces):
            string += "0"
        return([string])
    else:
        output_list = []
        position_list = []
        for _ in range(num_things):
            position_list.append("")

        def recursive(ordinal, smaller):
            nonlocal output_list
            nonlocal position_list

            for current in range(smaller+1, num_spaces -ordinal ):
                position_list[ordinal] = current

                if ordinal == 0:
                    placeholder = []
                    for item in position_list:
                        placeholder.append(item)

                    output_list.append(placeholder)
                else:
                    recursive(ordinal - 1, current)
        recursive(num_things-1, -1)
        return output_list

def wait():
    draw_board()
    pygame.display.update()

def brute(working):
    global check_list
    for square in check_list:
        print(square.row, square.column)
    wait()
    if working:
        print("going")
        working = False
        count = 0
        for i in range(len(check_list)):
            if stage == "play":
                item = check_list[count]
                if item.adjacent_mines("b") > 0:
                    attempt = item.autocomplete()
                    pygame.event.get()

                    if attempt:
                        working = True
                        del check_list[count]
                        wait()
                        check_win()
                    else:
                        count += 1
                else:
                    del check_list[count]

            else:
                break

    else:
        print("stuck")
        wait()
        stuck_v2()
        # thread = threading.Thread(stuck())
        # thread.start()
        # thread.join()

        working = True
        check_win()
    return working

def check_board():
    valid = True
    for row in square_list:
        for item in row:
            if (not item.hidden) and item.adjacent_mines("m") > 0:
                suspicious_count = 0
                for adjacent in item.adjacents(0):
                    if adjacent.flag or adjacent.mark_mine:
                        suspicious_count += 1
                if suspicious_count != item.adjacent_mines("m"):
                    valid = False
                    break
    return valid

def check_win():
    global win
    global stage
    if reveal_count == row_max*column_max - mine_count:
        stage = "end"
        win = True

timer_event = pygame.USEREVENT + 1
pygame.time.set_timer(timer_event, 1)

def game(option):
    global screen
    global stage
    global win
    global check_list
    win = False
    done = False

    screen.fill(WHITE)
    draw_grid()
    autocomplete_working = True
    stage_done = True

    while not done:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

            if stage == "setup":
                if option == "c":
                    setup(rand(0, row_max-1), rand(0,column_max-1))
                    stage = "play"
                    draw_board()
                    for row in square_list:
                        for square in row:
                            if not square.hidden and square.adjacent_mines("b") > 0:
                                check_list.append(square)
                    wait()
                    time.sleep(1.5)
                if  event.type == pygame.MOUSEBUTTONDOWN and option == "p":
                    mouse_row = pygame.mouse.get_pos()[1]//square_size
                    mouse_column = pygame.mouse.get_pos()[0]//square_size
                    setup(mouse_row, mouse_column)
                    stage = "play"
                    draw_board()

            elif stage == "play":
                if event.type == pygame.MOUSEBUTTONDOWN and option == "p":
                    mouse_row = pygame.mouse.get_pos()[1]//square_size
                    mouse_column = pygame.mouse.get_pos()[0]//square_size
                    button = event.button
                    if button == 1:
                        if square_list[mouse_row][mouse_column].hidden:
                            square_list[mouse_row][mouse_column].reveal()
                        else:
                            _ = square_list[mouse_row][mouse_column].autocomplete()
                        print(square_possibilities(square_list[mouse_row][mouse_column]))

                        check_win()
                        draw_board()
                    #Left click

                    elif button == 3:
                    #Right click
                        square_list[mouse_row][mouse_column].flag = not square_list[mouse_row][mouse_column].flag
                        draw_board()
                        print(square_possibilities(square_list[mouse_row][mouse_column]))

                elif option == "c" and event.type == timer_event:
                    autocomplete_working = brute(autocomplete_working)

            elif stage == "end":

                if stage_done == True:

                    if win == False:
                        print("lose")
                        # for row in square_list:
                        #     for square in row:
                        #         if square.flag:
                        #             print(square.row, square.column, "flag")

                    else:
                        print("win")
                        if option == "c":
                            for row in square_list:
                                for item in row:
                                    if not item.flag and item.hidden:
                                        item.flag = True
                            wait()
                    stage_done = False
                # # if event.type == pygame.MOUSEBUTTONDOWN:
                # #     done = True
                # # else:
                # if win:
                #     text = "You Win!"
                # else:
                #     text = "You Lose!"
                # screen.fill(BLACK)
                # font = pygame.font.SysFont(None, square_size)
                # screen.blit(font.render(text, True, WHITE), (square_size*column_max/3, square_size*row_max/3))

        pygame.display.update()

def square_possibilities(square):
    spaces = square.adjacent_mines("b")

    temp_list = list_possibilities(square.adjacent_mines("m") - square.adjacent_mines("f"), spaces)
    output_list = []
    for item in temp_list:
        binary_string = ''
        for i in range(spaces):
            if i in item:
                binary_string += "1"
            else:
                binary_string += "0"

        output_list.append(binary_string)
    return output_list

def convert_overlap(indexes, string):
    new_bin = ""
    for index in indexes:
        new_bin += string[index]
    return new_bin
#reduce string to just overlaps

def make_new_bin(overlap_indexes, possibilities):
    output_list = []
    for bin in possibilities:
        new_bin = convert_overlap(overlap_indexes, bin)
        if new_bin not in output_list:
            output_list.append(new_bin)
    return output_list
#make new list of just overlap possibilities
def check_in_list(possibility_list, indexes, overlap_possibilities):
    count = 0
    for i in range(len(possibility_list)):
        if convert_overlap(indexes, possibility_list[count]) not in overlap_possibilities:
            possibility_list.pop(count)
        else:
            count += 1
    return possibility_list
#Cull board_possibilities to possible lists

def stuck_v2():
    global check_list
    num_flags = 0
    for row in square_list:
        for item in row:
            if item.flag:
                num_flags += 1

    board_possibilities = square_possibilities(check_list[0])
    # for item in check_list:
    #     print(item.row, item.column, item.hidden)
    keys = check_list[0].adjacents(1)
    for key in keys:
        print(key.row, key.column, "key")
    # for key in keys:
    #     print("keys", key.row, key.column)
    big_success = False
    for k in range(1,len(check_list)):
        for key in keys:
            print(key.row, key.column, "key")

        item = check_list[k]
        # print(item, item.row, item.column)
        big_overlap_indexes = []
        small_overlap_indexes = []
        item_adjacents = item.adjacents(1)
        # for adjacent in item_adjacents:
        #     # print("adjacents",adjacent.row, adjacent.column )

        item_possibilities = square_possibilities(item)
        print(item_possibilities, item.row, item.column)
        for i in range(len(keys)):
            for k in range(len(item_adjacents)):
                if keys[i] == item_adjacents[k]:
                    big_overlap_indexes.append(i)
                    small_overlap_indexes.append(k)
#Finds which squares overlap

        big_overlap_possibilities = make_new_bin(big_overlap_indexes, board_possibilities)
        print(big_overlap_possibilities, "big overlap possibilities")
        small_overlap_possibilities = make_new_bin(small_overlap_indexes, item_possibilities)
        print(small_overlap_possibilities, "small """)

        overlap_possibilities = small_overlap_possibilities
        for bin in overlap_possibilities:
            if bin not in big_overlap_possibilities:
                overlap_possibilities.remove(bin)

        board_possibilities = check_in_list(board_possibilities, big_overlap_indexes, overlap_possibilities)
        item_possibilities = check_in_list(item_possibilities, small_overlap_indexes, overlap_possibilities)
        pygame.event.get()
        stripped_item_possibilities = []

        for bin in item_possibilities:
            new_bin = ""
            for i in range(len(bin)):
                if i not in small_overlap_indexes:
                    new_bin += bin[i]
            if new_bin not in stripped_item_possibilities:
                stripped_item_possibilities.append(new_bin)
        stripped_item_keys = []
        pygame.event.get()
        for i in range(len(item_adjacents)):
            if i not in small_overlap_indexes:
                stripped_item_keys.append(item_adjacents[i])
        new_board_possibilities = []
        for bin in board_possibilities:
            for item_bin in stripped_item_possibilities:
                potential_mines = 0
                new_bin = bin + item_bin
                check_bin = item_bin

                for i in range(len(big_overlap_indexes)):
                    check_bin = check_bin[:small_overlap_indexes[i]] + bin[big_overlap_indexes[i]] + check_bin[small_overlap_indexes[i]:]
                if check_bin in item_possibilities:
                    for character in new_bin:
                        if character == "1":
                            potential_mines += 1
                    if num_flags + potential_mines <= mine_count:
                        new_board_possibilities.append(new_bin)
                    #Check if there will be too many mines on the board

        # print(board_possibilities)
        # print(big_overlap_indexes)
        # print(item_possibilities)
        # print(small_overlap_indexes)
        board_possibilities = new_board_possibilities
        # print(new_board_possibilities)
        keys += stripped_item_keys
        # for key in keys:
        #     print("keys", key.row, key.column)
        pygame.event.get()

        print(board_possibilities)
        for i in range(len(board_possibilities[0])):
            small_success = True
            number = board_possibilities[0][i]
            for q in range(1, len(board_possibilities)):
                if number != board_possibilities[q][i]:
                    small_success = False
                    break

            if small_success:
                big_success = True
                print("big_success")

                if number == "0":
                    keys[i].reveal()

                elif number == "1":
                    keys[i].flag = True
                wait()
        if big_success:
            for key in keys:
                print(key.row, key.column, "key")
            break

    if not big_success:
        random_num = rand(0, len(keys)-1)
        keys[random_num].reveal()
        print(keys[random_num].row, keys[random_num].column, "random selection")
        wait()
        print("random")

game("c")


import torch
import copy
import numpy as np

from torch.nn import Dropout
from torch.nn import Linear
from torch.nn import LayerNorm
from torch.nn import functional as F
from RprMultiheadAttention import MultiheadAttention

def _get_activation_fn(activation):
	if activation == "relu":
		return F.relu
	elif activation == "gelu":
		return F.gelu
	else:
		raise RuntimeError("activation should be relu/gelu, not %s." % activation)

class MemTransformerEncoderLayer(torch.nn.Module):
	"""TransformerEncoderLayer is made up of self-attn and feedforward network.
	This standard encoder layer is based on the paper "Attention Is All You Need".
	Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez,
	Lukasz Kaiser, and Illia Polosukhin. 2017. Attention is all you need. In Advances in
	Neural Information Processing Systems, pages 6000-6010. Users may modify or implement
	in a different way during application.

	Args:
		d_model: the number of expected features in the input (required).
		nhead: the number of heads in the multiheadattention models (required).
		dim_feedforward: the dimension of the feedforward network model (default=2048).
		dropout: the dropout value (default=0.1).
		activation: the activation function of intermediate layer, relu or gelu (default=relu).

	Examples::
		>>> encoder_layer = nn.TransformerEncoderLayer(d_model=512, nhead=8)
		>>> src = torch.rand(10, 32, 512)
		>>> out = encoder_layer(src)
	"""

	def __init__(self, d_model, nhead, frame_len=29, dim_feedforward=2048, dropout=0.1, activation="relu"):
		super(MemTransformerEncoderLayer, self).__init__()
		self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout)
		# Implementation of Feedforward model
		self.linear1 = Linear(d_model, dim_feedforward)
		self.dropout = Dropout(dropout)
		self.linear2 = Linear(dim_feedforward, d_model)

		self.norm1 = LayerNorm(d_model)
		self.norm2 = LayerNorm(d_model)
		self.dropout1 = Dropout(dropout)
		self.dropout2 = Dropout(dropout)

		self.activation = _get_activation_fn(activation)

		# rpr encoding
		self.frame_len = frame_len
		self.k = 5
		self.create_rpr_table(self.k)
		self.pk_embed = nn.Embedding(2*self.k + 1, d_model)
		self.pv_embed = nn.Embedding(2*self.k + 1, d_model)

	def idx_clip(x, k):
		return max(-k, min(k, x))

	def create_rpr_table(self, k):
		self.rpr_table = np.zeros((self.frame_len, self.frame_len))
		for i in range(1, self.frame_len + 1):
			for j in range(1, self.frame_len + 1):
				self.rpr_table[i-1, j-1] = idx_clip(j-i, k) + k
		return

	def forward(self, src, src_mask=None, src_key_padding_mask=None):
		r"""Pass the input through the endocder layer.

		Args:
			src: the sequnce to the encoder layer (required).
			src_mask: the mask for the src sequence (optional).
			src_key_padding_mask: the mask for the src keys per batch (optional).

		Shape:
			see the docs in Transformer class.
		"""
		src2 = self.self_attn(src, src, src, posk, posv, attn_mask=src_mask,
							  key_padding_mask=src_key_padding_mask)[0]
		src = src + self.dropout1(src2)
		src = self.norm1(src)
		if hasattr(self, "activation"):
			src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
		else:  # for backward compatibility
			src2 = self.linear2(self.dropout(F.relu(self.linear1(src))))
		src = src + self.dropout2(src2)
		src = self.norm2(src)
		return src


import logging
import requests

from django.urls import reverse

from django.conf import settings
from django.core.mail import get_connection
from django.core.mail.backends.base import BaseEmailBackend

from .engine import send_email
from mail.models import Message

logger = logging.getLogger('mails')

def get_admins():
    return [mail for name, mail in settings.ADMINS]

class StoreBackend(BaseEmailBackend):
    def __get_category_from_header(self, email):
        category = None
        if settings.MAIL_CATEGORY_HEADER_KEY in email.extra_headers:
            category = email.extra_headers.get(settings.MAIL_CATEGORY_HEADER_KEY, None)
            email.extra_headers.pop(settings.MAIL_CATEGORY_HEADER_KEY)
        return category

    def __is_error_or_test_mail(self, subject):
        return subject.startswith(settings.MAIL_ERROR_MESAGE_SUBJECT) or subject.startswith(
            settings.MAIL_TEST_MESSAGE_SUBJECT)

    def send_messages(self, email_messages, notify_webhook=None):
        AUTOSEND = getattr(settings, 'MAILER_AUTOSEND', False)
        num_sent = 0

        for email in email_messages:
            if self.__is_error_or_test_mail(email.subject):
                email.to = get_admins()
                email.connection = get_connection(
                    backend=settings.MAILER_EMAIL_BACKEND
                )
                email.send()
            else:
                msg = Message.objects.create(
                    subject=email.subject,
                    to_email=email.to,
                    from_email=email.from_email,
                    reply_to=email.reply_to,
                    cc=email.cc,
                    bcc=email.bcc,
                    body=email.body,
                    category=self.__get_category_from_header(email),
                    attachment=email.attachments)

                if notify_webhook:
                    mail_url = '{}{}'.format(
                        settings.DOMAIN_NAME,
                        reverse('mail:inbox-message', kwargs={'pk': msg.pk}),
                    )
                    try:
                        requests.put(
                            notify_webhook,
                            data={
                                'email_url': mail_url,
                                'email_status': settings.MAIL_NOTIFY_WEBHOOK_STATUS_OK,
                            },
                        )
                    except Exception as e:  # noqa
                        logger.error('Notify webhook Exception: {}'.format(e))

                if AUTOSEND:
                    send_email(msg)

            num_sent += 1

        return num_sent


##############################################################################
#
#  File format versions:
#
#  1: initial version
#
#  2: now contains reciprocal planck opacity and rosseland opacity
#     previous rosseland opacity was actually reciprocal planck opacity
#
##############################################################################

from __future__ import print_function, division

import os
import hashlib

import h5py
import numpy as np
from astropy import log as logger
import six

from ..version import __version__

from ..util.constants import c
from ..util.functions import FreezableClass
from ..util.interpolate import interp1d_fast_loglog
from ..util.integrate import integrate_loglog
from ..util.nans import check_for_nans

from .optical_properties import OpticalProperties
from .emissivities import Emissivities
from .mean_opacities import MeanOpacities

def henyey_greenstein(mu, g, p_lin_max):
    P1 = (1. - g * g) / (1. + g * g - 2. * g * mu) ** 1.5
    P2 = - p_lin_max * P1 * (1. - mu * mu) / (1. + mu * mu)
    P3 = P1 * 2. * mu / (1. + mu * mu)
    P4 = 0.
    return P1, P2, P3, P4

class SphericalDust(FreezableClass):
    r"""
    This class should be used in cases where fully arbitrary dust properties
    need to be specified, within the framework of randomly oriented grains,
    which means that the scattering phase function has the general form:

    .. math:: R(\theta) = \left[\begin{array}{cccc}P_1 & P_2 & 0 & 0 \\P_2 & P_1 & 0 & 0 \\0 & 0 & P_3 & -P_4 \\0 & 0 & P_4 & P_3\end{array}\right]

    This class is initialized with::

        d = SphericalDust()

    and the properties should then be set manually. See
    `here <http://docs.hyperion-rt.org/en/stable/setup/setup_dust.html#fully-customized-4-element-dust>`_
    for a description of the available properties and how to set them.
    """

    def __init__(self, *args):

        self._file = None
        self.md5 = None

        self.optical_properties = OpticalProperties()
        self.mean_opacities = MeanOpacities()
        self.emissivities = Emissivities()

        self.set_sublimation_specific_energy('no', 0.)

        self._freeze()

        if len(args) == 0:
            pass
        elif len(args) == 1:
            self.read(args[0])
        else:
            raise Exception("SphericalDust cannot take more than one argument")

    def hash(self):

        h = hashlib.md5()

        if self.optical_properties is None or not self.optical_properties.all_set():
            h.update('none'.encode('utf-8'))
        else:
            h.update(self.optical_properties.hash().encode('utf-8'))

        if self.emissivities is None or not self.emissivities.all_set():
            h.update('none'.encode('utf-8'))
        else:
            h.update(self.emissivities.hash().encode('utf-8'))

        if self.mean_opacities is None or not self.mean_opacities.all_set():
            h.update('none'.encode('utf-8'))
        else:
            h.update(self.mean_opacities.hash().encode('utf-8'))

        import struct
        h.update(self.sublimation_mode.encode('utf-8'))
        h.update(struct.pack('>d', self.sublimation_energy))

        return h.hexdigest()

    def set_lte_emissivities(self, n_temp=1200, temp_min=0.1, temp_max=100000.):
        '''
        Calculate the emissivities assuming LTE

        Parameters
        ----------
        n_temp : int, optional
            The number of temperatures to calculate the emissivities for
        temp_min : float, optional
            The minimum temperature to calculate the emissivities for
        temp_max : float, optional
            The maximum temperature to calculate the emissivities for
        '''
        self.mean_opacities.compute(self.optical_properties, n_temp=n_temp,
                                    temp_min=temp_min, temp_max=temp_max)
        self.emissivities.set_lte(self.optical_properties, self.mean_opacities)

    def plot(self, filename):

        # Check that the optical properties have been set
        self.optical_properties.ensure_all_set()

        import matplotlib.pyplot as plt

        # Save original rc parameters
        rc_orig = plt.rcParams

        # Reset to defaults
        plt.rcdefaults()
        plt.rc('legend', fontsize=7)
        plt.rc('axes', titlesize='x-small')
        plt.rc('axes', labelsize='x-small')
        plt.rc('xtick', labelsize='xx-small')
        plt.rc('ytick', labelsize='xx-small')
        plt.rc('axes', linewidth=0.5)
        plt.rc('patch', linewidth=0.5)

        # Compute mean opacities if not already existent
        self._compute_mean_opacities()

        # Check that emissivities are set (before computing mean opacities)
        if not self.emissivities.all_set():
            logger.info("Computing emissivities assuming LTE")
            self.emissivities.set_lte(self.optical_properties, self.mean_opacities)

        # Initialize figure
        fig = plt.figure(figsize=(10, 12))

        # Plot optical properties
        fig = self.optical_properties.plot(fig, [421, 423, 424, 425, 426])

        # Plot mean opacities
        fig = self.mean_opacities.plot(fig, 428)

        # Plot emissivities
        fig = self.emissivities.plot(fig, 427)

        # Adjust spacing between subplots
        fig.subplots_adjust(left=0.08, right=0.92, wspace=0.22, hspace=0.30)

        # Save figure
        fig.savefig(filename, bbox_inches='tight')

        # Close figure to save RAM
        plt.close(fig)

        # Restore rc parameters
        plt.rc(rc_orig)

    def set_sublimation_temperature(self, mode, temperature=0.):
        '''
        Set the dust sublimation mode and temperature.

        Parameters
        ----------
        mode : str
            The dust sublimation mode, which can be:
                * 'no'   - no sublimation
                * 'fast' - remove all dust in cells exceeding the
                           sublimation temperature
                * 'slow' - reduce the dust in cells exceeding the
                           sublimation temperature
                * 'cap'  - any temperature exceeding the sublimation
                           temperature is reset to the sublimation
                           temperature.

        temperature : float, optional
            The dust sublimation temperature, in K
        '''

        if mode not in ['no', 'fast', 'slow', 'cap']:
            raise Exception("mode should be one of no/fast/slow/cap")

        if mode != 'no' and temperature is None:
            raise Exception("Need to specify a sublimation temperature")

        self.sublimation_mode = mode
        self.sublimation_energy = self.temperature2specific_energy(temperature)

    def set_sublimation_specific_energy(self, mode, specific_energy=0.):
        '''
        Set the dust sublimation mode and specific energy.

        Parameters
        ----------
        mode : str
            The dust sublimation mode, which can be:
                * 'no'   - no sublimation
                * 'fast' - remove all dust in cells exceeding the
                           sublimation specific energy
                * 'slow' - reduce the dust in cells exceeding the
                           sublimation specific energy
                * 'cap'  - any specific energy exceeding the sublimation
                           specific energy is reset to the sublimation
                           specific energy.

        specific_energy : float, optional
            The dust sublimation specific energy, in cgs
        '''

        if mode not in ['no', 'fast', 'slow', 'cap']:
            raise Exception("mode should be one of no/fast/slow/cap")

        if mode != 'no' and specific_energy is None:
            raise Exception("Need to specify a sublimation specific_energy")

        self.sublimation_mode = mode
        self.sublimation_energy = specific_energy

    def _write_dust_sublimation(self, group):
        group.attrs['sublimation_mode'] = np.string_(self.sublimation_mode)
        if self.sublimation_mode in ['slow', 'fast', 'cap']:
            group.attrs['sublimation_specific_energy'] = self.sublimation_energy

    def _read_dust_sublimation(self, group):
        if 'sublimation_mode' in group.attrs:
            self.sublimation_mode = group.attrs['sublimation_mode'].decode('ascii')
            if self.sublimation_mode in ['slow', 'fast', 'cap']:
                self.sublimation_energy = group.attrs['sublimation_specific_energy']

    def _compute_mean_opacities(self):
        if not self.mean_opacities.all_set():
            self.mean_opacities.compute(self.optical_properties)

    def write(self, filename, compression=True):
        '''
        Write out to a standard dust file, including calculations of the mean
        opacities and optionally thermal emissivities.
        '''

        # Check that the optical properties have been set
        self.optical_properties.ensure_all_set()

        # Compute mean opacities if not already existent
        self._compute_mean_opacities()

        # Check that emissivities are set (before computing mean opacities)
        if not self.emissivities.all_set():
            logger.info("Computing emissivities assuming LTE")
            self.emissivities.set_lte(self.optical_properties, self.mean_opacities)

        # Create dust table set
        if isinstance(filename, six.string_types):
            dt = h5py.File(filename, 'w')
        else:
            dt = filename

        # Add standard keywords to header
        dt.attrs['version'] = 2
        dt.attrs['type'] = 1
        dt.attrs['python_version'] = np.string_(__version__)
        if self.md5:
            dt.attrs['asciimd5'] = np.string_(self.md5)

        # Add optical properties and scattering angle tables
        self.optical_properties.to_hdf5_group(dt)

        # Add mean opacities table
        self.mean_opacities.to_hdf5_group(dt)

        # Add emissivities and emissivity variable tables
        self.emissivities.to_hdf5_group(dt)

        # Dust sublimation parameters
        self._write_dust_sublimation(dt)

        # Check that there are no NaN values in the file - if there are, a
        # warning is emitted.
        check_for_nans(dt)

        # Close dust file
        if isinstance(dt, h5py.File):
            dt.close()

        self._file = (filename, self.hash())

    def read(self, filename):
        '''
        Read in from a standard dust file
        '''

        from ..util.functions import asstr

        if isinstance(filename, six.string_types):

            # Check file exists
            if not os.path.exists(filename):
                raise Exception("File not found: %s" % filename)

            # Read in dust table set
            dt = h5py.File(filename, 'r')
            close = True

        else:

            # Read in dust table set
            dt = filename
            close = False

        # Check version and type
        if dt.attrs['version'] not in [1, 2]:
            raise Exception("Version should be 1 or 2")
        if dt.attrs['type'] != 1:
            raise Exception("Type should be 1")
        if 'asciimd5' in dt.attrs:
            self.md5 = asstr(dt.attrs['asciimd5'])
        else:
            self.md5 = None

        # Read in the optical properties
        self.optical_properties.from_hdf5_group(dt)

        # Read in the planck and rosseland mean opacities
        if dt.attrs['version'] == 1:
            logger.warning("Version 1 dust file detected - discarding mean opacities and recomputing them")
            self.mean_opacities.compute(self.optical_properties)
        else:
            self.mean_opacities.from_hdf5_group(dt)

        # Read in emissivities
        self.emissivities.from_hdf5_group(dt)

        # Dust sublimation parameters
        self._read_dust_sublimation(dt)

        # Close file object if needed
        if close:
            dt.close()
            self._file = (filename, self.hash())

    def chi_nu_temperature(self, temperature):
        """
        Compute the mean opacity to extinction for a blackbody at a given temperature.

        Parameters
        ----------
        temperature : float
            The temperature of the blackbody to use

        Returns
        -------
        chi_nu_mean : float
            The mean opacity to extinction
        """
        self._compute_mean_opacities()
        return interp1d_fast_loglog(self.mean_opacities.temperature,
                                    self.mean_opacities.chi_planck,
                                    temperature,
                                    bounds_error=True)

    def kappa_nu_temperature(self, temperature):
        """
        Compute the mean opacity to absorption for a blackbody at a given temperature.

        Parameters
        ----------
        temperature : float
            The temperature of the blackbody to use

        Returns
        -------
        kappa_nu_mean : float
            The mean opacity to absorption
        """
        self._compute_mean_opacities()
        return interp1d_fast_loglog(self.mean_opacities.temperature,
                                    self.mean_opacities.kappa_planck,
                                    temperature,
                                    bounds_error=True)

    def chi_nu_spectrum(self, nu, fnu):
        """
        Compute the mean opacity to extinction for a given spectrum.

        Parameters
        ----------
        nu : array_like
            The frequencies, in Hz
        fnu : array_like
            The monochromatic fluxes per unit frequency. Units are unimportant
            since proportionality constants are cancelled out in the
            computation.

        Returns
        -------
        chi_nu_mean : float
            The mean opacity to extinction
        """
        if nu.max() > self.optical_properties.nu.max() or nu.min() < self.optical_properties.nu.min():
            raise Exception("Opacity to extinction is not defined at all "
                            "spectrum frequencies")
        chi_nu = self.optical_properties.interp_chi_nu(nu)
        return (integrate_loglog(nu, fnu * chi_nu) /
                integrate_loglog(nu, fnu))

    def kappa_nu_spectrum(self, nu, fnu):
        """
        Compute the mean opacity to absorption for a given spectrum.

        Parameters
        ----------
        nu : array_like
            The frequencies, in Hz
        fnu : array_like
            The monochromatic fluxes per unit frequency. Units are unimportant
            since proportionality constants are cancelled out in the
            computation.

        Returns
        -------
        kappa_nu_mean : float
            The mean opacity to absorption
        """
        if nu.max() > self.optical_properties.nu.max() or nu.min() < self.optical_properties.nu.min():
            raise Exception("Opacity to absorption is not defined at all "
                            "spectrum frequencies")
        kappa_nu = self.optical_properties.interp_kappa_nu(nu)
        return (integrate_loglog(nu, fnu * kappa_nu) /
                integrate_loglog(nu, fnu))

    def temperature2specific_energy(self, temperature):
        """
        Convert a temperature to its corresponding specific energy value.

        Parameters
        ----------
        temperature : float or array_like
            The temperature to convert

        Returns
        -------
        specific_energy : float or array_like
            The specific energy corresponding to the input temperature
        """

        self._compute_mean_opacities()

        specific_energy = interp1d_fast_loglog(self.mean_opacities.temperature,
                                               self.mean_opacities.specific_energy,
                                               temperature,
                                               bounds_error=False,
                                               fill_value=np.nan)

        if np.isscalar(temperature):
            if temperature < self.mean_opacities.temperature[0]:
                specific_energy = self.mean_opacities.specific_energy[0]
            elif temperature > self.mean_opacities.temperature[-1]:
                specific_energy = self.mean_opacities.specific_energy[-1]
        else:
            specific_energy[temperature < self.mean_opacities.temperature[0]] = self.mean_opacities.specific_energy[0]
            specific_energy[temperature > self.mean_opacities.temperature[-1]] = self.mean_opacities.specific_energy[-1]

        return specific_energy

    def specific_energy2temperature(self, specific_energy):
        """
        Convert a specific energy value to its corresponding temperature.

        Parameters
        ----------
        specific_energy : float or array_like
            The specific energy to convert

        Returns
        -------
        temperature : float or array_like
            The temperature corresponding to the input specific energy
        """

        self._compute_mean_opacities()

        temperature = interp1d_fast_loglog(self.mean_opacities.specific_energy,
                                           self.mean_opacities.temperature,
                                           specific_energy,
                                           bounds_error=False,
                                           fill_value=np.nan)

        if np.isscalar(specific_energy):
            if specific_energy < self.mean_opacities.specific_energy[0]:
                temperature = self.mean_opacities.temperature[0]
            elif specific_energy > self.mean_opacities.specific_energy[-1]:
                temperature = self.mean_opacities.temperature[-1]
        else:
            temperature[specific_energy < self.mean_opacities.specific_energy[0]] = self.mean_opacities.temperature[0]
            temperature[specific_energy > self.mean_opacities.specific_energy[-1]] = self.mean_opacities.temperature[-1]

        return temperature

class IsotropicDust(SphericalDust):
    """
    This class should be used for dust properties that include isotropic
    scattering. The dust properties should be instatiated as::

        d = IsotropicDust(nu, albedo, chi)

    where ``nu``, ``albedo``, and ``chi`` are 1-D Numpy arrays containing the
    frequencies, albedo, and opacity to extinction respectively.
    """

    def __init__(self, nu, albedo, chi):

        SphericalDust.__init__(self)

        # Set cos(theta) grid for computing the scattering matrix elements
        self.optical_properties.mu = np.linspace(-1., 1., 2)

        # Set optical properties
        self.optical_properties.nu = nu
        self.optical_properties.albedo = albedo
        self.optical_properties.chi = chi

        # Compute scattering matrix elements
        self.optical_properties.initialize_scattering_matrix()

        # Set scattering matrix to isotropic values
        self.optical_properties.P1[:, :] = 1.
        self.optical_properties.P2[:, :] = 0.
        self.optical_properties.P3[:, :] = 1.
        self.optical_properties.P4[:, :] = 0.

        # Sort optical properties
        self.optical_properties._sort()

class HenyeyGreensteinDust(SphericalDust):
    """
    This class should be used for dust properties that include
    scattering parameterized by the `Henyey-Greenstein, 1941
    <http://dx.doi.org/10.1086/144246>`_ function. The dust properties should
    be instatiated as::

        d = HenyeyGreensteinDust(nu, albedo, chi, g, p_lin_max)

    where ``nu``, ``albedo``, and ``chi`` are 1-D Numpy arrays containing the
    frequencies, albedo, and opacity to extinction respectively, and ``g`` and
    ``p_lin_max`` are also 1-D Numpy arrays containing the asymmetry parameter
    and the maximum linear polarization.
    """

    def __init__(self, nu, albedo, chi, g, p_lin_max):

        SphericalDust.__init__(self)

        # Set cos(theta) grid for computing the scattering matrix elements
        n_mu = 100
        self.optical_properties.mu = np.linspace(-1., 1., n_mu)

        # Set optical properties
        self.optical_properties.nu = nu
        self.optical_properties.albedo = albedo
        self.optical_properties.chi = chi

        # Compute scattering matrix elements
        self.optical_properties.initialize_scattering_matrix()

        for i in range(n_mu):
            self.optical_properties.P1[:, i], \
                self.optical_properties.P2[:, i], \
                self.optical_properties.P3[:, i], \
                self.optical_properties.P4[:, i] = henyey_greenstein(self.optical_properties.mu[i], g, p_lin_max)

class HOCHUNKDust(HenyeyGreensteinDust):
    """
    This class should be used for dust properties that include
    scattering parameterized by the `Henyey-Greenstein, 1941
    <http://dx.doi.org/10.1086/144246>`_ function, which are formatted for the
    `HOCHUNK code <http://gemelli.colorado.edu/~bwhitney/codes/>`_. The dust
    properties should be instatiated as::

        d = HOCHUNKDust(filename)

    where ``filename`` is the name of the file containing the dust properties
    in the HOCHUNK format.
    """

    def __init__(self, filename):

        # Read in dust file
        dustfile = np.loadtxt(filename, dtype=[('wav', float), ('c_ext', float),
                                               ('c_sca', float), ('chi', float), ('g', float),
                                               ('p_lin_max', float)], usecols=[0, 1, 2, 3, 4, 5])

        # Ensure file is ordered in increasing frequency
        if dustfile['wav'][-1] > dustfile['wav'][0]:
            dustfile = dustfile[::-1]

        # Compute frequency and albedo
        nu = c / dustfile['wav'] * 1.e4
        albedo = dustfile['c_sca'] / dustfile['c_ext']

        self.md5 = hashlib.md5(open(filename, 'rb').read()).hexdigest()

        HenyeyGreensteinDust.__init__(self, nu, albedo, dustfile['chi'], dustfile['g'], dustfile['p_lin_max'])

TTsreDust = HOCHUNKDust

class CoatsphSingle(SphericalDust):

    def __init__(self, directory, size, density):
        '''
        Initialize single-component dust.

        Parameters
        ----------
        directory : str
            Directory containing all the files describing the dust
        size : float
            Grain size, in cm
        density : float
            Dust grain density, in g/cm^3
        '''

        SphericalDust.__init__(self)

        f = open('%s/coatsph_forw.dat' % directory, 'rb')
        version = f.readline()
        n_components = int(f.readline().strip().split()[5])

        # Read in main dust file

        dustfile = np.loadtxt(f, skiprows=3,
                              dtype=[('x', float), ('radius', float), ('wav', float),
                                     ('q_ext', float), ('q_sca', float), ('q_back', float),
                                     ('g', float)])

        n_wav = len(dustfile)

        self.optical_properties.nu = c / dustfile['wav'] * 1.e4
        self.optical_properties.albedo = dustfile['q_sca'] / dustfile['q_ext']
        self.optical_properties.chi = 0.75 * dustfile['q_ext'] / size / density

        # Read in scattering matrix elements

        for i in range(n_wav):

            filename = '%s/coatsph_scat_%04i_0001.dat' % (directory, i + 1)

            phasefile = np.loadtxt(filename, skiprows=9,
                                   dtype=[('theta', float), ('s11', float), ('polariz',
                                                                             float), ('s12', float), ('s33', float), ('s34',
                                                                                float)])

            if i == 0:
                self.optical_properties.mu = np.cos(np.radians(phasefile['theta']))
                self.optical_properties.initialize_scattering_matrix()

            self.optical_properties.P1[i, :] = phasefile['s11']
            self.optical_properties.P2[i, :] = phasefile['s12']
            self.optical_properties.P3[i, :] = phasefile['s33']
            self.optical_properties.P4[i, :] = phasefile['s34']

class CoatsphMultiple(SphericalDust):

    def __init__(self, directory):
        '''
        Initialize multi-component dust.

        Parameters
        ----------
        directory : str
            Directory containing all the files describing the dust
        '''

        SphericalDust.__init__(self)

        f = open('%s/coatsph_forw.dat' % directory, 'rb')
        version = f.readline()
        n_components = int(f.readline().strip().split()[5])

        # Read in main dust file

        dustfile = np.loadtxt(f, skiprows=7,
                              dtype=[('wav', float), ('c_ext', float), ('c_sca', float),
                                     ('chi', float), ('g', float), ('pmax', float),
                                     ('thetmax', float)])

        n_wav = len(dustfile)
        self.optical_properties.nu = c / dustfile['wav'] * 1.e4
        self.optical_properties.albedo = dustfile['c_sca'] / dustfile['c_ext']
        self.optical_properties.chi = dustfile['chi']

        # Read in scattering matrix elements

        for i in range(n_wav):

            filename = '%s/coatsph_scat.%04i.dat' % (directory, i + 1)

            phasefile = np.loadtxt(filename, skiprows=7,
                                   dtype=[('theta', float), ('s11', float), ('polariz',
                                                                             float), ('s12', float), ('s33', float), ('s34',
                                                                                float)])

            if i == 0:
                self.optical_properties.mu = np.cos(np.radians(phasefile['theta']))
                self.optical_properties.initialize_scattering_matrix()

            self.optical_properties.P1[i, :] = phasefile['s11']
            self.optical_properties.P2[i, :] = phasefile['s12']
            self.optical_properties.P3[i, :] = phasefile['s33']
            self.optical_properties.P4[i, :] = phasefile['s34']

class MieXDust(SphericalDust):

    def __init__(self, model):

        SphericalDust.__init__(self)

        wav = np.loadtxt('%s.alb' % model, usecols=[0])
        self.optical_properties.albedo = np.loadtxt('%s.alb' % model, usecols=[1])
        kappa = np.loadtxt('%s.k_abs' % model, usecols=[1])
        self.optical_properties.chi = kappa / (1 - self.optical_properties.albedo)

        # Check for NaN values
        for quantity in ['chi', 'albedo']:

            values = self.optical_properties.__dict__[quantity]

            if np.any(np.isnan(values)):
                logger.warning("NaN values found inside MieX %s file - interpolating" % quantity)
                invalid = np.isnan(values)
                values[invalid] = interp1d_fast_loglog(wav[~invalid], values[~invalid], wav[invalid])
                if np.any(np.isnan(values)):
                    raise Exception("Did not manage to fix NaN values in MieX %s" % quantity)

        self.optical_properties.nu = c / wav * 1.e4

        n_wav = len(wav)
        n_mu = (len(open('%s.f11' % model).readlines()) // n_wav) - 1

        mu = np.zeros(n_mu)

        # Read mu
        f11 = open('%s.f11' % model)
        f11.readline()
        f11.readline()
        for i in range(n_mu):
            mu[i] = np.cos(np.radians(float(f11.readline().split()[0])))
        f11.close()
        self.optical_properties.mu = mu[::-1]

        # Read in matrix elements

        self.optical_properties.initialize_scattering_matrix()

        f11 = open('%s.f11' % model)
        f12 = open('%s.f12' % model)
        f33 = open('%s.f33' % model)
        f34 = open('%s.f34' % model)

        f11.readline()
        f12.readline()
        f33.readline()
        f34.readline()

        for j in range(n_wav):

            if float(f11.readline()) != wav[j]:
                raise Exception("Incorrect wavelength in f11")
            if float(f12.readline()) != wav[j]:
                raise Exception("Incorrect wavelength in f12")
            if float(f33.readline()) != wav[j]:
                raise Exception("Incorrect wavelength in f33")
            if float(f34.readline()) != wav[j]:
                raise Exception("Incorrect wavelength in f34")

            for i in range(n_mu):

                self.optical_properties.P1[j, n_mu - i - 1] = float(f11.readline().split()[1])
                self.optical_properties.P2[j, n_mu - i - 1] = float(f12.readline().split()[1])
                self.optical_properties.P3[j, n_mu - i - 1] = float(f33.readline().split()[1])
                self.optical_properties.P4[j, n_mu - i - 1] = float(f34.readline().split()[1])

        for i in range(n_mu):

            for quantity in ['P1', 'P2', 'P3', 'P4']:

                values = self.optical_properties.__dict__[quantity]

                if np.any(np.isnan(values[:, i])):
                    logger.warning("NaN values found inside MieX %s file - interpolating" % quantity)
                    invalid = np.isnan(values[:, i])
                    values[:, i][invalid] = interp1d_fast_loglog(wav[~invalid], values[:, i][~invalid], wav[invalid])
                    if np.any(np.isnan(values[:, i])):
                        raise Exception("Did not manage to fix NaN values in MieX %s" % quantity)

class BHDust(SphericalDust):
    """
    This class should be used for dust properties that were computed using
    `this dust calculation code <https://github.com/hyperion-rt/bhmie>`_ which
    is a wrapper to the ``bhmie`` routine originally written by C.F. Bohren and
    D. Huffman and improved by B. Draine.

    When using the ``bhmie`` code, you should set the output format to ``2``,
    which will create a number of files ending in ``.wav``, ``.mu``, ``.alb``,
    etc. Then, instantiate this class with the name of the directory containing
    these output files along with the prefix used. For example, if you use
    ``directory/mydust`` as a prefix in ``bhmie``, you can import this dust
    with::

        >>> from hyperion.dust import BHDust
        >>> d = BHDust('directory/mydust')

    """

    def __init__(self, model):

        SphericalDust.__init__(self)

        mu = np.loadtxt('%s.mu' % model)

        nu = c / np.loadtxt('%s.wav' % model) * 1.e4
        albedo = np.loadtxt('%s.alb' % model)
        chi = np.loadtxt('%s.chi' % model)

        P1 = np.loadtxt('%s.f11' % model)
        P2 = np.loadtxt('%s.f12' % model)
        P3 = np.loadtxt('%s.f33' % model)
        P4 = np.loadtxt('%s.f34' % model)

        if nu[-1] < nu[0]:
            nu = nu[::-1]
            albedo = albedo[::-1]
            chi = chi[::-1]
            P1 = P1[::-1, :]
            P2 = P2[::-1, :]
            P3 = P3[::-1, :]
            P4 = P4[::-1, :]

        if mu[-1] < mu[0]:
            mu = mu[::-1]
            P1 = P1[:, ::-1]
            P2 = P2[:, ::-1]
            P3 = P3[:, ::-1]
            P4 = P4[:, ::-1]

        self.optical_properties.mu = mu

        self.optical_properties.nu = nu
        self.optical_properties.albedo = albedo
        self.optical_properties.chi = chi

        self.optical_properties.P1 = P1
        self.optical_properties.P2 = P2
        self.optical_properties.P3 = P3
        self.optical_properties.P4 = P4

{'struct': 'struct', 'plt': 'matplotlib.pyplot', 'asstr': 'util.functions.asstr'}.__init__


import json
import logging
import traceback

from django.views import View
from django.http import JsonResponse

from backend.models import AnnotationCategory, Video

# from django.core.exceptions import BadRequest
class AnnoatationCategoryCreate(View):
    def post(self, request):
        try:
            if not request.user.is_authenticated:
                return JsonResponse({"status": "error"})
            # decode data
            try:
                body = request.body.decode("utf-8")
            except (UnicodeDecodeError, AttributeError):
                body = request.body

            try:
                data = json.loads(body)
            except Exception as e:
                return JsonResponse({"status": "error", "type": "wrong_request_body"})

            if "name" not in data:
                return JsonResponse({"status": "error", "type": "missing_values"})

            try:
                query_args = {"name": data.get("name"), "owner": request.user}
                if "video_id" in data:
                    query_args["video__id"] = data.get("video_id")
                annotation_category_db = AnnotationCategory.objects.get(**query_args)
            except AnnotationCategory.DoesNotExist:
                create_args = {"name": data.get("name"), "owner": request.user}
                if "color" in data:
                    create_args["color"] = data.get("color")
                if "video_id" in data:
                    try:
                        video_db = Video.objects.get(id=data.get("video_id"))
                    except Video.DoesNotExist:
                        return JsonResponse({"status": "error", "type": "not_exist"})
                    create_args["video"] = video_db

                annotation_category_db = AnnotationCategory.objects.create(**create_args)
            return JsonResponse({"status": "ok", "entry": annotation_category_db.to_dict()})
        except Exception as e:
            logging.error(traceback.format_exc())
            return JsonResponse({"status": "error"})

class AnnoatationCategoryList(View):
    def get(self, request):
        try:
            if not request.user.is_authenticated:
                return JsonResponse({"status": "error"})

            query_args = {}

            query_args["owner"] = request.user

            if "video_id" in request.GET:
                query_args["video__id"] = request.GET.get("video_id")

            query_results = AnnotationCategory.objects.filter(**query_args)

            entries = []
            for annotation_category in query_results:
                entries.append(annotation_category.to_dict())
            return JsonResponse({"status": "ok", "entries": entries})
        except Exception as e:
            logging.error(traceback.format_exc())
            return JsonResponse({"status": "error"})


"""fask_dj URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^tasks', views.tasks, name = 'tasks'),
    url(r'^task/(?P<task_id>[0-9]+)/$', views.task, name = 'task'),
    url(r'^task_delete/(?P<task_id>[0-9]+)/$', views.task_delete, name = 'task_delete'),
    url(r'^task_project/(?P<task_id>[0-9]+)/(?P<project_id>[0-9]+)/(?P<redirect_name>[a-z]+)$', views.task_project, name = 'task_project'),
]


from django.db import models
import uuid

class apiKeys(models.Model):
    id = models.CharField(max_length=200, null=False, blank=False)
    key = models.TextField(primary_key=True, default=uuid.uuid4().hex)

    def __unicode__(self):
        return self.id

class ButtonTable(models.Model):
    SKRANKE = 0
    TELEFON = 1
    RANDOM = 2
    BUTTON_CHOICES= (
        (SKRANKE, 'Skranke'),
        (TELEFON, 'Telefon'),
        (RANDOM, 'Random')
    )
    sender = models.ForeignKey(apiKeys, editable=False, null=False)
    button = models.IntegerField(choices=BUTTON_CHOICES, default=0)
    date_registered = models.DateTimeField(auto_now_add=True, auto_now=False)

    def __unicode__(self):
        return str(self.button)

    class Meta:
        verbose_name_plural = 'Case registers'
        verbose_name = 'Registered case'


from flair.data import Sentence
from flair.models import SequenceTagger
from flair.embeddings import (DocumentPoolEmbeddings,
                              FlairEmbeddings, StackedEmbeddings)

from torch import dot, norm, squeeze

from pathlib import Path
import pickle

from odm.nlp.tensors import PCA, plot_embeddings, normalize_tensors
from odm.nlp.parse import parse_veclang

class Model:

    modelpath = '/data/model/'

    def __init__(self):

        # Sequence Tagging Model
        tagger_file = self.modelpath + 'tagger.pt'
        if Path(tagger_file).is_file():
            print('loading tagger from file')
            self.tagger = SequenceTagger.load_from_file(tagger_file)
        else:
            print('downloading pretrained tagger')
            self.tagger = SequenceTagger.load('ner-ontonotes')
            self.tagger.save(tagger_file)

        # Text Embedding Model
        embeddings_file = self.modelpath + 'embeddings.pickle'
        if Path(embeddings_file).is_file():
            print('loading embedder from file')
            filestream = open(embeddings_file, 'rb')
            self.embeddings = pickle.load(filestream)
        else:
            print('downloading pretrained embedders')
            self.embeddings = [
                # WordEmbeddings('glove'),
                FlairEmbeddings('multi-forward')
                # FlairEmbeddings('multi-backward')
            ]
            filestream = open(embeddings_file, 'wb')
            pickle.dump(self.embeddings, filestream)

        self.token_embedder = StackedEmbeddings(self.embeddings)
        self.doc_embedder = DocumentPoolEmbeddings(self.embeddings)

    def parse(self, text):

        sentence = Sentence(text)
        self.tagger.predict(sentence)
        self.token_embedder.embed(sentence)
        self.doc_embedder.embed(sentence)

        return sentence

    def mindmap(self, text):

        # parsing
        lines, arrows = parse_veclang(text)
        sentences = [self.parse(line) for line in lines]
        tensors = [s.get_embedding() for s in sentences]

        # tensor processing
        norm_tensors = normalize_tensors(tensors)
        flat_tensors = PCA(norm_tensors)

        # plot map
        filename = plot_embeddings(lines, flat_tensors, arrows)

        return f'Plotted mindmap to {filename}'

    def similarity(self, text):
        lines = text.split('//')
        sentences = [self.parse(line) for line in lines]
        vecs = [squeeze(s.embedding) for s in sentences]
        sim = dot(vecs[0], vecs[1])/(norm(vecs[0])*norm(vecs[1]))

        return f'the similarity is {sim}'


from __future__ import division
import geojson
from helper import load_geom, degrees_to_radians, get_coord, PRECISION
from math import pow, sqrt, pi, tan, cos, sin
from measurement import rhumb_destination
from transformation import transform_rotate

def ellipse(center, x_semi_axis, y_semi_axis, options={}):
    steps = 64
    if 'steps' in options:
        steps = options['steps']
    units = 'kilometers'
    units_mapping = {
        'miles': 'mi',
        'kilometers': 'km',
        'meters': 'm',
        'degrees': 'degrees',
    }
    if 'units' in options:
        units = options['units']
    angle = 0
    if 'angle' in options:
        angle = options['angle']

    # validation
    if center is None:
        raise Exception('center is required')
    if x_semi_axis is None:
        raise Exception('x_semi_axis is required')
    if y_semi_axis is None:
        raise Exception('y_semi_axis is required')
    if units not in units_mapping:
        raise Exception('non valid units')

    units = units_mapping[units]
    center = load_geom(center)
    center_coords = get_coord(center)
    angle_rad = 0
    if units == 'degrees':
        angle_rad = degrees_to_radians(angle)
    else:
        x_semi_axis = rhumb_destination(center, x_semi_axis, 90, {'units': units})
        y_semi_axis = rhumb_destination(center, y_semi_axis, 0, {'units': units})
        x_semi_axis = get_coord(x_semi_axis)[0] - center_coords[0]
        y_semi_axis = get_coord(y_semi_axis)[1] - center_coords[1]

    coordinates = []
    for i in range(0, steps):
        step_angle = (i * -360) / steps
        x = (x_semi_axis * y_semi_axis) / sqrt(
            pow(y_semi_axis, 2) + pow(x_semi_axis, 2) * pow(get_tan_deg(step_angle), 2)
        )
        y = (
            0
            if pow(get_tan_deg(step_angle), 2) == 0
            else (x_semi_axis * y_semi_axis)
            / sqrt(
                pow(x_semi_axis, 2)
                + pow(y_semi_axis, 2) / pow(get_tan_deg(step_angle), 2)
            )
        )
        if step_angle < -90 and step_angle >= -270:
            x = -x
        if step_angle < -180 and step_angle >= -360:
            y = -y
        if units == 'degrees':
            newx = x * cos(angle_rad) + y * sin(angle_rad)
            newy = y * cos(angle_rad) - x * sin(angle_rad)
            x = newx
            y = newy

        coordinates.append([x + center_coords[0], y + center_coords[1]])

    coordinates.append(coordinates[0])
    if units == 'degrees':
        return geojson.dumps(
            geojson.Feature(
                geometry=geojson.Polygon([coordinates], precision=PRECISION)
            )['geometry']
        )
    else:
        return geojson.dumps(
            transform_rotate(
                geojson.Feature(
                    geometry=geojson.Polygon([coordinates], precision=PRECISION)
                ),
                angle,
                mutate=True,
            )['geometry']
        )

def get_tan_deg(deg):
    rad = (deg * pi) / 180
    return tan(rad)