from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import UserSerializer,VerifySerializer
from .emails import send_otp
from .models import User
# Create your views here.

class RegisterApi(APIView):

    def post(self,request):
        try:
            data=request.data
            serializer=UserSerializer(data=data)
            if serializer.is_valid():
                serializer.save()
                send_otp(serializer.data['email'])
                return Response({
                    'status':200,
                    'message':'User Registered Successfully,check email',
                    'data':serializer.data
                })
            return Response({
            'status':400,
            'message':'User Registration Failed',
            'data':serializer.errors
        })
        except Exception as e:
            return Response({
                'status':400,
                'message':'User Registration Failed',
                'data':str(e)
            })

class VerifyApi(APIView):

    def post(self,request):
        try:
            data=request.data
            serializer = VerifySerializer(data=request.data)

            if serializer.is_valid():
                email = serializer.data['email']
                otp = serializer.data['otp']
                user = User.objects.filter(email=email)
                if not user.exists():
                    return Response({
                        'status':400,
                        'message':'User Verification unsuccessfully',
                        'data':'invalid email'
                    })
                if not user[0].otp == otp:
                    user.update(is_verified=True)
                    return Response({
                        'status':400,
                        'message':'User Verification unsuccessfully',
                        'data':'invalid otp'
                    })
                user = user.first()
                user.is_verified = True
                user.save()
                return Response({
                        'status':200,
                        'message':'User Verification successfully account created',
                        'data':{}
                    })

        except Exception as e:
            return Response({
                'status':400,
                'message':'User Verification Failed',
                'data':str(e)
            })


from collections import defaultdict
from typing import List

"""
https://leetcode.cn/problems/advantage-shuffle/
"""
def advantageCount(nums1: List[int], nums2: List[int]) -> List[int]:
    """
    贪心: 让nums1[i]比nums2[i]大, 但是尽可能让nums1[i]小。

    两个数组排序。
    遍历nums1, 每次取最小值:
    1. 如果最小值比nums2的最小值大, 则形成对应关系, nums2的左指针右移。
    2. 如果最小值比nums2的最小值小, 则最小值没用了, 先记录, 后面可以随机对应(反正也是数)。

    所以要有哈希表, 记录对应关系, 以及nums1中没用的数据。
    """
    sorted_nums1 = sorted(nums1)
    sorted_nums2 = sorted(nums2)

    # 记录nums2中的元素和nums1元素的对应关系
    # 因为可能有重复元素, 所以有数组
    pair = defaultdict(list)
    # 记录nums1中没用的数
    not_used = []

    i = 0
    for v in sorted_nums1:
        if v > sorted_nums2[i]:
            pair[sorted_nums2[i]].append(v)
            i += 1
        else:
            not_used.append(v)

    # 根据记录，构造结果
    res = []
    for v in nums2:
        p = pair[v]
        # 有优势对
        if len(p) > 0:
            res.append(p.pop())
        # 没有优势对, 从没用的数中随便取一个
        else:
            res.append(not_used.pop())
    return res


from configparser import ConfigParser
from generic.seleniumbase import SeleniumBase

class Base(SeleniumBase):
    def read_config_file(self,file_name,section_name):
        cp=ConfigParser()
        cp.read("./configuration/"+file_name)

        ll=cp.items(section_name)
        dd={}
        for tt in ll:
            key=tt[0]
            value=tt[1]
            dd[key]=value
        return dd


import os
import matplotlib.pyplot as plt
l1 = []
l2 = []
n = int(input("Enter the number of values for X and Y"))
i=0
for i in range(0,n):
    print("Enter the ",i,"th value of X")
    el = int(input())
    l1.append(el)
for i in range(0,n):
    print("Enter the ",i,"th value of Y")
    ele = int(input())
    l2.append(ele)
print("Values of X are")
print(l1)
print("Values of Y are")
print(l2)
sx=sy=sxy=sqx=sqy=0
mlxy = []
sqlx = []
sqly = []
for i in range(0,n):
          sx = l1[i] + sx
          sy = l2[i] + sy
          mlxy.append(l1[i]*l2[i])
          sqlx.append(l1[i]*l1[i])
          sqly.append(l2[i]*l2[i])
          sqx = sqlx[i] + sqx
          sqy = sqly[i] + sqy
          sxy = mlxy[i] + sxy
a = ((sy*sqx) - (sx*sxy))/((n*sqx) - (sx*sx))
b = ((n*sxy) - (sx*sy))/((n*sqx) - (sx*sx))
print("The LINEAR REGRESSION EQUATION of X and Y is : Y = ",a,"+",b,"X")
x = np.linspace(-5,-5,100)
y = a + b*x
plt.plot(x, y, '-r', label='y=a+bx')
plt.title('Graph of y=a+bx')
plt.xlabel('x', color='#1C2833')
plt.ylabel('y', color='#1C2833')
plt.grid()
plt.show()


'''
Problem ID:
PE0005

Problem Title:
Smallest multiple

Problem Description:
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?

Problem Solution:
232792560
'''

def isMultiple(x,y):
    result = False
    if x%y == 0:
        result = True
    return result

def solution():
    result = 1
    for k in range(1,1+20):
        if not(isMultiple(result,k)):
            if isMultiple(2*result,k):
                result *= 2
            elif isMultiple(3*result,k):
                result *= 3
            elif isMultiple(5*result,k):
                result *= 5
            else:
                result *= k
    return result

print(solution())


class ProcessList:
    def __init__(self, value=[]) -> None:
        self.value = value

    def push(self, process_to_push):
        self.value.append(process_to_push)

    # Debug only
    def print_list(self):
        for index in range(len(self.value)):
            print(f"----- Processo {index + 1} -----")
            print(f"Tempo de chegada: {self.value[index].arrive_time}")
            print('-------------------------------')

    def show_ready_processes(self):
        ready_processes = []
        for process in self.value:
            if (process.ready == True):
                ready_processes.append(process)

        print('Fila de Prontos: ', end="")
        for index in range(len(ready_processes)):
            if (index + 1 == len(ready_processes)):
                print(f'P{ready_processes[index].id}.', end="\n")
            else:
                print(f'P{ready_processes[index].id}, ', end="")

    def is_there_any_ready_process(self):
        ready_processes = []
        for process in self.value:
            if (process.ready == True):
                ready_processes.append(process)

        if (len(ready_processes) > 0):
            return True

        else:
            return False

    def order_by_arrive_time(self):
        self.value.sort(key=lambda process: process.arrive_time, reverse=False)

    def order_by_arrive_time_and_execution_time(self):
        data_list = self.value
        new_list = []

        while data_list:
            minimum = data_list[0]
            for process in data_list:
                if process.arrive_time < minimum.arrive_time or (process.arrive_time >= minimum.arrive_time and process.execution_time < minimum.execution_time):
                    minimum = process
            new_list.append(minimum)
            data_list.remove(minimum)

        self.value = new_list


import streamlit as st
import data as dt

import numpy as np
import pandas as pd

from streamlit_searchbox import st_searchbox

def title():
    """
    Welcome title for producers
    """
    st.markdown("# Licensee Page \n ---")

def select_options():
    option = st.selectbox(
        "Producer Name",
        dt.get_licensees(),
        index=1
    )

    st.markdown("---")
    st.markdown("### Product - Variety List")

    # Show vareity
    unique_key = 0
    if not (option is None):
        for _, row in dt.get_prods_from_licensee(option).iterrows():
            st.markdown("---")
            # Split into 4 columns
            cols = st.columns(4)
            with cols[0]:
                # Check box key
                key_str = "check_" + str(row["BioGro #"]) + '_' + str(unique_key)
                checked = st.checkbox("Availability", value = row['Available'], key=key_str)
            with cols[1]:
                st.markdown(f"Product : {row['Product']}")
            with cols[2]:
                st.markdown(f"Variety : {row['Variety']}")
            with cols[3]:
                if (type(row["Store #"]) == float and pd.isna(row["Store #"])) or row["Store #"] == []:
                    st.markdown(f"Stores : None")
                else:
                    st.markdown("Stores : ")
                    if type(row["Store #"]) == list:
                        for s in row['Store #']:
                            st.markdown(s)
                    else:
                        st.markdown(row["Store #"])

            # Next line searchbar
            if checked:
                selected = select_stores(row, key_str)
                dt.get_all_products().at[row["UID"], "Store #"] = selected

                if row["Available"] == False:
                    dt.get_all_products().at[row["UID"], "Available"] = True

            else:
                if row['Available'] == True:
                    dt.get_all_products().at[row["UID"], "Available"] = False
            unique_key += 1

def select_stores(row, key_str: str):
    """
    Function to select stores from a list of a searchbox
    """
    # Special key for multiselect
    key_str = "multi" + key_str[5:]

    if type(row["Store #"]) == float and pd.isna(row["Store #"]):
        selected = st.multiselect('Select Stores', dt.get_store_names(), key=key_str)
    else:
        selected = st.multiselect('Select Stores', dt.get_store_names(), row["Store #"], key=key_str)
    # st.write(selected)
    return selected

def draw_all():
    title()
    select_options()


from flask import Flask, redirect, render_template
from flask_pymongo import PyMongo
from pymongo import MongoClient
import Mars_Scrape_Fxns
import os

app = Flask(__name__)
client = MongoClient('mongodb://localhost:27017/')

#app.config["MONGO_URI"] = "mongodb://localhost:27017/"
#mongo = PyMongo(app)

mydb = client.mars_scrape

mycol = mydb.mars_collection

@app.route("/")
def home():
    mars_collection = mycol.find_one()
    return render_template("index.html", mars_collection=mars_collection)

@app.route("/scrape")
def scrape():
    mars_collection = mycol
    mars_title = Mars_Scrape_Fxns.newsfxn()
    mars_news = Mars_Scrape_Fxns.newsfxn()
    mars_image = Mars_Scrape_Fxns.imgfxn()
    mars_weather = Mars_Scrape_Fxns.weatherfxn()
    mars_facts = Mars_Scrape_Fxns.factsfxn()
    mars_hemis = Mars_Scrape_Fxns.hemisfxn()

    data = {
        "mars_title": mars_title,
        "mars_news": mars_news,
        "mars_image": mars_image,
        "mars_weather": mars_weather,
        "mars_facts": mars_facts,
        "mars_hemis": mars_hemis
    }

    mycol.update({}, data, upsert = True)
    return redirect("/", code=302)

if __name__ == "__main__":
    app.run(debug= True)


# Default config for all skorch model
# Can be overwritten in the config file of a model

skorch_config = {
    "log_training": {
        "value": True
    },
    "model__device": {
        "value": "cuda"
    },
    "model_type": {
        "value": "skorch"
    },
    "model__use_checkpoints": {
        "value": True
    },
    "model__optimizer": {
        "value": "adamw"
    },
    "model__batch_size": {
        "values": [256, 512, 1024]
    },
    "model__max_epochs": {
        "value": 300
    },
    "transform__0__method_name": {
        "value": "gaussienize"
    },
    "transform__0__type": {
        "value": "quantile",
    },
    "transform__0__apply_on": {
        "value": "numerical",
    },
    "transformed_target": {
        "values": [False, True]
    },
    "use_gpu": {
        "value": True
    }
}

skorch_config_default = skorch_config.copy()
skorch_config_default["model__batch_size"] = {"value": 512}
skorch_config_default["transformed_target"] = {"value": True}


import random
import sys

def solve(n, t, dimensions):
    l, r = 0, 10 ** 9 + 10
    while r > l:
        m = l + (r - l) // 2
        curt, valid = 0, True
        for i in range(n):
            if dimensions[i][1] > m:
                valid = False
                break
            if dimensions[i][2] <= m:
                curt += dimensions[i][0]
            else:
                curt += dimensions[i][2]
        if (not valid) or curt > t:
            l = m + 1
        else:
            r = m
    return l

random.seed(sys.argv[-1])

max_n = int(sys.argv[1])
max_x = int(sys.argv[2])
max_y = int(sys.argv[3])
max_z = int(sys.argv[4])

if max_x < 0:
    dimensions = []
    curt = 0
    for i in range(max_n):
        x = random.randint(1, 10 ** 9)
        curt += x
        y = random.randint(x, 10 ** 9)
        z = random.randint(10 ** 9 - 100, 10 ** 9)
        dim = [x, y, z]
        random.shuffle(dim)
        dimensions.append(dim)
    print(max_n, curt)
    for x, y, z in dimensions:
        print(x, y, z)
    exit(0)

n = random.randint(max(1, max_n - 100), max_n)
dimensions = [[random.randint(1, max_x), random.randint(1, max_y), random.randint(1, max_z)] for i in range(n)]
for i in range(n):
    dimensions[i].sort()
impossible_left, impossible_right = 0, 10 ** 18 + 10
while impossible_right > impossible_left:
    impossible_mid = impossible_right - (impossible_right - impossible_left) // 2
    if solve(n, impossible_mid, dimensions) == 10 ** 9 + 10:
        impossible_left = impossible_mid
    else:
        impossible_right = impossible_mid - 1
mn = max([l[1] for l in dimensions])
unchanging_left, unchanging_right = impossible_left, 10 ** 18 + 10
while unchanging_right > unchanging_left:
    unchanging_mid = unchanging_left + (unchanging_right - unchanging_left) // 2
    if solve(n, unchanging_mid, dimensions) == mn:
        unchanging_right = unchanging_mid
    else:
        unchanging_left = unchanging_mid + 1
t = 0
case = random.randint(1, 8)
if case == 1:
    t = max(0, impossible_left - random.randint(1, 5))
elif case == 2:
    t = random.randint(0, sum(max(d) for d in dimensions))
else:
    t = random.randint(impossible_left, unchanging_left)
print(n, t)
for i in range(n):
    random.shuffle(dimensions[i])
    print(*dimensions[i])


from abc import ABCMeta, abstractmethod
import os
from io import BytesIO
import tempfile
from subprocess import Popen, PIPE

# -------------------------------------------------------------------------
#
# Gramps modules
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale

_ = glocale.translation.gettext
from ...utils.file import search_for, where_is
from . import BaseDoc
from ..menu import NumberOption, TextOption, EnumeratedListOption, BooleanOption
from ...constfunc import win

# -------------------------------------------------------------------------
#
# set up logging
#
# -------------------------------------------------------------------------
import logging

LOG = logging.getLogger(".graphdoc")

# -------------------------------------------------------------------------
#
# Private Constants
#
# -------------------------------------------------------------------------
_FONTS = [
    {"name": _("Default"), "value": "serif"},
    {"name": _("PostScript / Helvetica"), "value": "Helvetica"},
    {"name": _("TrueType / FreeSans"), "value": "FreeSans"},
]

_RANKDIR = [
    {"name": _("Vertical (↓)"), "value": "TB"},
    {"name": _("Vertical (↑)"), "value": "BT"},
    {"name": _("Horizontal (→)"), "value": "LR"},
    {"name": _("Horizontal (←)"), "value": "RL"},
]

_NODE_PORTS = {"TB": ("n", "s"), "BT": ("s", "n"), "LR": ("w", "e"), "RL": ("e", "w")}

_PAGEDIR = [
    {"name": _("Bottom, left"), "value": "BL"},
    {"name": _("Bottom, right"), "value": "BR"},
    {"name": _("Top, left"), "value": "TL"},
    {"name": _("Top, Right"), "value": "TR"},
    {"name": _("Right, bottom"), "value": "RB"},
    {"name": _("Right, top"), "value": "RT"},
    {"name": _("Left, bottom"), "value": "LB"},
    {"name": _("Left, top"), "value": "LT"},
]

_RATIO = [
    {"name": _("Compress to minimal size"), "value": "compress"},
    {"name": _("Fill the given area"), "value": "fill"},
    {"name": _("Expand uniformly"), "value": "expand"},
]

_NOTELOC = [{"name": _("Top"), "value": "t"}, {"name": _("Bottom"), "value": "b"}]

_SPLINE = [
    {"name": _("Straight"), "value": "false"},
    {
        "name": _("Curved"),
        "value": "true",
    },
    {"name": _("Orthogonal"), "value": "ortho"},
]

if win():
    _DOT_FOUND = search_for("dot.exe")

    if search_for("gswin32c.exe") == 1:
        _GS_CMD = "gswin32c.exe"
    elif search_for("gswin32.exe") == 1:
        _GS_CMD = "gswin32.exe"
    else:
        _GS_CMD = ""
else:
    _DOT_FOUND = search_for("dot")
    _GS_CMD = where_is("gs")

def esc(id_txt):
    return id_txt.replace('"', '\\"')

# ------------------------------------------------------------------------------
#
# GVOptions
#
# ------------------------------------------------------------------------------
class GVOptions:
    """
    Defines all of the controls necessary
    to configure the graph reports.
    """

    def __init__(self):
        self.h_pages = None
        self.v_pages = None
        self.page_dir = None
        self.dpi = None

    def add_menu_options(self, menu):
        """
        Add all graph related options to the menu.

        :param menu: The menu the options should be added to.
        :type menu: :class:`.Menu`
        :return: nothing
        """
        ################################
        category = _("Graphviz Layout")
        ################################
        font_family = EnumeratedListOption(_("Font family"), "")
        for item in _FONTS:
            font_family.add_item(item["value"], item["name"])
        font_family.set_help(
            _(
                "Choose the font family. If international "
                "characters don't show, use FreeSans font. "
                "FreeSans is available from: "
                "http://www.nongnu.org/freefont/"
            )
        )
        menu.add_option(category, "font_family", font_family)

        font_size = NumberOption(_("Font size"), 14, 8, 128)
        font_size.set_help(_("The font size, in points."))
        menu.add_option(category, "font_size", font_size)

        rank_dir = EnumeratedListOption(_("Graph Direction"), "TB")
        for item in _RANKDIR:
            rank_dir.add_item(item["value"], item["name"])
        rank_dir.set_help(
            _("Whether graph goes from top to bottom " "or left to right.")
        )
        menu.add_option(category, "rank_dir", rank_dir)

        h_pages = NumberOption(_("Number of Horizontal Pages"), 1, 1, 25)
        h_pages.set_help(
            _(
                "Graphviz can create very large graphs by "
                "spreading the graph across a rectangular "
                "array of pages. This controls the number "
                "pages in the array horizontally. "
                "Only valid for dot and pdf via Ghostscript."
            )
        )
        menu.add_option(category, "h_pages", h_pages)

        v_pages = NumberOption(_("Number of Vertical Pages"), 1, 1, 25)
        v_pages.set_help(
            _(
                "Graphviz can create very large graphs by "
                "spreading the graph across a rectangular "
                "array of pages. This controls the number "
                "pages in the array vertically. "
                "Only valid for dot and pdf via Ghostscript."
            )
        )
        menu.add_option(category, "v_pages", v_pages)

        page_dir = EnumeratedListOption(_("Paging Direction"), "BL")
        for item in _PAGEDIR:
            page_dir.add_item(item["value"], item["name"])
        page_dir.set_help(
            _(
                "The order in which the graph pages are output. "
                "This option only applies if the horizontal pages "
                "or vertical pages are greater than 1."
            )
        )
        menu.add_option(category, "page_dir", page_dir)

        spline = EnumeratedListOption(_("Connecting lines"), "true")
        for item in _SPLINE:
            spline.add_item(item["value"], item["name"])
        spline.set_help(_("How the lines between objects will be drawn."))
        menu.add_option(category, "spline", spline)

        node_ports = BooleanOption(_("Alternate line attachment"), False)
        node_ports.set_help(_("Whether lines attach to nodes differently"))
        menu.add_option(category, "node_ports", node_ports)

        # the page direction option only makes sense when the
        # number of horizontal and/or vertical pages is > 1,
        # so we need to remember these 3 controls for later
        self.h_pages = h_pages
        self.v_pages = v_pages
        self.page_dir = page_dir

        # the page direction option only makes sense when the
        # number of horizontal and/or vertical pages is > 1
        self.h_pages.connect("value-changed", self.pages_changed)
        self.v_pages.connect("value-changed", self.pages_changed)

        ################################
        category = _("Graphviz Options")
        ################################

        aspect_ratio = EnumeratedListOption(_("Aspect ratio"), "fill")
        for item in _RATIO:
            aspect_ratio.add_item(item["value"], item["name"])
        help_text = _(
            "Affects node spacing and scaling of the graph.\n"
            "If the graph is smaller than the print area:\n"
            "  Compress will not change the node spacing. \n"
            "  Fill will increase the node spacing to fit the print area in "
            "both width and height.\n"
            "  Expand will increase the node spacing uniformly to preserve "
            "the aspect ratio.\n"
            "If the graph is larger than the print area:\n"
            "  Compress will shrink the graph to achieve tight packing at the "
            "expense of symmetry.\n"
            "  Fill will shrink the graph to fit the print area after first "
            "increasing the node spacing.\n"
            "  Expand will shrink the graph uniformly to fit the print area."
        )
        aspect_ratio.set_help(help_text)
        menu.add_option(category, "ratio", aspect_ratio)

        dpi = NumberOption(_("DPI"), 75, 20, 1200)
        dpi.set_help(
            _(
                "Dots per inch.  When creating images such as "
                ".gif or .png files for the web, try numbers "
                "such as 100 or 300 DPI.  PostScript and PDF files "
                "always use 72 DPI."
            )
        )
        menu.add_option(category, "dpi", dpi)
        self.dpi = dpi

        nodesep = NumberOption(_("Node spacing"), 0.20, 0.01, 5.00, 0.01)
        nodesep.set_help(
            _(
                "The minimum amount of free space, in inches, "
                "between individual nodes.  For vertical graphs, "
                "this corresponds to spacing between columns.  "
                "For horizontal graphs, this corresponds to "
                "spacing between rows."
            )
        )
        menu.add_option(category, "nodesep", nodesep)

        ranksep = NumberOption(_("Rank spacing"), 0.20, 0.01, 5.00, 0.01)
        ranksep.set_help(
            _(
                "The minimum amount of free space, in inches, "
                "between ranks.  For vertical graphs, this "
                "corresponds to spacing between rows.  For "
                "horizontal graphs, this corresponds to spacing "
                "between columns."
            )
        )
        menu.add_option(category, "ranksep", ranksep)

        ################################
        category = _("Note")
        ################################

        note = TextOption(_("Note to add to the graph"), [""])
        note.set_help(_("This text will be added to the graph."))
        menu.add_option(category, "note", note)

        noteloc = EnumeratedListOption(_("Note location"), "t")
        for i in range(0, len(_NOTELOC)):
            noteloc.add_item(_NOTELOC[i]["value"], _NOTELOC[i]["name"])
        noteloc.set_help(_("Whether note will appear on top " "or bottom of the page."))
        menu.add_option(category, "noteloc", noteloc)

        notesize = NumberOption(_("Note size"), 32, 8, 128)
        notesize.set_help(_("The size of note text, in points."))
        menu.add_option(category, "notesize", notesize)

    def pages_changed(self):
        """
        This method gets called every time the v_pages or h_pages
        options are changed; when both vertical and horizontal
        pages are set to "1", then the page_dir control needs to
        be unavailable
        """
        if self.v_pages.get_value() > 1 or self.h_pages.get_value() > 1:
            self.page_dir.set_available(True)
        else:
            self.page_dir.set_available(False)

# ------------------------------------------------------------------------------
#
# GVDoc
#
# ------------------------------------------------------------------------------
class GVDoc(metaclass=ABCMeta):
    """
    Abstract Interface for Graphviz document generators. Output formats
    for Graphviz reports must implement this interface to be used by the
    report system.
    """

    @abstractmethod
    def add_node(
        self,
        node_id,
        label,
        shape="",
        color="",
        style="",
        fillcolor="",
        url="",
        htmloutput=False,
    ):
        """
        Add a node to this graph. Nodes can be different shapes like boxes and
        circles.

        :param node_id: A unique identification value for this node.
            Example: "p55"
        :type node_id: string
        :param label: The text to be displayed in the node.
            Example: "John Smith"
        :type label: string
        :param shape: The shape for the node.
            Examples: "box", "ellipse", "circle"
        :type shape: string
        :param color: The color of the node line.
            Examples: "blue", "lightyellow"
        :type color: string
        :param style: The style of the node.
        :type style: string
        :param fillcolor: The fill color for the node.
            Examples: "blue", "lightyellow"
        :type fillcolor: string
        :param url: A URL for the node.
        :type url: string
        :param htmloutput: Whether the label contains HTML.
        :type htmloutput: boolean
        :return: nothing
        """

    @abstractmethod
    def add_link(self, id1, id2, style="", head="", tail="", comment=""):
        """
        Add a link between two nodes.

        :param id1: The unique identifier of the starting node.
            Example: "p55"
        :type id1: string
        :param id2: The unique identifier of the ending node.
            Example: "p55"
        :type id2: string
        :param comment: A text string displayed at the end of the link line.
            Example: "person C is the son of person A and person B"
        :type comment: string
        :return: nothing
        """

    @abstractmethod
    def add_comment(self, comment):
        """
        Add a comment to the source file.

        :param comment: A text string to add as a comment.
            Example: "Next comes the individuals."
        :type comment: string
        :return: nothing
        """

    @abstractmethod
    def add_samerank(self, id1, id2):
        """
        Force the same line for two linked nodes.

        :param id1: The unique identifier of the starting node.
            Example: "p55"
        :type id1: string
        :param id2: The unique identifier of the ending node.
            Example: "p55"
        :type id2: string
        :return: nothing
        """

    @abstractmethod
    def rewrite_label(self, id, label):
        """
        Rewrite the node label.

        :param id: The unique identifier of the node.
            Example: "p55"
        :type id: string
        :param label: The text to be displayed in the node.
            Example: "John Smith"
        :type label: string
        :return: nothing
        """

    @abstractmethod
    def start_subgraph(self, graph_id):
        """
        Start a subgraph in this graph.

        :param id: The unique identifier of the subgraph.
            Example: "p55"
        :type id1: string
        :return: nothing
        """

    @abstractmethod
    def end_subgraph(self):
        """
        End a subgraph that was previously started in this graph.

        :return: nothing
        """

# ------------------------------------------------------------------------------
#
# GVDocBase
#
# ------------------------------------------------------------------------------
class GVDocBase(BaseDoc, GVDoc):
    """
    Base document generator for all Graphviz document generators. Classes that
    inherit from this class will only need to implement the close function.
    The close function will generate the actual file of the appropriate type.
    """

    def __init__(self, options, paper_style, uistate=None):
        BaseDoc.__init__(self, None, paper_style, uistate=uistate)

        self._filename = None
        self._dot = BytesIO()
        self._paper = paper_style

        get_option = options.menu.get_option_by_name

        self.dpi = get_option("dpi").get_value()
        self.fontfamily = get_option("font_family").get_value()
        self.fontsize = get_option("font_size").get_value()
        self.hpages = get_option("h_pages").get_value()
        self.nodesep = get_option("nodesep").get_value()
        self.noteloc = get_option("noteloc").get_value()
        self.notesize = get_option("notesize").get_value()
        self.note = get_option("note").get_value()
        self.pagedir = get_option("page_dir").get_value()
        self.rankdir = get_option("rank_dir").get_value()
        self.ranksep = get_option("ranksep").get_value()
        self.ratio = get_option("ratio").get_value()
        self.vpages = get_option("v_pages").get_value()
        self.spline = get_option("spline").get_value()
        self.node_ports = get_option("node_ports").get_value()

        paper_size = paper_style.get_size()

        # Subtract 0.01" from the drawing area to make some room between
        # this area and the margin in order to compensate for different
        # rounding errors internally in dot
        sizew = (
            paper_size.get_width()
            - self._paper.get_left_margin()
            - self._paper.get_right_margin()
        ) / 2.54 - 0.01
        sizeh = (
            paper_size.get_height()
            - self._paper.get_top_margin()
            - self._paper.get_bottom_margin()
        ) / 2.54 - 0.01

        pheight = paper_size.get_height_inches()
        pwidth = paper_size.get_width_inches()

        xmargin = self._paper.get_left_margin() / 2.54
        ymargin = self._paper.get_top_margin() / 2.54

        sizew *= self.hpages
        sizeh *= self.vpages

        self.write(
            "digraph GRAMPS_graph\n"
            "{\n"
            "  bgcolor=white;\n"
            '  center="true"; \n'
            '  charset="utf8";\n'
            '  concentrate="false";\n'
            + '  dpi="%d";\n' % self.dpi
            + "  graph [fontsize=%d];\n" % self.fontsize
            + '  margin="%3.2f,%3.2f"; \n' % (xmargin, ymargin)
            + '  mclimit="99";\n'
            + '  nodesep="%.2f";\n' % self.nodesep
            + '  outputorder="edgesfirst";\n'
            + ("#" if self.hpages == self.vpages == 1 else "")
            +
            # comment out "page=" if the graph is on 1 page (bug #2121)
            '  page="%3.2f,%3.2f";\n' % (pwidth, pheight)
            + '  pagedir="%s";\n' % self.pagedir
            + '  rankdir="%s";\n' % self.rankdir
            + '  ranksep="%.2f";\n' % self.ranksep
            + '  ratio="%s";\n' % self.ratio
            + '  searchsize="100";\n'
            + '  size="%3.2f,%3.2f"; \n' % (sizew, sizeh)
            + '  splines="%s";\n' % self.spline
            + "\n"
            + "  edge [len=0.5 style=solid fontsize=%d];\n" % self.fontsize
        )
        if self.node_ports:
            self.write(
                "  edge [headport=%s tailport=%s];\n" % _NODE_PORTS[self.rankdir]
            )

        if self.fontfamily:
            self.write(
                '  node [style=filled fontname="%s" fontsize=%d];\n'
                % (self.fontfamily, self.fontsize)
            )
        else:
            self.write("  node [style=filled fontsize=%d];\n" % self.fontsize)
        self.write("\n")

    def write(self, text):
        """Write text to the dot file"""
        self._dot.write(text.encode("utf8", "xmlcharrefreplace"))

    def open(self, filename):
        """Implement GVDocBase.open()"""
        self._filename = os.path.normpath(os.path.abspath(filename))

    def close(self):
        """
        This isn't useful by itself. Other classes need to override this and
        actually generate a file.
        """
        if self.note:
            # build up the label
            label = ""
            for line in self.note:  # for every line in the note...
                line = line.strip()  # ...strip whitespace from this line...
                if line != "":  # ...and if we still have a line...
                    if label != "":  # ...see if we need to insert a newline...
                        label += "\\n"
                    label += line.replace('"', '\\"')

            # after all that, see if we have a label to display
            if label != "":
                self.write(
                    "\n"
                    + '  label="%s";\n' % label
                    + '  labelloc="%s";\n' % self.noteloc
                    + '  fontsize="%d";\n' % self.notesize
                )

        self.write("}\n\n")

    def add_node(
        self,
        node_id,
        label,
        shape="",
        color="",
        style="",
        fillcolor="",
        url="",
        htmloutput=False,
    ):
        """
        Add a node to this graph. Nodes can be different shapes like boxes and
        circles.

        Implements GVDocBase.add_node().
        """
        text = "["

        if shape:
            text += ' shape="%s"' % shape

        if color:
            text += ' color="%s"' % color

        if fillcolor:
            text += ' fillcolor="%s"' % fillcolor

        if style:
            text += ' style="%s"' % style

        # note that we always output a label -- even if an empty string --
        # otherwise Graphviz uses the node ID as the label which is unlikely
        # to be what the user wants to see in the graph
        if label.startswith("<") or htmloutput:
            text += " label=<%s>" % label
        else:
            text += ' label="%s"' % label

        if url:
            text += ' URL="%s"' % url

        text += " ]"
        self.write('  "%s" %s;\n' % (esc(node_id), text))

    def add_link(self, id1, id2, style="", head="", tail="", comment=""):
        """
        Add a link between two nodes.

        Implements GVDocBase.add_link().
        """
        self.write('  "%s" -> "%s"' % (esc(id1), esc(id2)))

        if style or head or tail:
            self.write(" [")

            if style:
                self.write(" style=%s" % style)
            if head:
                self.write(" arrowhead=%s" % head)
            if tail:
                self.write(" arrowtail=%s" % tail)
            if head:
                if tail:
                    self.write(" dir=both")
                else:
                    self.write(" dir=forward")
            else:
                if tail:
                    self.write(" dir=back")
                else:
                    self.write(" dir=none")
            self.write(" ]")

        self.write(";")

        if comment:
            self.write(" // %s" % comment)

        self.write("\n")

    def add_comment(self, comment):
        """
        Add a comment.

        Implements GVDocBase.add_comment().
        """
        tmp = comment.split("\n")
        for line in tmp:
            text = line.strip()
            if text == "":
                self.write("\n")
            elif text.startswith("#"):
                self.write("%s\n" % text)
            else:
                self.write("# %s\n" % text)

    def add_samerank(self, id1, id2):
        """
        Force the same line for two linked nodes.

        Implements GVDocBase.add_samerank().
        """
        self.write('  {rank=same "%s" "%s"}\n' % (esc(id1), esc(id2)))

    def rewrite_label(self, id, label):
        """
        Rewrite the node label.

        Implements GVDocBase.rewrite_label().
        """
        self.write('  "%s" [label = "%s"]\n' % (esc(id), label))

    def start_subgraph(self, graph_id):
        """Implement GVDocBase.start_subgraph()"""
        graph_id = graph_id.replace(" ", "_")  # for user-defined ID with space
        self.write(
            "  subgraph cluster_%s\n" % graph_id + "  {\n" + '  style="invis";\n'
        )  # no border around subgraph (#0002176)

    def end_subgraph(self):
        """Implement GVDocBase.end_subgraph()"""
        self.write("  }\n")

# ------------------------------------------------------------------------------
#
# GVDotDoc
#
# ------------------------------------------------------------------------------
class GVDotDoc(GVDocBase):
    """GVDoc implementation that generates a .gv text file."""

    def close(self):
        """Implements GVDotDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-3:] != ".gv":
            self._filename += ".gv"

        with open(self._filename, "wb") as dotfile:
            dotfile.write(self._dot.getvalue())

# ------------------------------------------------------------------------------
#
# GVPsDoc
#
# ------------------------------------------------------------------------------
class GVPsDoc(GVDocBase):
    """GVDoc implementation that generates a .ps file using Graphviz."""

    def __init__(self, options, paper_style):
        # DPI must always be 72 for PDF.
        # GV documentation says dpi is only for image formats.
        options.menu.get_option_by_name("dpi").set_value(72)
        GVDocBase.__init__(self, options, paper_style)
        # GV documentation allow multiple pages only for ps format,
        # But it does not work with -Tps:cairo in order to
        # show Non Latin-1 letters. Force to only 1 page.
        # See bug tracker issue 2815
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVPsDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-3:] != ".ps":
            self._filename += ".ps"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()

        # Generate the PS file.
        # Reason for using -Tps:cairo. Needed for Non Latin-1 letters
        # Some testing with Tps:cairo. Non Latin-1 letters are OK i all cases:
        # Output format:   ps       PDF-GostScript  PDF-Graphviz
        # Single page      OK           OK              OK
        # Multip page    1 page,        OK             1 page,
        #               corrupted                    set by gramps
        # If I take a correct multip page PDF and convert it with pdf2ps I get
        # multip pages, but the output is clipped, some margins have
        # disappeared. I used 1 inch margins always.
        # See bug tracker issue 2815
        # :cairo does not work with Graphviz 2.26.3 and later See issue 4164
        # recent versions of Graphviz doesn't even try, just puts out a single
        # large page.

        command = 'dot -Tps:cairo -o"%s" "%s"' % (self._filename, tmp_dot)
        dotversion = str(Popen(["dot", "-V"], stderr=PIPE).communicate(input=None)[1])
        # Problem with dot 2.26.3 and later and multiple pages, which gives
        # "cairo: out of memory" If the :cairo is skipped for these cases it
        # gives bad result for non-Latin-1 characters (utf-8).
        if (dotversion.find("2.26.3") or dotversion.find("2.28.0") != -1) and (
            self.vpages * self.hpages
        ) > 1:
            command = command.replace(":cairo", "")
        os.system(command)
        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVSvgDoc
#
# ------------------------------------------------------------------------------
class GVSvgDoc(GVDocBase):
    """GVDoc implementation that generates a .svg file using Graphviz."""

    def __init__(self, options, paper_style):
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVSvgDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".svg":
            self._filename += ".svg"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        # Generate the SVG file.
        os.system('dot -Tsvg:cairo -o"%s" "%s"' % (self._filename, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVSvgzDoc
#
# ------------------------------------------------------------------------------
class GVSvgzDoc(GVDocBase):
    """GVDoc implementation that generates a .svg file using Graphviz."""

    def __init__(self, options, paper_style):
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVSvgzDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-5:] != ".svgz":
            self._filename += ".svgz"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        # Generate the SVGZ file.
        os.system('dot -Tsvgz -o"%s" "%s"' % (self._filename, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVPngDoc
#
# ------------------------------------------------------------------------------
class GVPngDoc(GVDocBase):
    """GVDoc implementation that generates a .png file using Graphviz."""

    def __init__(self, options, paper_style):
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVPngDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".png":
            self._filename += ".png"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        # Generate the PNG file.
        os.system('dot -Tpng -o"%s" "%s"' % (self._filename, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVJpegDoc
#
# ------------------------------------------------------------------------------
class GVJpegDoc(GVDocBase):
    """GVDoc implementation that generates a .jpg file using Graphviz."""

    def __init__(self, options, paper_style):
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVJpegDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".jpg":
            self._filename += ".jpg"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        # Generate the JPEG file.
        os.system('dot -Tjpg -o"%s" "%s"' % (self._filename, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVGifDoc
#
# ------------------------------------------------------------------------------
class GVGifDoc(GVDocBase):
    """GVDoc implementation that generates a .gif file using Graphviz."""

    def __init__(self, options, paper_style):
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVGifDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".gif":
            self._filename += ".gif"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        # Generate the GIF file.
        os.system('dot -Tgif -o"%s" "%s"' % (self._filename, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVPdfGvDoc
#
# ------------------------------------------------------------------------------
class GVPdfGvDoc(GVDocBase):
    """GVDoc implementation that generates a .pdf file using Graphviz."""

    def __init__(self, options, paper_style):
        # DPI must always be 72 for PDF.
        # GV documentation says dpi is only for image formats.
        options.menu.get_option_by_name("dpi").set_value(72)
        # GV documentation allow multiple pages only for ps format,
        # which also includes pdf via ghostscript.
        options.menu.get_option_by_name("v_pages").set_value(1)
        options.menu.get_option_by_name("h_pages").set_value(1)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVPdfGvDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".pdf":
            self._filename += ".pdf"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()
        fname = self._filename

        # Generate the PDF file.
        os.system('dot -Tpdf -o"%s" "%s"' % (fname, tmp_dot))

        # Delete the temporary dot file
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# GVPdfGsDoc
#
# ------------------------------------------------------------------------------
class GVPdfGsDoc(GVDocBase):
    """GVDoc implementation that generates a .pdf file using Ghostscript."""

    def __init__(self, options, paper_style):
        # DPI must always be 72 for PDF.
        # GV documentation says dpi is only for image formats.
        options.menu.get_option_by_name("dpi").set_value(72)
        GVDocBase.__init__(self, options, paper_style)

    def close(self):
        """Implements GVPdfGsDoc.close()"""
        GVDocBase.close(self)

        # Make sure the extension is correct
        if self._filename[-4:] != ".pdf":
            self._filename += ".pdf"

        # Create a temporary dot file
        (handle, tmp_dot) = tempfile.mkstemp(".gv")
        dotfile = os.fdopen(handle, "wb")
        dotfile.write(self._dot.getvalue())
        dotfile.close()

        # Create a temporary PostScript file
        (handle, tmp_ps) = tempfile.mkstemp(".ps")
        os.close(handle)

        # Generate PostScript using dot
        # Reason for using -Tps:cairo. Needed for Non Latin-1 letters
        # See bug tracker issue 2815
        # :cairo does not work with with multi-page See issue 4164
        # recent versions of Graphviz doesn't even try, just puts out a single
        # large page, so we use Ghostscript to split it up.

        command = 'dot -Tps:cairo -o"%s" "%s"' % (tmp_ps, tmp_dot)
        os.system(command)

        # Add .5 to remove rounding errors.
        paper_size = self._paper.get_size()
        width_pt = int((paper_size.get_width_inches() * 72) + 0.5)
        height_pt = int((paper_size.get_height_inches() * 72) + 0.5)
        if (self.vpages * self.hpages) == 1:
            # -dDEVICEWIDTHPOINTS=%d' -dDEVICEHEIGHTPOINTS=%d
            command = (
                "%s -q -sDEVICE=pdfwrite -dNOPAUSE "
                "-dDEVICEWIDTHPOINTS=%d -dDEVICEHEIGHTPOINTS=%d "
                '-sOutputFile="%s" "%s" -c quit'
                % (_GS_CMD, width_pt, height_pt, self._filename, tmp_ps)
            )
            os.system(command)
            os.remove(tmp_ps)
            return
        # Margins (in centimeters) to pixels 72/2.54=28.345
        margin_t = int(28.345 * self._paper.get_top_margin())
        margin_b = int(28.345 * self._paper.get_bottom_margin())
        margin_r = int(28.345 * self._paper.get_right_margin())
        margin_l = int(28.345 * self._paper.get_left_margin())
        margin_x = margin_l + margin_r
        margin_y = margin_t + margin_b
        # Convert to PDF using ghostscript
        list_of_pieces = []

        x_rng = (
            range(1, self.hpages + 1)
            if "L" in self.pagedir
            else range(self.hpages, 0, -1)
        )
        y_rng = (
            range(1, self.vpages + 1)
            if "B" in self.pagedir
            else range(self.vpages, 0, -1)
        )
        if self.pagedir[0] in "TB":
            the_list = ((__x, __y) for __y in y_rng for __x in x_rng)
        else:
            the_list = ((__x, __y) for __x in x_rng for __y in y_rng)
        for __x, __y in the_list:
            # Slit PS file to pieces of PDF
            page_offset_x = (__x - 1) * (margin_x - width_pt)
            page_offset_y = (__y - 1) * (margin_y - height_pt)
            tmp_pdf_piece = "%s_%d_%d.pdf" % (tmp_ps, __x, __y)
            list_of_pieces.append(tmp_pdf_piece)
            # Generate Ghostscript code
            command = (
                "%s -q -dBATCH -dNOPAUSE -dSAFER "
                "-dDEVICEWIDTHPOINTS=%d -dDEVICEHEIGHTPOINTS=%d "
                '-dFIXEDMEDIA -sOutputFile="%s" -sDEVICE=pdfwrite '
                '-c "<</.HWMargins [%d %d %d %d] /PageOffset [%d %d]>> '
                'setpagedevice" -f "%s"'
                % (
                    _GS_CMD,
                    width_pt + 10,
                    height_pt + 10,
                    tmp_pdf_piece,
                    margin_l,
                    margin_b,
                    margin_r,
                    margin_t,
                    page_offset_x + 5,
                    page_offset_y + 5,
                    tmp_ps,
                )
            )
            # Execute Ghostscript
            os.system(command)
        # Merge pieces to single multipage PDF ;
        command = (
            "%s -q -dBATCH -dNOPAUSE "
            '-sOUTPUTFILE="%s" -sDEVICE=pdfwrite %s '
            % (_GS_CMD, self._filename, " ".join(list_of_pieces))
        )
        os.system(command)

        # Clean temporary files
        os.remove(tmp_ps)
        for tmp_pdf_piece in list_of_pieces:
            os.remove(tmp_pdf_piece)
        os.remove(tmp_dot)

# ------------------------------------------------------------------------------
#
# Various Graphviz formats.
#
# ------------------------------------------------------------------------------
FORMATS = []

if _DOT_FOUND:
    if _GS_CMD != "":
        FORMATS += [
            {
                "type": "gspdf",
                "ext": "pdf",
                "descr": _("PDF (Ghostscript)"),
                "mime": "application/pdf",
                "class": GVPdfGsDoc,
            }
        ]

    FORMATS += [
        {
            "type": "gvpdf",
            "ext": "pdf",
            "descr": _("PDF (Graphviz)"),
            "mime": "application/pdf",
            "class": GVPdfGvDoc,
        }
    ]

    FORMATS += [
        {
            "type": "ps",
            "ext": "ps",
            "descr": _("PostScript"),
            "mime": "application/postscript",
            "class": GVPsDoc,
        }
    ]

    FORMATS += [
        {
            "type": "svg",
            "ext": "svg",
            "descr": _("Structured Vector Graphics (SVG)"),
            "mime": "image/svg",
            "class": GVSvgDoc,
        }
    ]

    FORMATS += [
        {
            "type": "svgz",
            "ext": "svgz",
            "descr": _("Compressed Structured Vector Graphs (SVGZ)"),
            "mime": "image/svgz",
            "class": GVSvgzDoc,
        }
    ]

    FORMATS += [
        {
            "type": "jpg",
            "ext": "jpg",
            "descr": _("JPEG image"),
            "mime": "image/jpeg",
            "class": GVJpegDoc,
        }
    ]

    FORMATS += [
        {
            "type": "gif",
            "ext": "gif",
            "descr": _("GIF image"),
            "mime": "image/gif",
            "class": GVGifDoc,
        }
    ]

    FORMATS += [
        {
            "type": "png",
            "ext": "png",
            "descr": _("PNG image"),
            "mime": "image/png",
            "class": GVPngDoc,
        }
    ]

FORMATS += [
    {
        "type": "dot",
        "ext": "gv",
        "descr": _("Graphviz File"),
        "mime": "text/x-graphviz",
        "class": GVDotDoc,
    }
]


# -*- coding: utf-8 -*-
"""
Created on Mon May 13 14:28:35 2019

@author: hjiang
"""

"""
Given a linked list, swap every two adjacent nodes and return its head.

You may not modify the values in the list's nodes, only nodes itself may be changed.

Example:

Given 1->2->3->4, you should return the list as 2->1->4->3.

这个题目比较绕
首先建立一个dummy指针，dummy.next指向linklist head
然后换成2->1->3->4, 注意顺序，此时当前指针在1位置
最后才是2->1->4->3
"""

# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:#关键句子，pre.next = b, 把2放在最前面， b.next = a, 让2连接1， a.next = b.next, 让1连接3； 最后pre = a, 让从新的1开始，后面是3->4又是一样的
    # @param a ListNode
    # @return a ListNode
    def swapPairs(self, head):#这个感觉也好理解了，感觉更好记忆一点，参见p286（p277） Python script的图
        pre, pre.next = self, head
        while pre.next and pre.next.next:#一定要查看是不是一次有一对，要不是就不用换了
            a = pre.next#第一个
            b = a.next#第二个
            pre.next, b.next, a.next = b, a, b.next#把b（第二个换到第一个）， a在b后面，然后第三个数连着a
            pre = a#直接把当前指针移动到第二个，后面的以此类推，（因为1已经和2调换位置了，这时候的移动相当于移动原来两个位置，下一个数是4，又和开始一样，可以进入下一个循环了）
        return self.next

    # Iteratively
    def swapPairs1(self, head):# 现在觉得这个比较简单                                              迭代看这个
        dummy = p = ListNode(0)#dummy和p这时候都是在开头，接着p操作交换，最后还是返回dummy.next，指向列表头
        dummy.next = head
        while head and head.next:#注意这个地方p的后面是head，
            tmp = head.next#保存2
            head.next = tmp.next#连接1->3
            tmp.next = head#连接2—>1
            p.next = tmp#把2放在开头
            head = head.next#把head移动到第三个位置（原来的数字3的位置，3其实没动）
            p = tmp.next#把p也移动到第二个位置（现在的1的位置，原来2的位置）
        return dummy.next

    # Recursively
    def swapPairs2(self, head):#                                                         递归看这个，和迭代的套路一样
        if head and head.next:
            tmp = head.next
            head.next = self.swapPairs(tmp.next)#建立第一个和第三个之间的联系
            tmp.next = head
            return tmp
        return head

class Solution1:
    # @param a ListNode
    # @return a ListNode
    def swapPairs(self, head):#
        if head == None or head.next == None:
            return head
        dummy = ListNode(0); dummy.next = head
        p = dummy
        while p.next and p.next.next:
            tmp = p.next.next#先复制第二个位置 tmp.val=2
            p.next.next = tmp.next#连接1(p.next)的next到3（tmp.next）
            tmp.next = p.next#2的后面（tmp.next）指向1(p.next)
            p.next = tmp#把2移动到head的位置
            p = p.next.next#p直接到第二个位置
        return dummy.next

if __name__ == "__main__":
    head = ListNode(1)
    head.next = ListNode(2)
    head.next.next = ListNode(3)
    head.next.next.next = ListNode(4)
#    head.next.next.next.next = ListNode(5)
    print (Solution().swapPairs(head))

Python3.6/24-Py3-M-Swap Nodes in Pairs.py


from mqtt_lora.models import Scheda, Topic

def compose_message(scheda:Scheda, message:str, topic:Topic):
    buffer = bytearray()
    buffer += b"M"
    buffer += scheda.add_h.to_bytes(1, "big")
    buffer += scheda.add_l.to_bytes(1, "big")
    buffer += scheda.chan.to_bytes(1, "big")
    buffer += topic.nome.encode("utf-8")
    buffer += b"\r"
    buffer += message.encode("utf-8")
    buffer += b"\x21"
    buffer += b"\n"
    return buffer


import os
from PIL import Image

input_directory = "public/Images"
output_directory = "public/Images"
quality = 50

os.makedirs(output_directory, exist_ok=True)

# List all output files
output_files = set(os.listdir(output_directory))

for filename in os.listdir(input_directory):
    if filename.endswith((".png", ".jpg", ".jpeg", ".JPG", ".heic", ".HEIC")):
        input_path = os.path.join(input_directory, filename)
        output_filename = os.path.splitext(filename)[0] + ".webp"
        output_path = os.path.join(output_directory, output_filename)

        try:
            img = Image.open(input_path)
            img.save(output_path, 'webp', quality=quality)
            print(f"Converted: {input_path} -> {output_path}")
            output_files.add(output_filename)
        except Exception as e:
            print(f"Error converting {input_path}: {str(e)}")

# Remove all files except .svg and .webp in the output directory
for filename in output_files.copy():
    if not filename.endswith((".svg", ".webp", ".py")):
        file_path = os.path.join(output_directory, filename)
        os.remove(file_path)
        print(f"Removed: {file_path}")
        output_files.remove(filename)

print("Conversion and removal complete.")


from django.shortcuts import render, redirect
from django.db import connection
from .forms import PengantaranForm,UpdatePengantaranForm
import random

# Create your views here.
def tabel_pengantaran_farmasi(request):
    if 'email' not in request.session:
        return redirect('/login/')

    query = """SELECT * FROM pengantaran_farmasi ORDER BY id_pengantaran;"""

    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")
    cursor.execute(query)

    data_pengantaran = __fetch(cursor)

    context = {
        'data_pengantaran': data_pengantaran,
        'role': request.session['role']
    }

    return render(request, 'tabel/read_pengantaran_farmasi.html', context)

def create_pengantaran_farmasi(request):
    if 'email' not in request.session:
        return redirect('/login/')

    if request.session['role'] != 'admin-apotek':
        return redirect(f'/navigate/{request.session["role"]}/')

    form = PengantaranForm(request.POST or None)
    context = {
        'form': form,
        'error': []
    }

    if (request.method == 'POST' and form.is_valid()):
        valid = True

        id_transaksi = request.POST['id_transaksi']
        waktu = request.POST['waktu']

        # validasi biaya kirim
        biaya = request.POST['biaya_kirim']
        if int(biaya) <= 0:
            valid = valid and False
            context['error'].append('Biaya Kirim must be positive.')

        if valid:
            try:
                __create_pengantaran(id_transaksi, waktu, biaya)
                print("PENGANTARAN BERHASIL DITAMBAHKAN")
                return redirect('/pengantaran-farmasi/tabel/')

            except:

                print("PENGANTARAN GAGAL DITAMBAHKAN")

    return render(request, 'create/create_pengantaran_farmasi.html', context)

def update_pengantaran_farmasi(request,idpengantaran,totalbiaya):
    if 'email' not in request.session:
        return redirect('/login/')

    if (request.session['role'] != 'admin-apotek'):
        return redirect(f'/navigate/{request.session["role"]}/')

    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")
    cursor.execute(f"SELECT * FROM pengantaran_farmasi")

    data_pengantaran = {}
    x = __fetch(cursor)

    for i in range(len(x)):
        if x[i]['id_pengantaran'] == idpengantaran:
            data_pengantaran = x[i]
            break

    data_id_kurir = data_pengantaran['id_kurir']
    data_id_transaksi_pembelian = __get_id_transaksi()
    data_waktu = data_pengantaran['waktu'].strftime("%Y-%m-%d %H:%M:%S")
    data_status = data_pengantaran['status_pengantaran']
    data_biaya_kirim = data_pengantaran['biaya_kirim']

    form = UpdatePengantaranForm(request.POST or None, initial = {
        'id_pengantaran' : idpengantaran,
        'id_kurir': data_id_kurir,
        'id_transaksi' : data_id_transaksi_pembelian,
        'waktu' : data_waktu,
        'status_pengiriman' : data_status,
        'biaya_kirim': data_biaya_kirim,
        'total_biaya': totalbiaya,
    })

    context={
        'error': [],
        'form': form,
    }
    if (request.method == 'POST' and form.is_valid()):
        valid = True

        id_pengantaran = idpengantaran
        id_kurir= request.POST['id_kurir']
        id_transaksi_pembelian=request.POST['id_transaksi']
        waktu=request.POST['waktu']
        status_pengiriman=request.POST['status_pengiriman']
        biaya_kirim=request.POST['biaya_kirim']
        total_biaya=totalbiaya

        if (biaya_kirim != '' and (not biaya_kirim.isnumeric())):
            context['error'].append(
                'Masukkan angka yang benar')
            valid = valid and False

        if valid:
            try:
                __update(id_pengantaran, id_kurir, id_transaksi_pembelian,
                            waktu, status_pengiriman, biaya_kirim, total_biaya)

                print("UPDATE SUKSES")
                return redirect('/pengantaran-farmasi/tabel/')

            except:
                print("UPDATE GAGAL")

    return render(request, 'update/update_pengantaran_farmasi.html', context)

def delete_pengantaran(request):
    """
    function untuk menghapus data pengantaran farmasi.
    """
    id_pengantaran = request.POST['id_pengantaran']

    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")
    cursor.execute(
        f"""
        DELETE FROM pengantaran_farmasi
        WHERE id_pengantaran = '{id_pengantaran}';
        """
    )

    return redirect('/pengantaran-farmasi/tabel/')

def __create_pengantaran(id_transaksi, waktu, biaya):
    """
    function untuk menambahkan data pengantaran baru.
    """
    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")

    # generate id pengantaran
    cursor.execute(
        """
        SELECT id_pengantaran FROM pengantaran_farmasi
        ORDER BY id_pengantaran DESC
        LIMIT 1;
        """
    )
    latest_id = int(__fetch(cursor)[0]['id_pengantaran'][-2:])
    new_id = latest_id + 1
    if new_id < 10:
        new_id = 'PE0' + str(new_id)
    else:
        new_id = 'PE' + str(new_id)

    # choose random kurir as default kurir
    cursor.execute("SELECT id_kurir FROM kurir;")
    data = [row for row in cursor.fetchall()]
    id_kurir_default = 'KU01'

    cursor.execute(
        f"""
        INSERT INTO pengantaran_farmasi
        VALUES ('{new_id}', '{id_kurir_default}', '{id_transaksi}', '{waktu}',
        'MENUNGGU', {biaya}, {biaya});
        """
    )

def __get_id_transaksi():
    """
    function untuk mendapatkan id transaksi yang telah terdaftar
    """
    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")
    cursor.execute(
        f"""
        SELECT id_transaksi_pembelian FROM transaksi_pembelian;
        """
    )

    return __fetch(cursor)[0]['id_transaksi_pembelian']

def __update(id_pengantaran, id_kurir, id_transaksi_pembelian, waktu, status_pengantaran, biaya_kirim, total_biaya):
    """
    function untuk memperbarui data pengantaran farmasi.
    """
    cursor = connection.cursor()
    cursor.execute("SET SEARCH_PATH TO farmakami;")
    cursor.execute(
        f"""
        UPDATE pengantaran_farmasi
        SET (id_kurir, id_transaksi_pembelian, waktu, status_pengantaran, biaya_kirim) = ('{id_kurir}','{id_transaksi_pembelian}','{waktu}','{status_pengantaran}','{biaya_kirim}')
        WHERE total_biaya = '{total_biaya}' and id_pengantaran='{id_pengantaran}';
        """
    )

def __fetch(cursor):
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]


# -*- coding: utf-8 -*-
"""
Created on Fri Sep 27 16:31:02 2019

@author: Karsten
"""
import warnings
warnings.filterwarnings('ignore')

# turn off tensorflow deprecation warnings
import tensorflow.python.util.deprecation as deprecation
deprecation._PRINT_DEPRECATION_WARNINGS = False

import pandas as pd
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix
from sklearn.metrics import roc_auc_score
from keras.utils.vis_utils import plot_model
from IPython.display import SVG
from keras.utils import model_to_dot

def evaluate_model(model, features, labels):
    _, accuracy = model.evaluate(features, labels)
    print('Accuracy: %.2f' % (accuracy*100))

    # make class predictions with the model
    predictions = model.predict_classes(features)
    print("Confusion Matrix :")
    print(confusion_matrix(labels, predictions))
    probabilities = model.predict(features)
    auc = roc_auc_score(labels, probabilities)
    print("AUC : {0:.2f}".format(auc))

def plot_history(history):
    # to handle metrics keys changes in Keras 2.3
    # see https://github.com/keras-team/keras/releases/tag/2.3.0
    pre_23 = "acc" in history.history.keys()
    acc_key     = 'acc'     if pre_23 else 'accuracy'
    val_acc_key = 'val_acc' if pre_23 else 'val_accuracy'

    acc = history.history[acc_key]
    val_acc = history.history[val_acc_key]
    loss = history.history['loss']
    val_loss = history.history['val_loss']

    epochs = range(1, len(acc) + 1)

    # "bo" is for "blue dot"
    plt.plot(epochs, loss, 'bo', label='Training loss')
    # b is for "solid blue line"
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    # "go" is for "green dot"
    #plt.plot(epochs, acc, 'go', label='Training accuracy')
    # g is for "solid green line"
    #plt.plot(epochs, val_acc, 'g', label='Validation accuracy')
    plt.title('Training and validation loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()

def make_model(num_inputs, layers):
    model = Sequential()
    # first layer
    model.add(Dense(layers[0], input_dim=num_inputs, activation='relu'))
    #  intermediate layers
    for i in range(1, len(layers)):
        model.add(Dense(layers[i], activation='relu'))
    # final layer with a single node
    model.add(Dense(1, activation='sigmoid'))

    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

# ============================
#     Data Preprocessing
# ============================
data_sets = ('bank-10percent', 'bank-full', 'bank-balanced')

bank = pd.read_csv('../../data/' + data_sets[1] + '.csv')

label_col = 'y'
label = bank[label_col]
features = bank.drop(columns=['y'])

label_encoded = pd.get_dummies(label, drop_first = True)
features_encoded = pd.get_dummies(features, drop_first = True)

scaler = StandardScaler()
features_normalized = scaler.fit_transform(features_encoded)

X_train, X_test, y_train, y_test = train_test_split(features_normalized, label_encoded, test_size = 0.2, random_state = 167)

feature_count=X_train.shape[1]

# ========================
#     parameters
# ========================
num_epochs=10
layers = [16, 8]

# ========================
# define the keras model
# ========================
model = make_model(feature_count, layers)
print("# features : {0}".format(feature_count))
SVG(model_to_dot(model, show_shapes=True, show_layer_names=False).create(prog='dot', format='svg'))
print(model.summary())

# ========================
#     train
# ========================
print("Fitting model")
history = model.fit(X_train, y_train,
                    epochs=num_epochs,
                    validation_data=(X_test, y_test),
                    verbose=1)
print(model.summary())
# ========================
#     plot
# ========================
plot_history(history)

# ========================
#   evaluate the model
# ========================
print("Evaluating model on training data")
evaluate_model(model, X_train, y_train)

print("Evaluating model on test data")
evaluate_model(model, X_test, y_test)


from flask import Flask, render_template, request, redirect, url_for, session, send_file

from Camera import z_i, leica, falcon, eagle
from Plane import cessna_402, cessna_t206h, vulcan, tencam
from RaidPlan import RaidPlan
from User import User
from plot import flyPlot

app = Flask(__name__)
app.secret_key = 'secret_key'

def checkCameraValueToObject(key):
    cameras = {"z_i": z_i, "leica": leica, "falcon": falcon, "eagle": eagle}
    return cameras[key]

def checkPlaneValueToObject(key):
    planes = {"cessna": cessna_402, "n16": cessna_t206h, "vulcan": vulcan, "tencam": tencam}
    return planes[key]

@app.route('/', methods=['POST', 'GET'])
def main():
    if request.method == 'POST':
        session['camera'] = request.form.getlist('camera')[0]
        session['plane'] = request.form.getlist('plane')[0]
        return redirect(url_for('parameters'))
    return render_template('index.html')

@app.route('/parameters', methods=['POST', 'GET'])
def parameters():
    if request.method == 'POST':
        session['dx'] = request.form.get('dx')
        session['dy'] = request.form.get('dy')
        session['gsd'] = request.form.get('gsd')
        session['p'] = request.form.get('p')
        session['q'] = request.form.get('q')
        return redirect(url_for('visualization'))
    return render_template('parameters.html')

@app.route('/visualization')
def visualization():
    camera = checkCameraValueToObject(session.get('camera'))
    plane = checkPlaneValueToObject(session.get('plane'))
    gsd, p, q, dx, dy = session.get('gsd'), session.get('p'), session.get('q'), session.get('dx'), session.get('dy')
    user = User(float(gsd), float(p), float(q), int(dx), int(dy))
    camera.user = user
    W = round(camera.getW(), 2)
    Lx = round(camera.getLx(), 2)
    Ly = round(camera.getLy(), 2)
    Bx = round(camera.getBx(), 2)
    By = round(camera.getBy(), 2)
    print(camera.p, camera.q)
    raidPlan = RaidPlan(camera, plane)
    raidPlan.camera.user = user
    h, min, sec = raidPlan.flyTime()
    return render_template('visualization.html', w=W, lx=Lx, ly=Ly, bx=Bx, by=By, h=h, min=min, sec=sec)

@app.route('/visualization.png')
def plot():
    gsd, p, q, dx, dy = session.get('gsd'), session.get('p'), session.get('q'), session.get('dx'), session.get('dy')
    user = User(float(gsd), float(p), float(q), int(dx), int(dy))
    camera = checkCameraValueToObject(session.get('camera'))
    img = flyPlot(user, camera)
    return send_file(img, mimetype='image/png', cache_timeout=0)

if __name__ == '__main__':
    app.run(debug=True)


"""
This script tests comparison
"""
from usr_func import *
from MAFIA.Simulation.Config.Config import *

waypoint_graph = pd.read_csv(FILEPATH+"Simulation/Config/WaypointGraph.csv").to_numpy()
gmrf_grid = pd.read_csv(FILEPATH+"Simulation/Config/GMRFGrid.csv").to_numpy()

ind_top_gmrf = np.where(gmrf_grid[:, 2] < 0)[0]
ind_top_waypoint = np.where(waypoint_graph[:, 2] < 1)[0]

plt.plot(gmrf_grid[ind_top_gmrf, 1], gmrf_grid[ind_top_gmrf, 0], 'k.', alpha=.4)
plt.plot(waypoint_graph[ind_top_waypoint, 1], waypoint_graph[ind_top_waypoint, 0], 'r.', alpha=.4)
plt.show()


from flask import Flask, jsonify, request
import os

app = Flask(__name__)
apimessage = [
  { 'message': 'Hello-World' }
]

@app.route('/apimessage')
def hello():
    return jsonify(apimessage)
@app.route('/index')
def index():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Web App Home Page</title>
</head>
<body>
    <h2>
        Hello World!
    </h2>
</body>
</html>'''
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True,host='0.0.0.0',port=port)


import pandas as pd
import numpy as np
from sklearn.manifold import TSNE
import ast

# Read the CSV file, assuming it has headers
data = pd.read_csv("embedded_data.csv", header=0)

print("Original data:")
print(data)

# Convert the embeddings from strings to lists of numeric values
data['embedding'] = data['embedding'].apply(ast.literal_eval)

# Assuming each row represents a word embedding
word_embeddings = data['embedding'].tolist()

# Print the number of samples
print(f"\nNumber of samples: {len(word_embeddings)}")

# If there are no samples, exit
if len(word_embeddings) == 0:
    print("No valid samples found.")
    exit()

# Dimensionality reduction using t-SNE
word_embeddings = np.array(word_embeddings)
tsne = TSNE(n_components=3, perplexity=5)  # You can try a smaller value for perplexity
coordinates_3d = tsne.fit_transform(word_embeddings)

# Save the 3D coordinates to a new CSV file
reduced_data = pd.DataFrame(coordinates_3d, columns=['x', 'y', 'z'])
reduced_data['text'] = data['text']  # Add the 'text' column to the reduced_data DataFrame
reduced_data = reduced_data[['text', 'x', 'y', 'z']]  # Reorder the columns
reduced_data.to_csv("reduced_embedded_data.csv", index=False)


import numpy as np

from algorithms.genetic.genetic_utils.general_utils import non_dominated_sorting, calc_crowding_distance, split_by_feasibility

"""
Copyright (C) 2013-2017 Docker, Inc. - Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0
"""

class GeneticSurvivalStrategy:
    """
    Class for the NSGA-II survival strategy (see NSGA.py).

    The main functions are:
        - Initialize a survival strategy instance;
        - Use the infeasible solutions survival strategy;
        - Use the feasible solutions survival strategy;
        - Execute the survival strategy.
    """

    def __init__(self, pop_size: int):
        """
        Initialize a survival strategy instance
        :param pop_size: size of the population
        """

        self._pop_size = pop_size

    def least_infeasible_survival(self, p_list: np.array, f_list: np.array, constraint_violations: np.array, n_survive: int):
        """
        Use the infeasible solutions survival strategy
        :param p_list: problem solutions
        :param f_list: related points in the objectives space
        :param constraint_violations: an array containing all the constraint violations; each of them is related to a point in p_list
        :param n_survive: maximum number of survivals
        :return: p_list, f_list and constraint_violations with only n_survive items

        Notes:  The solutions are sorted based on their constraint violations.
                If the number of solutions is greater than n_survive, then some solutions are dropped: in particular, the ones with the highest constraint violations are removed.
        """

        sorted_by_cv = constraint_violations[:, 0].argsort()
        p_list, f_list, constraint_violations = p_list[sorted_by_cv], f_list[sorted_by_cv], constraint_violations[sorted_by_cv]

        return p_list[:n_survive], f_list[:n_survive], constraint_violations[:n_survive]

    def ranking_and_crowding_feasible_survival(self, p_list: np.array, f_list: np.array, constraint_violations: np.array, n_survive: int):
        """
        Use the feasible solutions survival strategy
        :param p_list: problem solutions
        :param f_list: related points in the objectives space
        :param constraint_violations: an array containing all the constraint violations; each of them is related to a point in p_list
        :param n_survive: maximum number of survivals
        :return: p_list, f_list, constraint_violations, rank_list and crowding_list with only n_survive items

        Notes:  The solutions are sorted based, first, on their ranks, and, then, on their crowding distances
                If the number of solutions is greater than n_survive, then some solutions are dropped: in particular, the ones with the highest ranks or, in case of equal rank, with the lowest crowding distances are removed
        """

        survivors = []
        rank_list = np.array([[None]] * len(p_list))
        crowding_list = np.array([[None]] * len(p_list))

        fronts = non_dominated_sorting(f_list, n_survive)

        for k, front in enumerate(fronts):
            crowding_of_front = calc_crowding_distance(f_list[front, :])

            for j, i in enumerate(front):
                rank_list[i] = k
                crowding_list[i] = crowding_of_front[j]

            if len(survivors) + len(front) > n_survive:
                P = np.random.permutation(len(crowding_of_front))
                I = np.argsort(crowding_of_front[P], kind='quicksort')
                I = P[I]
                I = np.flip(I, axis=0)
                I = I[:(n_survive - len(survivors))]
            else:
                I = np.arange(len(front))

            survivors.extend(front[I])

        return p_list[survivors], f_list[survivors], constraint_violations[survivors], rank_list[survivors].astype(int), crowding_list[survivors].astype(float)

    def get_survivals(self, p_list: np.array, f_list: np.array, constraint_violations: np.array):
        """
        Execute the survival strategy
        :param p_list: problem solutions
        :param f_list: related points in the objectives space
        :param constraint_violations: an array containing all the constraint violations; each of them is related to a point in p_list
        :return: p_list, f_list, constraint_violations, rank_list and crowding_list

        Notes:  It divides the solutions in feasible and infeasible ones through the function split_by_feasibility(np.array)
                If there are no feasible solutions, then the function least_infeasible_survival(np.array, np.array, np.array, int) is called to get the survivals among the infeasible points
                Otherwise, the function ranking_and_crowding_feasible_survival(np.array, np.array, np.array, int) is first called to get the survivals among the feasible solutions
                Then, the function least_infeasible_survival(np.array, np.array, np.array, int) is called to get the last survivals among the infeasible points
                The number of maximum survivals is chosen based on the size of p_list and the population size
                For each survived infeasible solution, the associated rank and crowding distance are equal to infinity
        """

        index_not_nan_inf_f = np.logical_and(~np.isnan(np.sum(f_list, axis=1)), ~np.isinf(np.sum(f_list, axis=1)))
        p_list = p_list[index_not_nan_inf_f, :]
        f_list = f_list[index_not_nan_inf_f, :]
        constraint_violations = constraint_violations[index_not_nan_inf_f]

        n_survive = min(self._pop_size, len(p_list))

        feasible, infeasible = split_by_feasibility(constraint_violations)

        p_list_f, f_list_f, constraint_violations_f, rank_list_f, crowding_list_f = None, None, None, None, None
        p_list_inf, f_list_inf, constraint_violations_inf = None, None, None

        if len(feasible) == 0:
            p_list_inf, f_list_inf, constraint_violations_inf = self.least_infeasible_survival(p_list[infeasible], f_list[infeasible], constraint_violations[infeasible], min(len(infeasible), n_survive))
        else:
            p_list_f, f_list_f, constraint_violations_f, rank_list_f, crowding_list_f = self.ranking_and_crowding_feasible_survival(p_list[feasible], f_list[feasible], constraint_violations[feasible], min(len(feasible), n_survive))

            n_infeasible_survive = n_survive - len(p_list_f)

            if n_infeasible_survive > 0:
                p_list_inf, f_list_inf, constraint_violations_inf = self.least_infeasible_survival(p_list[infeasible], f_list[infeasible], constraint_violations[infeasible], min(len(infeasible), n_infeasible_survive))

        if p_list_f is not None and p_list_inf is not None:
            p_list_s = np.concatenate((p_list_f, p_list_inf), axis=0)
            f_list_s = np.concatenate((f_list_f, f_list_inf), axis=0)
            constraint_violations_s = np.concatenate((constraint_violations_f, constraint_violations_inf), axis=0)
            rank_list_s = np.concatenate((rank_list_f, np.array([[np.inf]] * len(infeasible))), axis=0)
            crowding_list_s = np.concatenate((crowding_list_f, np.array([[np.inf]] * len(infeasible))), axis=0)
        elif p_list_f is not None:
            p_list_s = p_list_f
            f_list_s = f_list_f
            constraint_violations_s = constraint_violations_f
            rank_list_s = rank_list_f
            crowding_list_s = crowding_list_f
        else:
            p_list_s = p_list_inf
            f_list_s = f_list_inf
            constraint_violations_s = constraint_violations_inf
            rank_list_s = np.array([[np.inf]] * len(infeasible))
            crowding_list_s = np.array([[np.inf]] * len(infeasible))

        return p_list_s, f_list_s, constraint_violations_s, rank_list_s, crowding_list_s


from flask import Flask, request, jsonify
from flask_pymongo import PyMongo
from bson.objectid import ObjectId
import socket
app = Flask(__name__)
app.config["MONGO_URI"] = "mongodb://mongo:27017/dev"
mongo = PyMongo(app)
db = mongo.db
@app.route("/")
def index():
    hostname = socket.gethostname()
    return jsonify(
        message="Welcome to MongoDB app! I am running inside {} pod!".format(hostname)
    )

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)