# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe
from frappe import msgprint
from rigpl_erpnext.utils.rigpl_perm import *
import frappe.permissions
import re

def on_update(doc,method):
	allowed_ids = []
	#Check if Customer Login ID is not Repeated
	if doc.customer_login_id:
		other_login_id = frappe.db.sql("""SELECT name FROM `tabCustomer`
			WHERE customer_login_id = '%s'
			AND name != '%s'""" %(doc.customer_login_id, doc.name), as_list=1)
		if other_login_id:
			frappe.throw(("Customer {0} already linked to User ID {1}").format(other_login_id[0][0], \
				doc.customer_login_id))
	#Check for From Lead field and don't allow duplication.
	if doc.lead_name:
		other_lead = frappe.db.sql("""SELECT name FROM `tabCustomer`
			WHERE lead_name = '%s' AND name != '%s' """ %(doc.lead_name, doc.name), as_list=1)
		if other_lead:
			frappe.throw(("Lead {0} already linked to Customer {1}").format(doc.lead_name, \
			other_lead[0][0]))
		else:
			#Check all previous quotations and Opportunity on Lead and add the name of Customer
			quote = frappe.db.sql("""SELECT name FROM `tabQuotation`
				WHERE lead = '%s' AND (customer IS NULL OR customer = '')"""%(doc.lead_name), as_list=1)
			opp = frappe.db.sql("""SELECT name FROM `tabOpportunity`
				WHERE lead = '%s' AND (customer IS NULL OR customer = '')"""%(doc.lead_name), as_list=1)
			if quote:
				for i in quote:
					frappe.db.set_value("Quotation", i[0], "customer", doc.name)
			if opp:
				for i in opp:
					frappe.db.set_value("Opportunity", i[0], "customer", doc.name)
	else:
		#Check if any Quote or Opportunity is linked to Customer with Lead and if so Remove it.
		quote = frappe.db.sql("""SELECT name FROM `tabQuotation`
			WHERE customer = '%s' AND (lead IS NOT NULL OR lead = '')""" %(doc.name), as_list=1)
		opp = frappe.db.sql("""SELECT name FROM `tabOpportunity`
			WHERE customer = '%s' AND (lead IS NOT NULL OR lead = '')""" %(doc.name), as_list=1)
		if quote:
			for i in quote:
				frappe.db.set_value("Quotation", i[0], "customer", None)
		if opp:
			for i in opp:
				frappe.db.set_value("Opportunity", i[0], "customer", None)

	allowed_ids = get_customer_allowed_ids(doc.name)
	for user in allowed_ids:
		role_list = get_user_roles(user)
		role_in_settings, apply_to_all_doctypes, applicable_for = \
			check_role(role_list, "Customer", apply_to_all_doctypes="None")
		if role_in_settings == 1:
			create_new_user_perm(allow="Customer", for_value=doc.name, \
				user=user, apply_to_all_doctypes=apply_to_all_doctypes, \
				applicable_for=applicable_for)

	con_list = get_dl_parent(dt='Contact', linked_dt='Customer', linked_dn= doc.name)
	for contact in con_list:
		for user in allowed_ids:
			role_list = get_user_roles(user)
			role_in_settings, apply_to_all_doctypes, applicable_for = \
				check_role(role_list, "Contact", apply_to_all_doctypes="None")
			if role_in_settings == 1:
				create_new_user_perm(allow="Contact", for_value=contact[0], \
					user=user, apply_to_all_doctypes=apply_to_all_doctypes, \
					applicable_for=applicable_for)

	add_list = get_dl_parent(dt='Address', linked_dt='Customer', linked_dn= doc.name)
	for address in add_list:
		for user in allowed_ids:
			role_list = get_user_roles(user)
			role_in_settings, apply_to_all_doctypes, applicable_for = \
				check_role(role_list, "Address", apply_to_all_doctypes="None")
			if role_in_settings == 1:
				create_new_user_perm(allow="Address", for_value=address[0], \
					user=user, apply_to_all_doctypes=apply_to_all_doctypes, \
					applicable_for=applicable_for)

	cust_perm_list = get_permission(allow="Customer", for_value=doc.name)
	con_perm_list = []
	add_perm_list = []
	if con_list:
		for contact in con_list:
			con_perm_list_temp = get_permission(allow="Contact", for_value=contact[0])
			con_perm_list.extend(con_perm_list_temp)
	if add_list:
		for add in add_list:
			add_perm_list_temp = get_permission(allow="Address", for_value=add[0])
			add_perm_list.extend(add_perm_list_temp)
	for perm in cust_perm_list:
		if perm[3] not in allowed_ids:
			delete_permission(perm[0])
	for perm in add_perm_list:
		if perm[3] not in allowed_ids:
			delete_permission(perm[0])
	for perm in con_perm_list:
		if perm[3] not in allowed_ids:
			delete_permission(perm[0])

def validate(doc,method):
	new_name, entered_name = check_customer_id (doc,method)
	if doc.get('__islocal'):
		if new_name != doc.name:
			doc.customer_name = entered_name
			doc.name = new_name
	else:
		if new_name != doc.name:
			frappe.throw(("Special Characters not allowed in Customer ID.\
				Current Customer ID: {0}-->Allowed Customer ID: {1}").format(doc.name, new_name))

def check_customer_id(doc,method):
	#Disallow Special Characters in Customer ID
	new_name = re.sub('[^A-Za-z0-9]+', '', doc.name)
	entered_name = doc.name
	return new_name, entered_name


def checkRotation(str1, str2):

    str1 = [letter for letter in str1]
    str2 = [letter for letter in str2]

    n = len(str1)

    while(n > 0):
        item = str2.pop(0)
        str2.append(item)

        if(str1 == str2):
            return True

        n -= 1

    return False

str1 = "ABCD"
str2 = "CDBD"

rotationArr = checkRotation(str1, str2)
if(rotationArr):
    print("Yes Rotation")
else:
    print("No Rotation")


from typing import Dict
from src.shared.mongo import get_collection_from_config
from src.dao.raw_tweet.getter.mongo_raw_tweet_getter import MongoRawTweetGetter
from src.dao.processed_tweet.setter.mongo_processed_tweet_setter import MongoProcessedTweetSetter
from src.process.raw_tweet_processing.tweet_processor import TweetProcessor
from src.dao.raw_tweet.raw_tweet_dao_factory import RawTweetDAOFactory
from src.dao.local_neighbourhood.local_neighbourhood_dao_factory import LocalNeighbourhoodDAOFactory
from src.dao.processed_tweet.processed_tweet_dao_factory import ProcessedTweetDAOFactory

class ProcessLocalNeighbourhoodTweetsActivity():
    def __init__(self, config: Dict):
        self.tweet_processor = None
        self.local_neighbourhood_getter = None

        self.configure(config)

    def configure(self, config: Dict):
        if config is not None:
            input_datastore = config["input-datastore"]

            raw_tweet = input_datastore['UserTweet']
            local_neighbourhood = input_datastore['LocalNeighbourhood']

            raw_tweet_getter = RawTweetDAOFactory.create_getter(raw_tweet)
            local_neighbourhood_getter = LocalNeighbourhoodDAOFactory.create_getter(local_neighbourhood)

            output_datastore = config["output-datastore"]

            processed_tweet = output_datastore['ProcessedTweet']

            processed_tweet_setter = ProcessedTweetDAOFactory.create_setter(processed_tweet)

            self.tweet_processor = TweetProcessor(raw_tweet_getter,
                processed_tweet_setter)

            self.local_neighbourhood_getter = local_neighbourhood_getter

    def process_local_neighbourhood_tweets(self, seed_id: str, params=None):
        local_neighbourhood = self.local_neighbourhood_getter.get_local_neighbourhood(seed_id, params)
        self.tweet_processor.process_tweets_by_local_neighbourhood(local_neighbourhood)


import itertools
import time
import typing
from enum import IntFlag
from pathlib import Path
from typing import *

from pros.common import ui
from pros.common.utils import retries, logger
from pros.conductor import Project
from pros.serial import bytes_to_str
from pros.serial.devices.vex import VEXCommError
from pros.serial.devices.vex.stm32_device import STM32Device
from pros.serial.ports import list_all_comports

from .vex_device import VEXDevice
from ..system_device import SystemDevice

def find_cortex_ports():
    return [p for p in list_all_comports() if p.vid is not None and p.vid in [0x4D8, 0x67B]]

class CortexDevice(VEXDevice, SystemDevice):
    class SystemStatus(object):
        def __init__(self, data: Tuple[bytes, ...]):
            self.joystick_firmware = data[0:2]
            self.robot_firmware = data[2:4]
            self.joystick_battery = float(data[4]) * .059
            self.robot_battery = float(data[5]) * .059
            self.backup_battery = float(data[6]) * .059
            self.flags = CortexDevice.SystemStatusFlags(data[7])

        def __str__(self):
            return f'  Tether: {str(self.flags)}\n' \
                   f'  Cortex: F/W {self.robot_firmware[0]}.{self.robot_firmware[1]} w/ {self.robot_battery:1.2f} V ' \
                   f'(Backup: {self.backup_battery:1.2f} V)\n' \
                   f'Joystick: F/W {self.joystick_firmware[0]}.{self.robot_firmware[1]} w/ ' \
                   f'{self.joystick_battery:1.2f} V'

    class SystemStatusFlags(IntFlag):
        DL_MODE = (1 << 0)
        TETH_VN2 = (1 << 2)
        FCS_CONNECT = (1 << 3)
        TETH_USB = (1 << 4)
        DIRECT_USB = (1 << 5)
        FCS_AUTON = (1 << 6)
        FCS_DISABLE = (1 << 7)

        TETH_BITS = DL_MODE | TETH_VN2 | TETH_USB

        def __str__(self):
            def andeq(a, b):
                return (a & b) == b

            if not self.value & self.TETH_BITS:
                s = 'Serial w/VEXnet 1.0 Keys'
            elif andeq(self.value, 0x01):
                s = 'Serial w/VEXnet 1.0 Keys (turbo)'
            elif andeq(self.value, 0x04):
                s = 'Serial w/VEXnet 2.0 Keys'
            elif andeq(self.value, 0x05):
                s = 'Serial w/VEXnet 2.0 Keys (download mode)'
            elif andeq(self.value, 0x10):
                s = 'Serial w/ a USB Cable'
            elif andeq(self.value, 0x20):
                s = 'Directly w/ a USB Cable'
            else:
                s = 'Unknown'

            if andeq(self.value, self.FCS_CONNECT):
                s += ' - FCS Connected'
            return s

    def get_connected_device(self) -> SystemDevice:
        logger(__name__).info('Interrogating Cortex...')
        stm32 = STM32Device(self.port, do_negoitate=False)
        try:
            stm32.get(n_retries=1)
            return stm32
        except VEXCommError:
            return self

    def upload_project(self, project: Project, **kwargs):
        assert project.target == 'cortex'
        output_path = project.path.joinpath(project.output)
        if not output_path.exists():
            raise ui.dont_send(Exception('No output files were found! Have you built your project?'))
        with output_path.open(mode='rb') as pf:
            return self.write_program(pf, **kwargs)

    def write_program(self, file: typing.BinaryIO, **kwargs):
        action_string = ''
        if hasattr(file, 'name'):
            action_string += f' {Path(file.name).name}'
        action_string += f' to Cortex on {self.port}'
        ui.echo(f'Uploading {action_string}')

        logger(__name__).info('Writing program to Cortex')
        status = self.query_system()
        logger(__name__).info(status)
        if not status.flags | self.SystemStatusFlags.TETH_USB and not status.flags | self.SystemStatusFlags.DL_MODE:
            self.send_to_download_channel()

        bootloader = self.expose_bootloader()
        rv = bootloader.write_program(file, **kwargs)

        ui.finalize('upload', f'Finished uploading {action_string}')
        return rv

    @retries
    def query_system(self) -> SystemStatus:
        logger(__name__).info('Querying system information')
        rx = self._txrx_simple_struct(0x21, "<8B2x")
        status = CortexDevice.SystemStatus(rx)
        ui.finalize('cortex-status', status)
        return status

    @retries
    def send_to_download_channel(self):
        logger(__name__).info('Sending to download channel')
        self._txrx_ack_packet(0x35, timeout=1.0)

    @retries
    def expose_bootloader(self):
        logger(__name__).info('Exposing bootloader')
        for _ in itertools.repeat(None, 5):
            self._tx_packet(0x25)
            time.sleep(0.1)
        self.port.read_all()
        time.sleep(0.3)
        return STM32Device(self.port, must_initialize=True)

    def _rx_ack(self, timeout: float = 0.01):
        # Optimized to read as quickly as possible w/o delay
        start_time = time.time()
        while time.time() - start_time < timeout:
            if self.port.read(1)[0] == self.ACK_BYTE:
                return
        raise IOError("Device never ACK'd")

    def _txrx_ack_packet(self, command: int, timeout=0.1):
        """
                Goes through a send/receive cycle with a VEX device.
                Transmits the command with the optional additional payload, then reads and parses the outer layer
                of the response
                :param command: Command to send the device
                :param retries: Number of retries to attempt to parse the output before giving up and raising an error
                :return: Returns a dictionary containing the received command field and the payload. Correctly computes
                the payload length even if the extended command (0x56) is used (only applies to the V5).
                """
        tx = self._tx_packet(command)
        self._rx_ack(timeout=timeout)
        logger(__name__).debug('TX: {}'.format(bytes_to_str(tx)))


__title__  = "FreeCAD Arch Truss"
__author__ = "Yorik van Havre"
__url__    = "https://www.freecad.org"

import math
import FreeCAD
import ArchComponent

if FreeCAD.GuiUp:
    import FreeCADGui
    from draftutils.translate import translate
    from PySide.QtCore import QT_TRANSLATE_NOOP
else:
    # \cond
    def translate(ctxt,txt):
        return txt
    def QT_TRANSLATE_NOOP(ctxt,txt):
        return txt
    # \endcond

## @package ArchTruss
#  \ingroup ARCH
#  \brief The Truss object and tools
#
#  This module provides tools to build Truss objects.

rodmodes = ("/|/|/|",
            "/\\/\\/\\",
            "/|\\|/|\\",
           )

def makeTruss(baseobj=None,name=None):

    """
    makeTruss([baseobj],[name]): Creates a space object from the given object (a line)
    """

    if not FreeCAD.ActiveDocument:
        FreeCAD.Console.PrintError("No active document. Aborting\n")
        return
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Truss")
    obj.Label = name if name else translate("Arch","Truss")
    Truss(obj)
    if FreeCAD.GuiUp:
        ViewProviderTruss(obj.ViewObject)
    if baseobj:
        obj.Base = baseobj
        if FreeCAD.GuiUp:
            baseobj.ViewObject.hide()
    return obj

class CommandArchTruss:

    "the Arch Truss command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Truss',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Truss","Truss"),
                'Accel': "T, U",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Truss","Creates a truss object from selected line or from scratch")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        sel = FreeCADGui.Selection.getSelection()
        if len(sel) > 1:
            FreeCAD.Console.PrintError(translate("Arch","Please select only one base object or none")+"\n")
        elif len(sel) == 1:
            # build on selection
            FreeCADGui.Control.closeDialog()
            FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Truss"))
            FreeCADGui.addModule("Draft")
            FreeCADGui.addModule("Arch")
            FreeCADGui.doCommand("obj = Arch.makeTruss(FreeCAD.ActiveDocument."+FreeCADGui.Selection.getSelection()[0].Name+")")
            FreeCADGui.doCommand("Draft.autogroup(obj)")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()
        else:
            # interactive line drawing
            self.points = []
            if hasattr(FreeCAD,"DraftWorkingPlane"):
                FreeCAD.DraftWorkingPlane.setup()
            if hasattr(FreeCADGui,"Snapper"):
                FreeCADGui.Snapper.getPoint(callback=self.getPoint)

    def getPoint(self,point=None,obj=None):

        """Callback for clicks during interactive mode"""

        if point is None:
            # cancelled
            return
        self.points.append(point)
        if len(self.points) == 1:
            FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint)
        elif len(self.points) == 2:
            FreeCADGui.Control.closeDialog()
            FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Truss"))
            FreeCADGui.addModule("Draft")
            FreeCADGui.addModule("Arch")
            FreeCADGui.doCommand("base = Draft.makeLine(FreeCAD."+str(self.points[0])+",FreeCAD."+str(self.points[1])+")")
            FreeCADGui.doCommand("obj = Arch.makeTruss(base)")
            FreeCADGui.doCommand("Draft.autogroup(obj)")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()

class Truss(ArchComponent.Component):

    "The truss object"

    def __init__(self,obj):

        ArchComponent.Component.__init__(self,obj)
        self.setProperties(obj)
        obj.IfcType = "Beam"

    def setProperties(self,obj):

        pl = obj.PropertiesList
        if not "TrussAngle" in pl:
            obj.addProperty("App::PropertyAngle","TrussAngle","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The angle of the truss"))
            obj.setEditorMode("TrussAngle",1)
        if not "SlantType" in pl:
            obj.addProperty("App::PropertyEnumeration","SlantType","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The slant type of this truss"))
            obj.SlantType = ["Simple","Double"]
        if not "Normal" in pl:
            obj.addProperty("App::PropertyVector","Normal","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The normal direction of this truss"))
            obj.Normal = FreeCAD.Vector(0,0,1)
        if not "HeightStart" in pl:
            obj.addProperty("App::PropertyLength","HeightStart","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The height of the truss at the start position"))
            obj.HeightStart = 100
        if not "HeightEnd" in pl:
            obj.addProperty("App::PropertyLength","HeightEnd","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The height of the truss at the end position"))
            obj.HeightEnd = 200
        if not "StrutStartOffset" in pl:
            obj.addProperty("App::PropertyDistance","StrutStartOffset","Truss",
                            QT_TRANSLATE_NOOP("App::Property","An optional start offset for the top strut"))
        if not "StrutEndOffset" in pl:
            obj.addProperty("App::PropertyDistance","StrutEndOffset","Truss",
                            QT_TRANSLATE_NOOP("App::Property","An optional end offset for the top strut"))
        if not "StrutHeight" in pl:
            obj.addProperty("App::PropertyLength","StrutHeight","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The height of the main top and bottom elements of the truss"))
            obj.StrutHeight = 10
        if not "StrutWidth" in pl:
            obj.addProperty("App::PropertyLength","StrutWidth","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The width of the main top and bottom elements of the truss"))
            obj.StrutWidth = 5
        if not "RodType" in pl:
            obj.addProperty("App::PropertyEnumeration","RodType","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The type of the middle element of the truss"))
            obj.RodType = ["Round","Square"]
        if not "RodDirection" in pl:
            obj.addProperty("App::PropertyEnumeration","RodDirection","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The direction of the rods"))
            obj.RodDirection = ["Forward","Backward"]
        if not "RodSize" in pl:
            obj.addProperty("App::PropertyLength","RodSize","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The diameter or side of the rods"))
            obj.RodSize = 2
        if not "RodSections" in pl:
            obj.addProperty("App::PropertyInteger","RodSections","Truss",
                            QT_TRANSLATE_NOOP("App::Property","The number of rod sections"))
            obj.RodSections = 3
        if not "RodEnd" in pl:
            obj.addProperty("App::PropertyBool","RodEnd","Truss",
                            QT_TRANSLATE_NOOP("App::Property","If the truss has a rod at its endpoint or not"))
        if not "RodMode" in pl:
            obj.addProperty("App::PropertyEnumeration","RodMode","Truss",
                            QT_TRANSLATE_NOOP("App::Property","How to draw the rods"))
            obj.RodMode = rodmodes
        self.Type = "Truss"

    def onDocumentRestored(self,obj):

        ArchComponent.Component.onDocumentRestored(self,obj)
        self.setProperties(obj)

    def onChanged(self,obj,prop):

        ArchComponent.Component.onChanged(self,obj,prop)

    def execute(self,obj):

        if self.clone(obj):
            return

        import Part

        pl = obj.Placement

        # test properties
        if not obj.StrutWidth.Value or not obj.StrutHeight.Value:
            FreeCAD.Console.PrintLog(obj.Label+": Invalid strut size\n")
            return
        if not obj.HeightStart.Value or not obj.HeightEnd.Value:
            FreeCAD.Console.PrintLog(obj.Label+": Invalid truss heights\n")
            return
        if not obj.RodSections or not obj.RodSize.Value:
            FreeCAD.Console.PrintLog(obj.Label+": Invalid rod config\n")
            return
        if not obj.Base:
            FreeCAD.Console.PrintLog(obj.Label+": No base\n")
            return
        if (not hasattr(obj.Base,"Shape")) or (len(obj.Base.Shape.Vertexes) > 2):
            FreeCAD.Console.PrintLog(obj.Label+": No base edge\n")
            return

        # baseline
        v0 = obj.Base.Shape.Vertexes[0].Point
        v1 = obj.Base.Shape.Vertexes[-1].Point

        if obj.SlantType == "Simple":
            topstrut,bottomstrut,rods,angle = self.makeTruss(obj,v0,v1)
        else:
            v3 = v0.add((v1.sub(v0)).multiply(0.5))
            topstrut1,bottomstrut1,rods1,angle = self.makeTruss(obj,v0,v3)
            topstrut2,bottomstrut2,rods2,angle = self.makeTruss(obj,v1,v3)
            topstrut = topstrut1.fuse(topstrut2)
            topstrut = topstrut.removeSplitter()
            bottomstrut = bottomstrut1.fuse(bottomstrut2)
            bottomstrut = bottomstrut.removeSplitter()
            if obj.RodEnd:
                rods2 = rods2[:-1]
            rods = rods1 + rods2

        # mount shape
        shape = Part.makeCompound([topstrut,bottomstrut]+rods)
        shape = self.processSubShapes(obj,shape,pl)
        self.applyShape(obj,shape,pl)
        obj.TrussAngle = angle

    def makeTruss(self,obj,v0,v1):

        import Part

        # get normal direction
        normal = obj.Normal
        if not normal.Length:
            normal = FreeCAD.Vector(0,0,1)

        # create base profile
        maindir = v1.sub(v0)
        sidedir = normal.cross(maindir)
        if not sidedir.Length:
            FreeCAD.Console.PrintLog(obj.Label+": normal and base are parallel\n")
            return
        sidedir.normalize()
        p0 = v0.add(FreeCAD.Vector(sidedir).negative().multiply(obj.StrutWidth.Value/2))
        p1 = p0.add(FreeCAD.Vector(sidedir).multiply(obj.StrutWidth.Value/2).multiply(2))
        p2 = p1.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight))
        p3 = p0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight))
        trussprofile = Part.Face(Part.makePolygon([p0,p1,p2,p3,p0]))

        # create bottom strut
        bottomstrut = trussprofile.extrude(maindir)

        # create top strut
        v2 = v0.add(FreeCAD.Vector(normal).multiply(obj.HeightStart.Value))
        v3 = v1.add(FreeCAD.Vector(normal).multiply(obj.HeightEnd.Value))
        topdir = v3.sub(v2)
        if obj.StrutStartOffset.Value:
            v2f = v2.add((v2.sub(v3)).normalize().multiply(obj.StrutStartOffset.Value))
        else:
            v2f = v2
        if obj.StrutEndOffset.Value:
            v3f = v3.add((v3.sub(v2)).normalize().multiply(obj.StrutEndOffset.Value))
        else:
            v3f = v3
        offtopdir = v3f.sub(v2f)
        topstrut = trussprofile.extrude(offtopdir)
        topstrut.translate(v2f.sub(v0))
        topstrut.translate(FreeCAD.Vector(normal).multiply(-obj.StrutHeight.Value))
        angle = math.degrees(topdir.getAngle(maindir))

        # create rod profile on the XY plane
        if obj.RodType == "Round":
            rodprofile = Part.Face(Part.Wire([Part.makeCircle(obj.RodSize/2)]))
        else:
            rodprofile = Part.Face(Part.makePlane(obj.RodSize,obj.RodSize,FreeCAD.Vector(-obj.RodSize/2,-obj.RodSize/2,0)))

        # create rods
        rods = []
        bottomrodstart = v0.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value/2))
        toprodstart = v2.add(FreeCAD.Vector(normal).multiply(obj.StrutHeight.Value/2).negative())
        bottomrodvec = FreeCAD.Vector(maindir).multiply(1/obj.RodSections)
        toprodvec = FreeCAD.Vector(topdir).multiply(1/obj.RodSections)
        bottomrodpos = [bottomrodstart]
        toprodpos = [toprodstart]

        if obj.RodMode == rodmodes[0]:
            # /|/|/|
            for i in range(1,obj.RodSections+1):
                if (i > 1) or (obj.StrutStartOffset.Value >= 0):
                    # do not add first vert rod if negative offset
                    rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1]))
                bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec))
                toprodpos.append(toprodpos[-1].add(toprodvec))
                if obj.RodDirection == "Forward":
                    rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1]))
                else:
                    rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2]))

        elif obj.RodMode == rodmodes[1]:
            # /\/\/\
            fw = True
            for i in range(1,obj.RodSections+1):
                bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec))
                toprodpos.append(toprodpos[-1].add(toprodvec))
                if obj.RodDirection == "Forward":
                    if fw:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1]))
                    else:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2]))
                else:
                    if fw:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2]))
                    else:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1]))
                fw = not fw

        elif obj.RodMode == rodmodes[2]:
            # /|\|/|\
            fw = True
            for i in range(1,obj.RodSections+1):
                if (i > 1) or (obj.StrutStartOffset.Value >= 0):
                    # do not add first vert rod if negative offset
                    rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1]))
                bottomrodpos.append(bottomrodpos[-1].add(bottomrodvec))
                toprodpos.append(toprodpos[-1].add(toprodvec))
                if obj.RodDirection == "Forward":
                    if fw:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1]))
                    else:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2]))
                else:
                    if fw:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-2]))
                    else:
                        rods.append(self.makeRod(rodprofile,bottomrodpos[-2],toprodpos[-1]))
                fw = not fw

        # add end rod
        if obj.RodEnd:
            rods.append(self.makeRod(rodprofile,bottomrodpos[-1],toprodpos[-1]))

        # trim rods
        rods = [rod.cut(topstrut).cut(bottomstrut) for rod in rods]

        return topstrut,bottomstrut,rods,angle

    def makeRod(self,profile,p1,p2):

        """makes a rod by extruding profile between p1 and p2"""

        rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),p2.sub(p1))
        rod = profile.copy().translate(p1)
        rod = rod.rotate(p1,rot.Axis,math.degrees(rot.Angle))
        rod = rod.extrude(p2.sub(p1))
        return rod

class ViewProviderTruss(ArchComponent.ViewProviderComponent):

    "A View Provider for the Truss object"

    def __init__(self,vobj):

        ArchComponent.ViewProviderComponent.__init__(self,vobj)

    def getIcon(self):

        import Arch_rc
        return ":/icons/Arch_Truss_Tree.svg"

if FreeCAD.GuiUp:
    FreeCADGui.addCommand('Arch_Truss',CommandArchTruss())


# Understanding Types

# Syntax Reminder
class Calc:
  def __init__(self, a, b = 3):
    self.a = a
    self.b = b
  def do(self):
    return self.a + self.b

# Instance Creation
calc = Calc(5)
result = calc.do()
print(result)

# Python comes with a few built-in classes (or "types") that we've been using a lot so far
print(str, int, float, bool, list, dict)
# And some more less used ones
print(tuple, set, frozenset, bytes, bytearray)

# Python offers ways to create instances of these classes without calling them
'Hello World!', 6, 9.4, True, [], {}

# But that doesn't mean that we can't call their classes for additional functionality
print(str(True)) # 'True'
print(int('4')) # 4
print(float('.2')) # 0.2
print(bool(0)) # False
print(list('abcde')) # ['a', 'b', 'c', 'd', 'e']
print(dict()) # {}

# And just like custom class instances, they have methods built-into them.

x = ' Hello World!'
print(x)
x = x.lower()
print(x)
x = x.title()
print(x)
weird_x = x.replace('o', 'u')
print(weird_x)
l_count = x.count('l')
print(l_count)
words = x.split(' ')
print(words) # one empty string
x = x.strip()
print(x)
# And many more...

# A list of every attribute and function of each instance (or class) can be obtained with "dir".
print(dir(x))

# You can see information about what each method does by calling "help" on it
help(float.as_integer_ratio) # the interpreter will block until you press "q"

# Exercise - Given a string (ex: 'aesthetic') create an aesthetic string ('A E S T H E T I C').

# Answer - Long
x = 'aesthetic'
x = x.upper().split()
x = ' '.join(x)

# Answer - Short
x = 'aesthetic'
x = ' '.join(x).upper()

# Exercise - Merge two dictionaries into a new dictionary.
# Hint: Copy one of them, creating a completely new dictionary and then merge the other with it.

# Answer
a = {'one': 'thing'}
b = {'two': 2}

c = a.copy()
c.update(b)

# Just like custom classes, built-in ones are subject to inheritance and can be subclassed

class MyStr(str):
  def five_of(self, value):
    return self.count(value) == 5

# This is a "str" instance but not a "MyStr" instance so it doesn't have "five_of".
x = 'whoooooa'

x = MyStr('whoooooa')
print(x)
print(x.five_of('u'))
print(x.five_of('o'))

# Next Half

dir(int) # ['__add__', '__class__', '__contains__', '__delattr__', ...

# There are called "dunder", "special" or "magic" variables.
# They are always set (and usually managed by) python itself and dictate functionalities.

# For example, int.__gt__ is a method that tells us if a number is greater than another number
x = 5
print(x.__gt__(7))
print(x > 7)
# These are the same thing!

# We can overwrite these methods with inheritance to change their behaviour
# int.__add__ dictates what the + operator does
class MyInt(int):
  def __add__(self, other):
    return self - other

a = MyInt(9)
print(a)
b = a + 6
print(b) # normally 15 but it's 3

a = MyInt(4)
print(a)
b = 4 + a # 8, not 0! Because int's __add__ is used here.
print(b)

# There are a lot of dunder methods and attributes like these, not all are meant to be overwritten.
# The most common one to overwrite is the __init__ method! (You already know that).

# Truth is, every normal class is actually an implicit subclass of "object".
# When we create a class, we overwrite the object's __init__ method which normally does nothing.

class MyX(object):
  def __init__(self):
    self.x = 4

class MyX:
  def __init__(self):
    self.x = 4

# These are the same thing! In fact, older versions of pyhon always used the first notation.

# Exercise - Make a pokemon class that, when instances are added together, combine their stats.
# Stats should be "name"(str), "health"(int)
# "name" should be combined with a space using "str.join"
# "health" should be added normally
# Finally, return a new Pokemon class using self.__class__

# Answer

class Pokemon:
  def __init__(self, name, health):
    self.name = name
    self.health = health
  def __add__(self, other):
    # name
    name = ' '.join([self.name, other.name])
    # health
    health = self.health + other.health
    # why use self.__class__ instead of Pokemon directly?
    return self.__class__(name, health)
  def __repr__(self):
    # Do this after the exercise
    return f'Pokemon({self.name},{self.health})'

poke1 = Pokemon('Pikachu', 53)
print(poke1)
poke2 = Pokemon('Spinda', 12)
print(poke2)
poke3 = poke1 + poke2
print(poke3)

# We can add a lot together
poke4 = Pokemon('Bastiodon', 94) + Pokemon('Hattrem', 76) + Pokemon('Chansey', 41)
print(poke4)

# Exercise - Make a list with a ".add" method that creates, appends and returns a pokemon
# Hint: Use *args to supply the class with the method's arguments
# Extra - Make it so that the len() of the list only returns the amount of alive pokemon (health > 0)

class Party(list):
  def add(self, *args):
    poke = Pokemon(*args)
    self.append(poke)
    return poke
  def __len__(self):
    alive = []
    for poke in self:
      if poke.health > 0:
        alive.append(poke)
    return len(alive)

party = Party()
party.add('Venipede', 12)
party.add('Seedot', 30)
party.add('Kricketot', 0)
party.add('Drilbur', 32)
print(party)
print(len(party))

# Good practice? No! This is all for learning purposes.

# type(x) gives the class of an instance "x" (same as doing x.__class__)

type(party) # class 'Party'
type([]) # class 'list'
type(party.add) # class 'method'
type(Party.add) # class 'function'

# methods are bound to instances
# funcions are not, in fact, class-level functions are not bound to anything and can be used as-is

# So, as long as we pass the appropriate arguments, they should still work
x = []
Party.add(x, 'Sobble', 65)
print(x)

# Although x is not an instance of Party, it was a valid argument for add
# To understand this a bit better, go ahead and replace "self" with "x" in Party.add
# class functions are just wannabe methods, but are not methods yet!
# We could not have done this with an instance's .add method, because "self" is passed automatically
# So we would end up passing "self" AND "x", where "x" would take the spot of "name" (not cool)

# Exercise - Using your actual party instance, get the total amount of pokemon (not just alive)
# Careful: Do not use loops!

# Answer
total = list.__len__(party)


import logging

def set_logger(name: str, file_path: str)-> logging.GetLogger:

    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)

    formatter = logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s')
    stream_handler = logging.StreamHandler()
    file_handler = logging.FileHandler(file_path)

    stream_handler.setFormatter(formatter)
    file_handler.setFormatter(formatter)

    logger.addHandler(stream_handler)
    logger.addHandler(file_handler)

    return logger

if __name__ == '__main__':

    logger = set_logger('test_logger', 'test.log')
    logger.info('test')


import acquire
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings("ignore")
from sklearn.model_selection import train_test_split
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
import pandas_profiling
df = get_titanic_data()
profile = pandas_profiling.ProfileReport(df)
profile.get_rejected_variables(threshold=.9)
df.isnull().sum()
df.age.value_counts(bins=9, sort=True)
df.embarked.value_counts(dropna=False)
df.age.value_counts(dropna=False)
df.drop(columns=["deck"], inplace=True)
df.fillna(np.nan, inplace=True)
train, test = train_test_split(df, train_size=.8, random_state=123)
train.age.value_counts(dropna=False)
imp_mode = SimpleImputer(missing_values=np.nan, strategy="most_frequent")
imp_mode.fit(train[["embarked"]])
train["embarked"]=imp_mode.transform(train[["embarked"]])
test["embarked"]=imp_mode.transform(test[["embarked"]])
train.embarked.value_counts()
imp_median = SimpleImputer(missing_values=np.nan, strategy="median")
train['age'] = imp_median.fit_transform(train[['age']])
train.age.isnull().sum()
int_encoder = LabelEncoder()
int_encoder.fit(train.embarked)
train.embarked = int_encoder.transform(train.embarked)
train.embarked.value_counts()
embarked_array = np.array(train.embarked)
embarked_array[0:5]
embarked_array = embarked_array.reshape(len(embarked_array),1)
ohe = OneHotEncoder(sparse=False, categories="auto")
embarked_ohe = ohe.fit_transform(embarked_array)
embarked_test_ohe = ohe.transform(embarked_array)
embarked_test_ohe[0:5]


# based on gridworld.py and tictactoe.py

import datetime
import os
import sys
import logging
from collections import deque

import numpy as np
import torch
from gym.utils import seeding
#from webdriverwrapper import Chrome
#from selenium import webdriver
from .webdriver_imitation import WebDriverImitaion
#from webdriver_selenium import SeleniumWebDriver

NUMBER_ACTIONS = 7
MAX_STEPS = 100

WIN_REWARD, POS_REWARD, NEG_REWARD = 100, 1, 0 # 100, 1, 0

def negative_reward():
    #print("-1")
    return NEG_REWARD

def positive_reward():
    #print("+1")
    return POS_REWARD

class TestDriverEnviroment:
    """This is enviroment for a test driver
    """
    def __init__(self):

        self.solved_task = False

        self.env_size = (3, 5)
        self.step_count = 0
        self.total_steps = 0
        self.last_num_steps = 0
        self.history_steps = deque()
        self.seed()
        #self.board = numpy.zeros((3, 3)).astype(int)
        # Prepare the webdriver
        self.driver = WebDriverImitaion()
        #self.driver = SeleniumWebDriver(init_url_address="https://demo1.testgold.dev/login")

        # Init the state
        self.chosen_type = None     # 'clickables', 'selectables', 'enterables'
        self.chosen_number = None
        self.reset()

        self.possible_actions = [
            #"CLICK-N": "Click the N-th clickable element",
            #"ENTER-RND": "Enter random text",
            #"OPEN":     "Open the given website",
            (0, "NEXT",     "Go to the next active element"),
            (1, "CLICK",    "Click on the current element"),
            (2, "CHOOSE_FIRST_CLICK",  "Choose the firse clickable element"),
            (3, "ENTER",    "Enter DATA in the current element"),
            (4, "CHOOSE_FIRST_ENTER",  "Choose the firse enterable element"),
            (5, "SELECT",    "Select the current element"),
            (6, "CHOOSE_FIRST_SELECT", "Choose the firse selectable element"),
            #(7, "HIT",      "Hit the current element"),
            #(8, "VERIFY",   "Verify the current URL"),
            #(9, "CLOSE",    "Close the current page"),
            #(10, "WAIT",     "Wait 1 sec"),
        ]
        self.possible_actions = self.possible_actions[:NUMBER_ACTIONS]
        self.cmd_to_element_type = {
            "CHOOSE_FIRST_CLICK": "clickables",
            "CHOOSE_FIRST_SELECT": "selectables",
            "CHOOSE_FIRST_ENTER": "enterables",
            "CLICK": "clickables",
            "SELECT": "selectables",
            "ENTER": "enterables"
        }
        self.action_number_to_cmd = {i: x[1] for i, x in enumerate(self.possible_actions)}
        self.action_number_to_description = {i: x[2] for i, x in enumerate(self.possible_actions)}
        self.wins = 0
        self.losses = 0

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    #def to_play(self):
    #    return 0 if self.player == 1 else 1

    def reset(self):
        #self.board = numpy.zeros((3, 3)).astype(int)
        #self.state = [0]
        self.last_num_steps = self.step_count
        self.step_count = 0
        self.driver.reset()
        self.prv_state = None
        self.step_history = {'actions': [], 'commands': []}

        return self.get_observation()

    def step(self, action):
        """
        action (int or str)
        """
        self.step_count += 1
        self.total_steps += 1

        # Call the webdriver and perform the action
        if type(action) is str:
            cmd = action
        else:
            action = int(action)
            cmd = self.action_number_to_cmd[action]
            #print("action type:", type(action))
            #raise Exception("Wrong the action type")

        print("wins={}, cmd={}".format(self.wins, cmd))
        #print(cmd)

        #print("{}: wins={} (in {}), obs={} cmd={}".format(
        #    self.step_count, self.wins, self.last_num_steps, self.get_observation()[:15], cmd))

        self.step_history['actions'].append(action)

        #cmd = self.action_number_to_cmd[action]
        reward = 0
        site_elements = self.driver.get_site_elements()
        current_element = None

        if cmd == "WAIT":
            pass

        elif cmd in {"CHOOSE_FIRST_CLICK", "CHOOSE_FIRST_SELECT", "CHOOSE_FIRST_ENTER"}:
            self.chosen_type = self.cmd_to_element_type[cmd]
            if len(site_elements[self.chosen_type]) > 0:
                #current_element = site_elements[self.chosen_type][0]
                self.chosen_number = 0

                self.step_history['commands'].append((cmd,))

            else:
                reward = negative_reward()

        elif cmd == "NEXT":
            if self.chosen_number is None:
                reward = negative_reward()
            elif self.chosen_type:
                if len(site_elements[self.chosen_type]) > self.chosen_number + 1:
                    self.chosen_number += 1
                    print("self.chosen_number:", self.chosen_number)

                    self.step_history['commands'].append((cmd,))

                    #current_element = site_elements[self.chosen_type][self.chosen_number]
                else:
                    reward = negative_reward()
            else:
                reward = negative_reward()

        elif cmd in {"CLICK", "ENTER", "SELECT"}:
            if self.chosen_number is None:
                reward = negative_reward()
            elif self.chosen_type != self.cmd_to_element_type[cmd]:
                reward = negative_reward()
            elif (self.chosen_type and self.chosen_number < len(site_elements[self.chosen_type])):
                #reward = positive_reward()
                current_element = site_elements[self.chosen_type][self.chosen_number]
                if current_element is None: # perhaps, the element has been already used
                    reward = negative_reward()  # prevent clicking the same element twice
                else:
                    reward = positive_reward()

                    #print("current_element:", current_element)

                    element_number = self.chosen_number
                    #element_number = current_element # wrong!

                    if cmd == "CLICK":
                        self.driver.action_on_element("CLICK", element_number)
                        #self.driver.click(current_element)
                    elif cmd == "ENTER":
                        self.driver.action_on_element("ENTER", element_number, data="Hello world")
                        #self.driver.enter(current_element, data="Hello world")
                    elif cmd == "SELECT":
                        pass
                    # it works only if I put current_element instead of self.chosen_number

                    self.step_history['commands'].append((cmd, element_number))

                    if self.solved_task:
                        print("{} {}".format(cmd, self.chosen_number))

            else:
                reward = negative_reward()

        done = self.have_winner() or len(self.legal_actions()) == 0

        #reward = 1 if self.have_winner() else 0
        if self.have_winner():
            reward = WIN_REWARD * (1 - 0.9*(self.step_count / MAX_STEPS))
            self.wins += 1

            self.history_steps.append(self.step_count)
            if len(self.history_steps) > 100:
                self.history_steps.popleft()
            avg_steps = np.mean(self.history_steps)

            print("{}-th win in {} steps [{:.1f}]; reward={:.4f}".format(
                self.wins, self.step_count, avg_steps, reward))
            self.last_num_steps = self.step_count
            self.step_count = 0

            if avg_steps < 19.0:
                self.solved_task = True

            print("actions:", self.step_history['actions'])
            print("commands:", self.step_history['commands'])

        return self.get_observation_float(), reward, done, {}

    def get_observation(self):
        # It should return the current state as a numpy array of the float32 type
        # i.e. whole necessary information from the current webpage
        # including the list of active elements and so on.
        # probably, some additional information.
        # It will be fed to neural network.

        #board_player1 = numpy.where(self.board == 1, 1.0, 0.0)
        #board_player2 = numpy.where(self.board == -1, 1.0, 0.0)
        #board_to_play = numpy.full((3, 3), self.player).astype(float)
        #return numpy.array([board_player1, board_player2, board_to_play])

        #site_elements = {'clickables': ['Sign', 'Currency', 'Skip'], 'selectables': [], 'enterables': ['Your email']}
        site_elements = self.driver.get_site_elements()
        de_type = {0: 'clickables', 1: 'selectables', 2: 'enterables'}
        lengths = [len(site_elements[k]) for k in site_elements]

        (hight, width) = self.env_size
        # env_state shows which elements have been touched
        env_state = [[1 if i<lengths[j] and site_elements[de_type[j]][i] else 0 for i in range(width)] for j in range(len(lengths))]
        env_state = np.array(env_state, dtype=np.int32)

        # int_state shows which element is chosen right now
        int_state = np.zeros((hight, width), dtype=np.int32)
        de_type_to_number = {y:x for x,y in de_type.items()}
        if self.chosen_type is not None and self.chosen_number is not None:
            de_type_number = de_type_to_number.get(self.chosen_type)
            int_state[de_type_number, self.chosen_number] = 1

        if self.prv_state is None:
            self.prv_state = env_state

        #state = np.vstack([env_state, self.prv_state, int_state])

        state = np.vstack([env_state, int_state])
        state = np.expand_dims(state, axis=0) # 3-dim. array is required

        #print(state)

        #state = state.flatten()
        self.prv_state = env_state

        return state

    def get_observation_float(self):
        return np.array(self.get_observation(), dtype=np.float32)

    def legal_actions(self):
        legal = list(range(len(self.possible_actions)))
        return legal

    def have_winner(self):
        #observation = self.get_observation()
        #sum_obs = np.sum(observation[:self.env_size[0]*self.env_size[1]])
        #return True if sum_obs < 0.01 else False # if all_active_elements_have_been_clicked
        return self.driver.is_target_achieved()

    def render(self):
        print("Display the game observation")
        print(self.get_observation())

    def obs_size(self):
        return 3 * self.env_size[0] * self.env_size[1]

    def number_of_actions(self):
        return NUMBER_ACTIONS

if __name__ == "__main__":

    #im = Webdriver_imitation()

    env = TestDriverEnviroment()
    print(env.get_observation())
    print(env.obs_size())
    print(env.legal_actions())
    env.step("CHOOSE_FIRST_CLICK")
    env.step("NEXT")
    env.step("CLICK")
    env.step("CLICK")
    print(env.get_observation())


import math

sample_var_1 = float(input("Enter sample variance 1: "))
n_1 = float(input("Enter n 1: "))
v_1 = n_1 - 1

sample_var_2 = float(input("Enter sample variance 2: "))
n_2 = float(input("Enter n 2: "))
v_2 = n_2 - 1

c_lvl = float(input("Enter confidence level: "))

half_alpha = (1 - (c_lvl / 100)) / 2
half_alpha = round(half_alpha, 9)
print(f"half alpha = {half_alpha}")
print(f"v1 = {v_1}")
print(f"v2 = {v_2}")

f_half_alpha_left = float(input("Enter F point (left) according to half alpha, v1, v2: "))
f_half_alpha_right = float(input("Enter F point (right) according to half alpha, v1, v2: "))

# Bounds are for sigma2sq/sigma1sq (Reversed Form)

lower_bound_r = f_half_alpha_left * sample_var_2 / sample_var_1
upper_bound_r = f_half_alpha_right * sample_var_2 / sample_var_1

# Following are for sigma1sq/sigma2sq (Normal Form)

lower_bound_n = 1 / upper_bound_r
upper_bound_n = 1 / lower_bound_r

form = input("For which form do you want to get your CI? Type N for sigma1sq/sigma2sq, R for sigma2sq/sigma1sq: ")

if form == "N":
    print(f"L1,L2 = {lower_bound_n},{upper_bound_n}")
elif form == "R":
    print(f"L1,L2 = {lower_bound_r},{upper_bound_r}")
else:
    print("Invalid input!")


from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from .models import *
# Create your views here.

#admin Dashboard Loading

class AdminDashboard(View):
    def get(self,request):
        try:
            adminposts = ImagePosts.objects.all()
            context = {'posts':adminposts}
            return render(request,'index.html',context)
        except Exception as e:
            print(e)
    def post(self,request):
        try:
            postname = request.POST.get('postname')
            images = request.FILES
            desc = request.POST

            lend = len(images.getlist('image'))

            AdminPost.objects.get_or_create(post_name=postname)

            adminpost = AdminPost.objects.get(post_name=postname)
            for i in range(0,lend) :

                post = ImagePosts.objects.create(image_add=adminpost,images=images.getlist('image')[i],description=desc.getlist('desc')[i],tag=desc.getlist('tag')[i])

            return redirect('admindashboard')
        except Exception as e:
            print(e)

class LikedUsers(View):
    def get(self, request,id):
        post = ImagePosts.objects.get(id=id)

        statust = StatusTable.objects.filter(action=True,posts=post)

        return render(request,'likeduser.html',{'likedusers':statust})


import json
from typing import *

INPUT_FILE_NAME = "input"

def input_user_info() -> Union[Dict, bool]:
    try:
        with open(f"{INPUT_FILE_NAME}.json", "r", encoding="UTF-8") as f:
            input_data = json.load(f)

    except FileNotFoundError:
        # input.json 파일이 없음.
        with open(f"{INPUT_FILE_NAME}.json", "w", encoding="UTF-8") as f:
            f.write(
"""{
    "user_company" : "",
    "user_job" : "",
    "job_requirement" : "",
    "cover_letter" : "",
    "self_introduce" : ""
}"""
            )
        print("input.json을 열고, 정보를 작성하세요.")
        return False

    return input_data

def create_question_parser(question: str) -> List:
    question_list = question.replace("\n\n", "\n").split("\n")

    for quest_idx in range(len(question_list)):
        temp_str = question_list[quest_idx]
        for str_idx in range(len(temp_str)):
            if temp_str[str_idx] == ".":
                question_list[quest_idx] = temp_str[str_idx+1:].strip()
                break

    return question_list

def follow_up_question_parser(question: str) -> str:
    question.replace("\n", "")
    for str_idx in range(len(question)):
        if question[str_idx] == ":":
            return question[str_idx+1:].strip()
    return question

def remove_indent(string: str) -> str:
    return string.replace("    ", "").replace("\t", "")


import numpy as np
import pytest

from pandas import DataFrame, Series, concat
import pandas._testing as tm

@pytest.mark.parametrize("func", ["cov", "corr"])
def test_ewm_pairwise_cov_corr(func, frame):
    result = getattr(frame.ewm(span=10, min_periods=5), func)()
    result = result.loc[(slice(None), 1), 5]
    result.index = result.index.droplevel(1)
    expected = getattr(frame[1].ewm(span=10, min_periods=5), func)(frame[5])
    tm.assert_series_equal(result, expected, check_names=False)

@pytest.mark.parametrize("name", ["cov", "corr"])
def test_ewm_corr_cov(name):
    A = Series(np.random.randn(50), index=np.arange(50))
    B = A[2:] + np.random.randn(48)

    A[:10] = np.NaN
    B[-10:] = np.NaN

    result = getattr(A.ewm(com=20, min_periods=5), name)(B)
    assert np.isnan(result.values[:14]).all()
    assert not np.isnan(result.values[14:]).any()

@pytest.mark.parametrize("min_periods", [0, 1, 2])
@pytest.mark.parametrize("name", ["cov", "corr"])
def test_ewm_corr_cov_min_periods(name, min_periods):
    # GH 7898
    A = Series(np.random.randn(50), index=np.arange(50))
    B = A[2:] + np.random.randn(48)

    A[:10] = np.NaN
    B[-10:] = np.NaN

    result = getattr(A.ewm(com=20, min_periods=min_periods), name)(B)
    # binary functions (ewmcov, ewmcorr) with bias=False require at
    # least two values
    assert np.isnan(result.values[:11]).all()
    assert not np.isnan(result.values[11:]).any()

    # check series of length 0
    empty = Series([], dtype=np.float64)
    result = getattr(empty.ewm(com=50, min_periods=min_periods), name)(empty)
    tm.assert_series_equal(result, empty)

    # check series of length 1
    result = getattr(Series([1.0]).ewm(com=50, min_periods=min_periods), name)(
        Series([1.0])
    )
    tm.assert_series_equal(result, Series([np.NaN]))

@pytest.mark.parametrize("name", ["cov", "corr"])
def test_different_input_array_raise_exception(name):
    A = Series(np.random.randn(50), index=np.arange(50))
    A[:10] = np.NaN

    msg = "Input arrays must be of the same type!"
    # exception raised is Exception
    with pytest.raises(Exception, match=msg):
        getattr(A.ewm(com=20, min_periods=5), name)(np.random.randn(50))

def create_mock_weights(obj, com, adjust, ignore_na):
    if isinstance(obj, DataFrame):
        if not len(obj.columns):
            return DataFrame(index=obj.index, columns=obj.columns)
        w = concat(
            [
                create_mock_series_weights(
                    obj.iloc[:, i], com=com, adjust=adjust, ignore_na=ignore_na
                )
                for i, _ in enumerate(obj.columns)
            ],
            axis=1,
        )
        w.index = obj.index
        w.columns = obj.columns
        return w
    else:
        return create_mock_series_weights(obj, com, adjust, ignore_na)

def create_mock_series_weights(s, com, adjust, ignore_na):
    w = Series(np.nan, index=s.index)
    alpha = 1.0 / (1.0 + com)
    if adjust:
        count = 0
        for i in range(len(s)):
            if s.iat[i] == s.iat[i]:
                w.iat[i] = pow(1.0 / (1.0 - alpha), count)
                count += 1
            elif not ignore_na:
                count += 1
    else:
        sum_wts = 0.0
        prev_i = -1
        count = 0
        for i in range(len(s)):
            if s.iat[i] == s.iat[i]:
                if prev_i == -1:
                    w.iat[i] = 1.0
                else:
                    w.iat[i] = alpha * sum_wts / pow(1.0 - alpha, count - prev_i)
                sum_wts += w.iat[i]
                prev_i = count
                count += 1
            elif not ignore_na:
                count += 1
    return w

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
def test_ewm_consistency_mean(consistency_data, adjust, ignore_na, min_periods):
    x, is_constant, no_nans = consistency_data
    com = 3.0

    result = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).mean()
    weights = create_mock_weights(x, com=com, adjust=adjust, ignore_na=ignore_na)
    expected = (
        x.multiply(weights).cumsum().divide(weights.cumsum()).fillna(method="ffill")
    )
    expected[
        x.expanding().count() < (max(min_periods, 1) if min_periods else 1)
    ] = np.nan
    tm.assert_equal(result, expected.astype("float64"))

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
def test_ewm_consistency_consistent(consistency_data, adjust, ignore_na, min_periods):
    x, is_constant, no_nans = consistency_data
    com = 3.0

    if is_constant:
        count_x = x.expanding().count()
        mean_x = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).mean()
        # check that correlation of a series with itself is either 1 or NaN
        corr_x_x = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).corr(x)
        exp = x.max() if isinstance(x, Series) else x.max().max()

        # check mean of constant series
        expected = x * np.nan
        expected[count_x >= max(min_periods, 1)] = exp
        tm.assert_equal(mean_x, expected)

        # check correlation of constant series with itself is NaN
        expected[:] = np.nan
        tm.assert_equal(corr_x_x, expected)

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
def test_ewm_consistency_var_debiasing_factors(
    consistency_data, adjust, ignore_na, min_periods
):
    x, is_constant, no_nans = consistency_data
    com = 3.0

    # check variance debiasing factors
    var_unbiased_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).var(bias=False)
    var_biased_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).var(bias=True)

    weights = create_mock_weights(x, com=com, adjust=adjust, ignore_na=ignore_na)
    cum_sum = weights.cumsum().fillna(method="ffill")
    cum_sum_sq = (weights * weights).cumsum().fillna(method="ffill")
    numerator = cum_sum * cum_sum
    denominator = numerator - cum_sum_sq
    denominator[denominator <= 0.0] = np.nan
    var_debiasing_factors_x = numerator / denominator

    tm.assert_equal(var_unbiased_x, var_biased_x * var_debiasing_factors_x)

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
@pytest.mark.parametrize("bias", [True, False])
def test_moments_consistency_var(
    consistency_data, adjust, ignore_na, min_periods, bias
):
    x, is_constant, no_nans = consistency_data
    com = 3.0

    mean_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).mean()
    var_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).var(bias=bias)
    assert not (var_x < 0).any().any()

    if bias:
        # check that biased var(x) == mean(x^2) - mean(x)^2
        mean_x2 = (
            (x * x)
            .ewm(com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na)
            .mean()
        )
        tm.assert_equal(var_x, mean_x2 - (mean_x * mean_x))

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
@pytest.mark.parametrize("bias", [True, False])
def test_moments_consistency_var_constant(
    consistency_data, adjust, ignore_na, min_periods, bias
):
    x, is_constant, no_nans = consistency_data
    com = 3.0
    if is_constant:
        count_x = x.expanding(min_periods=min_periods).count()
        var_x = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).var(bias=bias)

        # check that variance of constant series is identically 0
        assert not (var_x > 0).any().any()
        expected = x * np.nan
        expected[count_x >= max(min_periods, 1)] = 0.0
        if not bias:
            expected[count_x < 2] = np.nan
        tm.assert_equal(var_x, expected)

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
@pytest.mark.parametrize("bias", [True, False])
def test_ewm_consistency_std(consistency_data, adjust, ignore_na, min_periods, bias):
    x, is_constant, no_nans = consistency_data
    com = 3.0
    var_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).var(bias=bias)
    std_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).std(bias=bias)
    assert not (var_x < 0).any().any()
    assert not (std_x < 0).any().any()

    # check that var(x) == std(x)^2
    tm.assert_equal(var_x, std_x * std_x)

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
@pytest.mark.parametrize("bias", [True, False])
def test_ewm_consistency_cov(consistency_data, adjust, ignore_na, min_periods, bias):
    x, is_constant, no_nans = consistency_data
    com = 3.0
    var_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).var(bias=bias)
    assert not (var_x < 0).any().any()

    cov_x_x = x.ewm(
        com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
    ).cov(x, bias=bias)
    assert not (cov_x_x < 0).any().any()

    # check that var(x) == cov(x, x)
    tm.assert_equal(var_x, cov_x_x)

@pytest.mark.parametrize("min_periods", [0, 1, 2, 3, 4])
@pytest.mark.parametrize("bias", [True, False])
def test_ewm_consistency_series_cov_corr(
    consistency_data, adjust, ignore_na, min_periods, bias
):
    x, is_constant, no_nans = consistency_data
    com = 3.0

    if isinstance(x, Series):
        var_x_plus_y = (
            (x + x)
            .ewm(com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na)
            .var(bias=bias)
        )
        var_x = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).var(bias=bias)
        var_y = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).var(bias=bias)
        cov_x_y = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).cov(x, bias=bias)
        # check that cov(x, y) == (var(x+y) - var(x) -
        # var(y)) / 2
        tm.assert_equal(cov_x_y, 0.5 * (var_x_plus_y - var_x - var_y))

        # check that corr(x, y) == cov(x, y) / (std(x) *
        # std(y))
        corr_x_y = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).corr(x, bias=bias)
        std_x = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).std(bias=bias)
        std_y = x.ewm(
            com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
        ).std(bias=bias)
        tm.assert_equal(corr_x_y, cov_x_y / (std_x * std_y))

        if bias:
            # check that biased cov(x, y) == mean(x*y) -
            # mean(x)*mean(y)
            mean_x = x.ewm(
                com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
            ).mean()
            mean_y = x.ewm(
                com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
            ).mean()
            mean_x_times_y = (
                (x * x)
                .ewm(
                    com=com, min_periods=min_periods, adjust=adjust, ignore_na=ignore_na
                )
                .mean()
            )
            tm.assert_equal(cov_x_y, mean_x_times_y - (mean_x * mean_y))


"""
Step 4.
To extract issue-code links.
"""

import ast
from hashlib import new
import git
import json
import os
import pandas
import sys
import utils

# enum
DIFF_LINES_AS_CODE = 0
METHODS_AS_CODE = 1

CODE_CONTENT = METHODS_AS_CODE

MIN_CODE_LINES_IN_A_COMMIT = 6

COUNT_REMOVED_FILES_LINES_FOR_MIN_LINES = True
COUNT_MODIFIED_FILES_REMOVED_LINES_FOR_MIN_LINES = True

# Commits with more files are ignored.
MAX_FILES_IN_A_COMMIT = 10

# Diffs with more lines of difference are ignored
MAX_DIFF_LINES = 1000

# A constraint for when METHODS_AS_CODE is used
# This is a constraint in the Java code.
#MINIMUM_METHOD_COMMIT_LINES = 1

def extract_data(input_issues_dir: str, input_code_dir: str, output_issue_code_dir: str):
    for input_file_name in os.listdir(input_issues_dir):
        input_file_path = os.path.join(input_issues_dir, input_file_name)
        if os.path.isfile(input_file_path) and input_file_path.endswith(".csv"):
            if os.path.exists(os.path.join(output_issue_code_dir, input_file_name)):
                continue
            repo_name = utils.discover_repo_address_from_issue_data(input_file_path, input_file_name)
            if repo_name is None:
                print("Could not find out the repo address of '" + input_file_name + "', skipping...")
                continue
            try:
                repo = git.Repo(os.path.join(input_code_dir, repo_name.replace('/', '_')))
            except:
                print(
                    "Could not open repo '"
                    + os.path.join(input_code_dir, repo_name.replace('/', '_'))
                    + "'. retrieve_code.py must clone repositories before running this script. Skipping this file..."
                )
                continue
            print("Processing " + repo_name +  " (" + input_file_name + ")...")
            data = pandas.read_csv(input_file_path)

            def count_commit_lines(diff: utils.CommitDiff):
                count = 0
                for item in diff.added_files:
                    lines = item.text.splitlines()
                    if len(lines) > MAX_DIFF_LINES:
                        continue
                    # Doesn't count empty lines
                    count += len([1 for line in lines if len(line.strip()) != 0])
                if COUNT_REMOVED_FILES_LINES_FOR_MIN_LINES:
                    for item in diff.removed_files:
                        lines = item.text.splitlines()
                        if len(lines) > MAX_DIFF_LINES:
                            continue
                        count += len([1 for line in lines if len(line.strip()) != 0])
                for item in diff.modified_files:
                    if len(item.added_lines) + len(item.removed_lines) > MAX_DIFF_LINES:
                        continue
                    if item.new_file_info.mime_type == "text/x-java-source":
                        # Doesn't count empty lines
                        count += len([1 for line in item.added_lines if len(line.line_text.strip()) != 0])
                    if COUNT_MODIFIED_FILES_REMOVED_LINES_FOR_MIN_LINES and item.old_file_info.mime_type == "text/x-java-source":
                        count += len([1 for line in item.removed_lines if len(line.line_text.strip()) != 0])
                return count

            def diff_line_count_filter(diff: utils.CommitDiff):
                return count_commit_lines(diff) >= MIN_CODE_LINES_IN_A_COMMIT

            def get_diffs_of_issue(x) -> list[utils.CommitDiff]:
                visited_commits = {}
                diffs: list[utils.CommitDiff] = []

                def add_diff(commit: git.Commit):
                    if len([f for f in commit.stats.files if f.endswith(".java")]) > MAX_FILES_IN_A_COMMIT:
                        return
                    new_diff = utils.CommitDiff(commit, mime_type="text/x-java-source")
                    for diff in diffs:
                        if new_diff.is_duplicate_of(diff):
                            #print("[TEST] Found a duplicate! " + diff.commit.hexsha + " and " + new_diff.commit.hexsha)
                            return
                    if (diff_line_count_filter(new_diff)):
                        diffs.append(new_diff)

                if type(x["pull_request_merge_commit_sha"]) == str: # First priority
                    commit_id = x["pull_request_merge_commit_sha"]
                    try:
                        commit = repo.commit(commit_id)
                        add_diff(commit)
                    except:
                        pass
                    visited_commits[commit_id] = True
                for item in ast.literal_eval(x["events_other_commit_events_and_urls"]): # Second priority
                    commit_id = utils.get_commit_id_from_url(item[item.find(':') + 1:])
                    if commit_id in visited_commits:
                        continue
                    try:
                        commit = repo.commit(commit_id)
                        add_diff(commit)
                    except:
                        pass
                    visited_commits[commit_id] = True
                for url in ast.literal_eval(x["events_merged_commit_urls"]): # Third priority
                    commit_id = utils.get_commit_id_from_url(url)
                    if commit_id in visited_commits:
                        continue
                    try:
                        commit = repo.commit(commit_id)
                        add_diff(commit)
                    except:
                        pass
                    visited_commits[commit_id] = True
                urls = ast.literal_eval(x["events_referenced_commit_urls"]) # Last priority
                for url in urls: # Last priority
                    commit_id = utils.get_commit_id_from_url(url)
                    if commit_id in visited_commits:
                        continue
                    try:
                        commit = repo.commit(commit_id)
                        add_diff(commit)
                    except:
                        pass
                    visited_commits[commit_id] = True
                return diffs

            output = []
            if CODE_CONTENT == DIFF_LINES_AS_CODE:
                for row in data.iterrows():
                    x = row[1]
                    diffs = get_diffs_of_issue(x)
                    for diff in diffs:
                        code = ""
                        for item in diff.added_files:
                            if len(item.text.splitlines()) > MAX_DIFF_LINES:
                                continue
                            code += item.text
                        for item in diff.removed_files:
                            if len(item.text.splitlines()) > MAX_DIFF_LINES:
                                continue
                            code += item.text
                        for item in diff.modified_files:
                            if len(item.added_lines) + len(item.removed_lines) > MAX_DIFF_LINES:
                                continue
                            if item.new_file_info.mime_type == "text/x-java-source" and item.old_file_info.mime_type == "text/x-java-source":
                                lines = item.added_lines + item.removed_lines
                                lines.sort(key=lambda x: x.line_index)
                                code += '\n'.join([line.line_text for line in lines])
                            elif item.new_file_info.mime_type == "text/x-java-source":
                                code += '\n'.join([line.line_text for line in item.added_lines])
                            elif item.old_file_info.mime_type == "text/x-java-source":
                                code += '\n'.join([line.line_text for line in item.removed_lines])
                        if len(code) != 0:
                            output.append(
                                {
                                    "issue_number": x["number"],
                                    "issue_title": x["title"],
                                    "issue_body": x["body"],
                                    "issue_comments": x["comments"],
                                    "issue_text": str(x["title"]) + '\n' + str(x["body"]),
                                    "commit_id": diff.commit.hexsha,
                                    "commit_message": diff.commit.message.strip(),
                                    "code": code
                                }
                            )
            elif CODE_CONTENT == METHODS_AS_CODE: # Needs to be processed in Java.
                for row in data.iterrows():
                    x = row[1]
                    diffs = get_diffs_of_issue(x)
                    for diff in diffs:
                        code = {}
                        code["added_files"] = []
                        code["removed_files"] = []
                        code["modified_files"] = []
                        for item in diff.added_files:
                            if len(item.text.splitlines()) > MAX_DIFF_LINES:
                                continue
                            added_file = {}
                            added_file["path"] = item.path
                            added_file["text"] = item.text
                            code["added_files"].append(added_file)
                        for item in diff.removed_files:
                            if len(item.text.splitlines()) > MAX_DIFF_LINES:
                                continue
                            removed_file = {}
                            removed_file["path"] = item.path
                            removed_file["text"] = item.text
                            code["removed_files"].append(removed_file)
                        for item in diff.modified_files:
                            if len(item.added_lines) + len(item.removed_lines) > MAX_DIFF_LINES:
                                continue
                            modified_file = {}
                            modified_file["old_mime_type"] = item.old_file_info.mime_type
                            modified_file["new_mime_type"] = item.new_file_info.mime_type
                            modified_file["old_path"] = item.old_file_info.path
                            modified_file["new_path"] = item.new_file_info.path
                            modified_file["old_text"] = item.old_file_info.text
                            modified_file["new_text"] = item.new_file_info.text
                            modified_file["removed_lines"] = [{ "index": line.line_index, "text": line.line_text } for line in item.removed_lines]
                            modified_file["added_lines"] = [{ "index": line.line_index, "text": line.line_text } for line in item.added_lines]
                            #modified_file["nonempty_removed_lines"] = [{ "index": line.line_index, "text": line.line_text } for line in item.removed_lines if len(line.line_text.strip()) != 0]
                            #modified_file["nonempty_added_lines"] = [{ "index": line.line_index, "text": line.line_text } for line in item.added_lines if len(line.line_text.strip()) != 0]
                            code["modified_files"].append(modified_file)
                        if len(code) != 0:
                            output.append(
                                {
                                    "issue_number": x["number"],
                                    "issue_title": x["title"],
                                    "issue_body": x["body"],
                                    "issue_comments": x["comments"],
                                    "issue_text": str(x["title"]) + '\n' + str(x["body"]),
                                    "commit_id": diff.commit.hexsha,
                                    "commit_message": diff.commit.message.strip(),
                                    "code": code
                                }
                            )

            output_lines = []
            for item in output:
                output_lines.append(json.dumps(item))
            output_file = open(os.path.join(output_issue_code_dir, repo_name.replace('/', '_') + "_0.jsonl"), "w+")
            output_file.write('\n'.join(output_lines))
            output_file.close()

def notice_code_content():
    print("----------------------------------------------------------------")
    if CODE_CONTENT == DIFF_LINES_AS_CODE:
        print("CODE_CONTENT is set to DIFF_LINES_AS_CODE.")
        print("The output files are final.")
    if CODE_CONTENT == METHODS_AS_CODE:
        print("CODE_CONTENT is set to METHODS_AS_CODE.")
        print("The next step is to process the output files with the Java program 'extract-data-java-methods'.")
    print("----------------------------------------------------------------")

def main():
    if len(sys.argv) != 4:
        print("Use: python extract_data.py issues-directory code-directory output-issue-code-data-directory")
        exit()
    notice_code_content()
    extract_data(sys.argv[1], sys.argv[2], sys.argv[3])
    notice_code_content()

if __name__ == "__main__":
    main()


'''
Numerik I für Ingenieurwissenschaften
Projekt: Wirbelpartikel
Barnes Hut Algorithmus
speed up for random vortex method
created 16/04/2021 by @V. Herbig
1) Build the Quad-Tree
2) For each free vortice: compute the total induced velocity by traversing the Quad-Tree
3) Update the vorticities and positions using Runge Kutta time integration (Collatz)
'''
##### --- IMPORTS --- #####
import math
import numpy as np
import matplotlib.pyplot as plt
import time

##### numba #####
from numba import njit, prange, float64, boolean, deferred_type, optional
from numba.experimental import jitclass

parameters = {'xtick.labelsize': 25, 'ytick.labelsize': 25,
          'legend.fontsize': 25, 'axes.labelsize': 25}
plt.rcParams.update(parameters)

##### --- FUNCTIONS --- ###
branch_type = deferred_type()
spec=[('xm',float64),
        ('ym',float64),
        ('D',float64),
        ('hasChild',boolean),
        ('child0',optional(branch_type)),
        ('child1',optional(branch_type)),
        ('child2',optional(branch_type)),
        ('child3',optional(branch_type)),
        ('hasLeaf',boolean),
        ('xLeaf',float64),
        ('yLeaf',float64),
        ('GLeaf',float64)]
@jitclass(spec)
class Branch:
    '''
    Branch Object
    Stores Vortices
    Parameters:
        xm, ym [float] - geometric center
        D [float] - diameter
    Features:
        children [branch object] - sub branches
        leaf properties [float] - position and circulation of single vortice
        If not hasLeaf, Leaf properties are Multipole properties
    '''
    def __init__(self, xm, ym, D):

        self.xm = xm
        self.ym = ym
        self.D = D

        self.hasChild = False
        self.child0 = None
        self.child1 = None
        self.child2 = None
        self.child3 = None
        # self.children = [None,None,None,None] # NW -+, SW --, SE +-, NE ++

        self.hasLeaf = False
        self.xLeaf = 0.0
        self.yLeaf = 0.0
        self.GLeaf = 0.0

branch_type.define(Branch.class_type.instance_type)

# @njit(fastmath=True, parallel=False)
def insertPoint(x,y,G,h,branch,diffusion_method=None):
    '''
    Insert a vortex into the tree using the following recursive procedure \\
    i) if branch does not contain a vortex, put vortex as leaf here \\
    ii) if branch is a child (internal branch), update the multipole coefficient and total vorticity. \\
        recursively insert the vortex in the appropriate quadrant
    iii) if branch is a leaf (external branch), then there are two vortices in the same region.
        subdivide the region further by creating children. then, recursively insert both vortices into the appropriate quadrant(s).
        since both vortices may still end up in the same quadrant, there may be several subdivisions during
        a single insertion. \\
        Finally, update the multipole coefficient (dependant on diffusion method) and total vorticity of branch
    Parameters:
        x, y, G [float] - position and circulation of current vortice
        h [float] - smoothing parameter
        branch [branch object] - specifies the current branch that is filled (initialize with "root")
        diffusion_method [string] - either viscous_vortex or vortex_exchange
    '''
    if not branch.hasChild and not branch.hasLeaf:
        branch.xLeaf = x
        branch.yLeaf = y
        branch.GLeaf = G
        branch.hasLeaf = True
    else:
        if branch.hasLeaf:
            if branch.D > 0.25*np.sqrt(h):
                if branch.xLeaf < branch.xm:
                    if branch.yLeaf < branch.ym: # SW
                        branch.child1 = Branch(branch.xm-branch.D/4,branch.ym-branch.D/4,branch.D/2)
                        insertPoint(branch.xLeaf,branch.yLeaf,branch.GLeaf,h,branch.child1,diffusion_method)
                    else: # NW
                        branch.child0= Branch(branch.xm-branch.D/4,branch.ym+branch.D/4,branch.D/2)
                        insertPoint(branch.xLeaf,branch.yLeaf,branch.GLeaf,h,branch.child0,diffusion_method)
                else:
                    if branch.yLeaf < branch.ym: # SE
                        branch.child2 = Branch(branch.xm+branch.D/4,branch.ym-branch.D/4,branch.D/2)
                        insertPoint(branch.xLeaf,branch.yLeaf,branch.GLeaf,h,branch.child2,diffusion_method)
                    else: # NE
                        branch.child3 = Branch(branch.xm+branch.D/4,branch.ym+branch.D/4,branch.D/2)
                        insertPoint(branch.xLeaf,branch.yLeaf,branch.GLeaf,h,branch.child3,diffusion_method)
            branch.hasLeaf = False
            branch.hasChild = True # is multipole

            if diffusion_method == 'viscous_vortex':
                branch.xLeaf = branch.xLeaf - branch.xm # switch from leaf-data to multipole-coefficient
                branch.yLeaf = branch.yLeaf - branch.ym # same as above
            else:
                branch.xLeaf = (branch.xLeaf - branch.xm) * branch.GLeaf # switch from leaf-data to multipole-coefficient
                branch.yLeaf = (branch.yLeaf - branch.ym) * branch.GLeaf # same as above

        if branch.D > 0.25*np.sqrt(h) or True:
            if x < branch.xm:
                if y < branch.ym:
                    if branch.child1 == None: # SW
                        branch.child1 = Branch(branch.xm-branch.D/4,branch.ym-branch.D/4,branch.D/2)
                    insertPoint(x,y,G,h,branch.child1,diffusion_method)
                else: # NW
                    if branch.child0== None:
                        branch.child0 = Branch(branch.xm-branch.D/4,branch.ym+branch.D/4,branch.D/2)
                    insertPoint(x,y,G,h,branch.child0,diffusion_method)
            else:
                if y < branch.ym: # SE
                    if branch.child2 == None:
                        branch.child2 = Branch(branch.xm+branch.D/4,branch.ym-branch.D/4,branch.D/2)
                    insertPoint(x,y,G,h,branch.child2,diffusion_method)
                else: # NE
                    if branch.child3 == None:
                        branch.child3= Branch(branch.xm+branch.D/4,branch.ym+branch.D/4,branch.D/2)
                    insertPoint(x,y,G,h,branch.child3,diffusion_method)
            branch.hasChild = True

        if diffusion_method == 'viscous_vortex':
            branch.xLeaf += x-branch.xm  # distance to geometric center
            branch.yLeaf += y-branch.ym
        else:
            branch.xLeaf += (x-branch.xm) * G # distance to geometric center
            branch.yLeaf += (y-branch.ym) * G
        branch.GLeaf += G # total vorticity increases

@njit(fastmath=True, parallel=False)
def create_tree(elements,buffer=0.05,name=None):
    '''
    Create the base branch for a list of elements, with complex position as entries
    Geometric center is decided by max distance between elements
    Parameters:
        elements [list, complex] - list for which tree to create
        buffer [float] - parameter that slightly enlarges the branch diameter
        name [string] - option for debugging causes
    '''
    x_dist = math.sqrt((min(elements.real)-max(elements.real))**2) #math.hypot(min(elements.real),max(elements.real))
    y_dist = math.sqrt((min(elements.imag)-max(elements.imag))**2) #math.hypot(min(elements.imag),max(elements.imag))
    D = max(x_dist,y_dist)
    x_m = min(elements.real) + x_dist / 2
    y_m = min(elements.imag) + y_dist / 2

    tree = Branch(x_m,y_m,D+buffer)
    if name is not None:
        tree.name = name

    return tree

def draw_tree(branch):
    '''
    Recursively draws all sub-branches of given branch
    Parameters:
        branch [branch object] - branch to draw
    '''
    plt.plot([branch.xm-branch.D/2,branch.xm-branch.D/2,branch.xm+branch.D/2,branch.xm+branch.D/2,branch.xm-branch.D/2],[branch.ym-branch.D/2,branch.ym+branch.D/2,branch.ym+branch.D/2,branch.ym-branch.D/2,branch.ym-branch.D/2])
    plt.axis('equal')
    for child in [branch.child0,branch.child1,branch.child2,branch.child3]:
        if child is not None:
            draw_tree(child)

##### vortex exchange #####
@njit(fastmath=True, parallel=False)
def calculations(x1,x2,y1,y2,G2,h,multi_flag=False,multi_x=0.0,multi_y=0.0,gammas_flag=False,G1=0.0,visco=0.0):
    '''
    Calculate velocity and vorticity induced from one vortice one the other \\
    Vortice 1 is the one on which velocity is induced \\
    If a multipole is considered (multi_flag == True), the velocity is approximated via "Multipolentwicklung"
        multi_x is then equivalent to the sum over all distances from vortices to geometric center multiplied with each circulation
        multi_y analog
        G2 is then equivalent to \Gamma_i 1_i, i.e. the sum over all gammas in that branch
    If relation of free and boundary vortices is considered no vorticity is exchanged (gammas_flag == False)
    Parameters:
        x1, y1 [float] - position of vortice on which velocity is induced
        x2, y2 [float] - position of vortice or cluster which induces velocity
        G2 [float] - circulation of vortice or cluster which induces velocity
        h [float] - smoothing parameter
        multi_flag [bool] - True, if velocity should be approximated via multipole
        multi_x, multi_y [float] - multipol coefficients of cluster which induces velocity (use xLeaf and yLeaf)
        gammas_flag [bool] - True, if vortice exchange influence on vorticity should be calculated
        G1 [float] - circulation of vortice on which velocity is induced
        visco [float] - viscosity, determined by Re
    Returns:
        Induced velocity, Changed omega (is None if not gammas_flag)
    '''
    dx = x1 - x2
    dy = y1 - y2
    d2 = dx**2 + dy**2 + h

    U = - G2 * dy/(2*np.pi * d2)
    V = G2 * dx/(2*np.pi * d2)

    if multi_flag:
        # U -= (1/(2*np.pi)) * (1/(d2**2)) * (2*dx*dy * multi_x + (- dx**2 + dy**2 - h) * multi_y)
        # V -= (1/(2*np.pi)) * (1/(d2**2)) * ((- dx**2 + dy**2 + h) * multi_x - 2*dx*dy * multi_y)
        U = -multi_x * 2 * dx*dy
        U -= multi_y * (-dx**2 + dy**2 -h)
        U /= (d2+h)
        U -= G2 * dy
        U /= 2*np.pi * (d2+h)

        V = multi_y * 2 * dx*dy
        V -= multi_x * (-dx**2 + dy**2 + h)
        V /= (d2+h)
        V += G2 * dx
        V /= 2*np.pi * (d2+h)

    w = None
    if gammas_flag:
        w = (G2-G1) * h**2 * visco * 4 * (5*d2 - 6*h)/((d2)**4)

    return U+1j*V, w

@njit(fastmath=True, parallel=False)
def barnes_exchange(x,y,h,G,branch,visco,alpha):
    '''
    Walk through entire branch and compute induced velocity and vorticity on current vortice, the latter using vortex exchange \\
    Parameters:
        x, y, G [float] - position and vorticity of current vortice \\
        branch [branch object] - specifies the current branch that is checked (initialize with "root") \\
        alpha [float] - multipole acceptance criterion
    Returns:
        Induced velocity, Changed omega
    '''
    if branch.hasLeaf:
        vel, W = calculations(x,branch.xLeaf,y,branch.yLeaf,branch.GLeaf,h,multi_flag=False,gammas_flag=True,G1=G,visco=visco)
        return vel, W

    dist = ((x-branch.xm)**2)+((y-branch.ym)**2) # calculate distance to geometric center of branch
    if (branch.D)**2/dist < alpha**2: # maybe add h to prevent division by 0 in case vortice and weighted center of branch are very close
        vel, W = calculations(x,branch.xm,y,branch.ym,branch.GLeaf,h,multi_flag=True,multi_x=branch.xLeaf,multi_y=branch.yLeaf,gammas_flag=True,G1=G,visco=visco)
        return vel, W

    vel = 0.0 + 0.0j
    W = 0.0
    for child in [branch.child0,branch.child1,branch.child2,branch.child3]: #branch.children:
        if child is not None:
            vel_p, W_p = barnes_exchange(x,y,h,G,child,visco,alpha)
            vel += vel_p
            W += W_p

    return vel, W

# @njit(fastmath=True, parallel=False)
def barnes_collatz(vortices, bd_vortices, vel_inf, dt, h, gammas, bd_gammas, N, visco, alpha):
    ### first collatz step ###
    # create initial trees
    root_half = create_tree(vortices)
    root_bd = create_tree(bd_vortices)

    # insert all free vortices in one and all boundary vortices in the other tree
    # tic = time.time()
    for i in prange(len(vortices)):
        insertPoint(vortices[i].real,vortices[i].imag,gammas[i],h,root_half,diffusion_method='exchange')
    # toc = time.time()
    # runtime = toc-tic
    # print("Time to insert {} particles in octree is {}s." .format(len(vortices), toc-tic))
    for k in prange(len(bd_vortices)):
        insertPoint(bd_vortices[k].real,bd_vortices[k].imag,bd_gammas[k],h,root_bd,diffusion_method='exchange')

    # half step
    u_half = np.zeros((len(vortices)), dtype = np.complex128)
    w_gamma_half = np.zeros((len(vortices)), dtype = np.float64)
    for j in prange(len(vortices)):
        u_f, w_f = barnes_exchange(vortices[j].real,vortices[j].imag,h,gammas[j],root_half,visco,alpha)
        u_half[j] += u_f
        w_gamma_half[j] += w_f
        u_bd, w_bd = barnes_exchange(vortices[j].real,vortices[j].imag,h,gammas[j],root_bd,visco,alpha)
        u_half[j] += u_bd

    mid_points = vortices + (u_half + vel_inf)*0.5*dt
    mid_gammas = gammas + w_gamma_half*0.5*dt

    ### second collatz step ###
    # create mid tree for free vortices
    root_full = create_tree(mid_points)

    # insert all mid points
    for i in prange(len(mid_points)):
        insertPoint(mid_points[i].real,mid_points[i].imag,gammas[i],h,root_full,diffusion_method='exchange')

    # draw_tree(root_bd)
    # plt.show()

    u_full = np.zeros((len(mid_points)), dtype = np.complex128)
    w_gamma_full = np.zeros((len(vortices)), dtype = np.float64)
    for j in prange(len(mid_points)):
        u_f, w_f = barnes_exchange(mid_points[j].real,mid_points[j].imag,h,gammas[j],root_full,visco,alpha)
        u_full[j] += u_f
        w_gamma_full[j] += w_f
        u_bd, w_bd = barnes_exchange(mid_points[j].real,mid_points[j].imag,h,gammas[j],root_bd,visco,alpha)
        u_full[j] += u_bd

    vortices += (u_full + vel_inf)*dt
    gammas += w_gamma_full*dt

    return vortices, gammas #, runtime

##### viscous vortex #####
def calc_omega(x,y,h,branch,alpha):
    '''
    Calculate omega used in Viscous Vortex Domain
    Parameters:
        x, y [float] - position of current vortice
        h [float] - smoothing parameter
        branch [branch object] - specifies the current branch that is checked (initialize with "root")
        alpha [float] - multipole acceptance criterion
    Returns:
        omega [float]
    '''
    if branch.hasLeaf:
        dx = x - branch.xLeaf
        dy = y - branch.yLeaf
        d2 = (dx**2 + dy**2 + h)**2
        omega = branch.GLeaf/d2
        return omega

    dist = ((x-branch.xm)**2)+((y-branch.ym)**2) # calculate distance to geometric center of branch
    if (branch.D)**2/dist < alpha**2: # maybe add h to prevent division by 0 in case vortice and weighted center of branch are very close
        dx = x - branch.xm
        dy = y - branch.ym
        d2 = (dx**2 + dy**2 + h)**2
        omega = (dx**2 + dy**2 + h)**2
        return omega

    omega = 0.0
    for child in [branch.child0,branch.child1,branch.child2,branch.child3]: #branch.children:
        if child is not None:
            omega_p = calc_omega(x,y,h,child,alpha)
            omega += omega_p

    return omega

def barnes_vv(x,y,h,branch,visco,alpha,omega):
    '''
    Walk through entire branch and compute induced velocity using viscous vortex domains \\
    Parameters:
        x, y [float] - position of current vortice \\
        h [float] - smoothing parameter
        branch [branch object] - specifies the current branch that is checked (initialize with "root") \\
        visco [float] - viscosity, determined by Re
        alpha [float] - multipole acceptance criterion
        omega [float] - vorticity used in viscous vortex domains
    Returns:
        Induced velocity, Changed omega
    '''
    if branch.hasLeaf:
        dx = x - branch.xLeaf
        dy = y - branch.yLeaf
        d2 = dx**2 + dy**2 + h
        U = (visco/omega)*(branch.GLeaf * 4 * dx/((d2)**3)) - (branch.GLeaf/(2*np.pi)) * (dy/d2)
        V = (visco/omega)*(branch.GLeaf * 4 * dy/((d2)**3)) + (branch.GLeaf/(2*np.pi)) * (dx/d2)
        return U + 1j*V

    dist = ((x-branch.xm)**2)+((y-branch.ym)**2) # calculate distance to geometric center of branch
    if (branch.D)**2/dist < alpha**2: # maybe add h to prevent division by 0 in case vortice and weighted center of branch are very close
        dx = x - branch.xm
        dy = y - branch.ym
        d2 = dx**2 + dy**2 + h
        U = (visco/omega)*(branch.GLeaf * 4 * dx/((d2)**3)) - (branch.GLeaf/(2*np.pi)) * (dy/d2)
        U -= (visco/omega**2) * (-4 * branch.GLeaf * (dx / d2**3))**2
        U -= (visco/omega) * (((h-5*dx**2+dy**2)/ d2**4) * branch.xLeaf - ((6*dx*dy)/ d2**4) * branch.yLeaf)

        V = (visco/omega)*(branch.GLeaf * 4 * dy/((d2)**3)) + (branch.GLeaf/(2*np.pi)) * (dx/d2)
        V -= (visco/omega**2) * (-4 * branch.GLeaf * (dy / d2**3))**2
        V -= (visco/omega) * (((h-5*dy**2+dx**2)/ d2**4) * branch.yLeaf - ((6*dx*dy)/ d2**4) * branch.xLeaf)
        return U + 1j*V

    vel = 0.0 + 0.0j
    for child in [branch.child0,branch.child1,branch.child2,branch.child3]: #branch.children:
        if child is not None:
            vel_p = barnes_vv(x,y,h,child,visco,alpha,omega)
            vel += vel_p
    return vel

def barnes_collatz_vv(vortices, bd_vortices, vel_inf, dt, h, gammas, bd_gammas, N, visco, alpha):
    elements = np.hstack((vortices, bd_vortices))
    gammas = np.hstack((gammas, bd_gammas))

    root_half = create_tree(elements)

    for i in prange(len(elements)):
        insertPoint(elements[i].real,elements[i].imag,gammas[i],h,root_half,diffusion_method='viscous_vortex')

    u_half = np.zeros((len(vortices)), dtype = np.complex128)
    for j in prange(len(vortices)):
        omega_half = calc_omega(vortices[j].real,vortices[j].imag,h,root_half,alpha)
        u_v = barnes_vv(vortices[j].real,vortices[j].imag,h,root_half,visco,alpha,omega_half)
        u_half[j] += u_v

    mid_points = vortices + (u_half + vel_inf)*0.5*dt

    ### second collatz step ###
    mid_elements = np.hstack((mid_points, bd_vortices))
    root_full = create_tree(mid_elements)

    for i in prange(len(mid_elements)):
        insertPoint(mid_elements[i].real,mid_elements[i].imag,gammas[i],h,root_full,diffusion_method='viscous_vortex')

    u_full = np.zeros((len(mid_points)), dtype = np.complex128)
    for j in prange(len(mid_points)):
        omega_full = calc_omega(mid_points[j].real,mid_points[j].imag,h,root_full,alpha)
        u_v = barnes_vv(mid_points[j].real,mid_points[j].imag,h,root_full,visco,alpha,omega_full)
        u_full[j] += u_v

    vortices += (u_full + vel_inf)*dt

    return vortices


from thing import *

class Computer (Thing):
    def __init__ (self,name,loc,desc):
        Thing.__init__(self,name,loc,desc)

    def use (self, actor):
        actor.say('I crank up the ol\' computer')
        stuff = actor.check_pocket()
        hws = [x for x in stuff if ('hw-' in x.name() and 'done-' not in x.name())]
        if hws:
            for hw in hws:
                actor.say('I work on ' + hw.name())
                hw.do_hw()
        else:
            actor.say('I do not seem to be carrying anything that needs work')


# Given a collection of intervals, merge all overlapping intervals.
#
# Example 1:
#
# Input: [[1,3],[2,6],[8,10],[15,18]]
# Output: [[1,6],[8,10],[15,18]]
# Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
# Example 2:
#
# Input: [[1,4],[4,5]]
# Output: [[1,5]]
# Explanation: Intervals [1,4] and [4,5] are considerred overlapping.

# Definition for an interval.
# class Interval(object):
#     def __init__(self, s=0, e=0):
#         self.start = s
#         self.end = e

class Solution(object):
    def merge(self, intervals):
        """
        :type intervals: List[Interval]
        :rtype: List[Interval]
        """

        # Credits -> https://leetcode.com/problems/merge-intervals/solution/

        # If we sort the intervals by their start value,
        # then each set of intervals that can be merged will appear
        # as a contiguous "run" in the sorted list.

        # Then, we insert the first interval into our merged list and continue
        # considering each interval in turn as follows: If the current interval
        # begins after the previous interval ends, then they do not overlap and
        # we can append the current interval to merged. Otherwise, they do overlap,
        # and we merge them by updating the end of the previous interval if it is
        # less than the end of the current interval.

        # Sort the list by start times
        intervals.sort(key=lambda x:x.start)

        merged = []

        for item in intervals:
            # If merged list is empty or if the current interval
            # under considertaion does not overlap with the last
            # item in the merged list, just append this interval to the merged list
            if len(merged) == 0 or merged[-1].end < item.start:
                merged.append(item)

            else:
                # This means that there is overlap
                merged[-1].end = max(merged[-1].end, item.end)

        return merged


def get_ranges(line):
    left, right = line.split(",")
    return (get_range(left), get_range(right))

def get_range(assignments):
    start, end = assignments.split("-")
    start, end = int(start), int(end) + 1
    return range(start, end)

def main():
    subset_pairs = 0
    with open("input.txt", "r") as f:
        for line in f:
            line = line.rstrip()
            left_range, right_range = get_ranges(line)
            left_set = set(left_range)
            right_set = set(right_range)
            if left_set.issubset(right_set) or right_set.issubset(left_set):
                subset_pairs += 1

    print(subset_pairs)

if __name__ == "__main__":
    main()