import os
import pickle
import re
import sys
from typing import Any, Dict, List

import numpy as np
import pandas as pd
from argoverse.data_loading.argoverse_forecasting_loader import \
    ArgoverseForecastingLoader
from argoverse.map_representation.map_api import ArgoverseMap
from tqdm import tqdm

from .preprocess_utils.feature_utils import compute_feature_for_one_seq, save_features
from .preprocess_utils.map_utils_vec import save_map

# vectorization
from .vectorization import VectorizedCase

class ArgoverseConvertor(object):

    def __init__(self, cfg):

        self.data_dir = cfg['DATA_DIR']
        self.obs_len = cfg['OBS_LEN']
        self.lane_radius = cfg['LANE_RADIUS']
        self.object_radius = cfg['OBJ_RADIUS']
        self.raw_dataformat = cfg['RAW_DATA_FORMAT']
        self.am = ArgoverseMap()
        self.Afl = ArgoverseForecastingLoader
        self.out_dir = cfg['INTERMEDIATE_DATA_DIR']
        self.save_dir_pretext = cfg['info_prefix']
        self.specific_data_fold_list = cfg['specific_data_fold_list']

        # vectorization
        self.vec_processor = VectorizedCase(cfg['vectorization_cfg'])

    def preprocess_map(self):

        os.makedirs(self.out_dir, exist_ok=True)
        if not os.path.exists(os.path.join(self.out_dir, 'map.pkl')):
            print("Processing maps ...")
            save_map(self.out_dir)
            print('Map is save at '+ os.path.join(self.out_dir, 'map.pkl'))

    def process(self,):

        # preprocess the map
        self.preprocess_map()

        # storage the case infomation
        data_info = {}

        for folder in os.listdir(self.data_dir):

            if folder not in self.specific_data_fold_list:
                continue

            afl = self.Afl(os.path.join(self.data_dir, folder, 'data'))
            info_dict = {}
            data_info[folder] = {}

            for path_name_ext in tqdm(afl.seq_list):

                afl_ = afl.get(path_name_ext)
                path, name_ext = os.path.split(path_name_ext)
                name, ext = os.path.splitext(name_ext)

                info_dict[name] = self.process_case(afl_.seq_df)

            out_path = os.path.join(
                self.out_dir,  self.save_dir_pretext + f'{folder}.pkl')
            with open(out_path, 'wb') as f:
                pickle.dump(info_dict, f, pickle.HIGHEST_PROTOCOL)

            data_info[folder]['sample_num'] = len(afl.seq_list)
            print('Data is save at ' + out_path)

        # print info
        print("Finish Preprocessing.")
        for k in data_info.keys():
            print('dataset name: ' + k +
                  '\n sample num: {}'.format(data_info[k]['sample_num']))

    def preprocess_case(self, seq_df):
        '''
            Args:
                seq_df:

        '''
        # retrieve info from csv
        agent_feature, obj_feature_ls, nearby_lane_ids, norm_center, city_name =\
            compute_feature_for_one_seq(
                seq_df,
                self.am,
                self.obs_len,
                self.lane_radius,
                self.object_radius,
                self.raw_dataformat,
                viz=False,
                mode='nearby'
            )

        # pack as the output
        dic = save_features(
            agent_feature, obj_feature_ls, nearby_lane_ids, norm_center, city_name
        )

        return dic

    def process_case(self, seq_df):

        # tensorized
        data = self.preprocess_case(seq_df)
        # vectorized
        vec_dic = self.vec_processor.process_case(data)

        return vec_dic

if __name__ == '__main__':

    import argparse
    parser = argparse.ArgumentParser(
        description='Preprocess argoverse dataset')
    parser.add_argument('config', help='config file path')
    args = parser.parse_args()
    from config.Config import Config
    cfg = Config.fromfile(args.config)
    preprocess_cfg = cfg.get('preprocess_dataset')
    processor = ArgoverseConvertor(preprocess_cfg)
    processor.process()


import torch as t
from torch.utils.data import Dataset, DataLoader
import os
from torchvision.io import read_image, decode_image
import random
import pandas as pd
import numpy as np
import json

class ImgPairDataset(Dataset):

    def __init__(self, path):
        super(ImgPairDataset, self).__init__()
        self.width = 1024
        self.height = 384

        lvl1_subfolders = [path]
        lvl2_subfolders = []
        for subfolder in lvl1_subfolders:
            lvl2_subfolder = [f.path for f in os.scandir(subfolder) if f.is_dir() and "season" in f.name]
            lvl2_subfolders.append(lvl2_subfolder)
        images_displacements = {}
        for subfolder in lvl2_subfolders:
            for subsubfolder in subfolder:
                os.path.join(subsubfolder, "displacements.txt")
                file = open(os.path.join(subsubfolder, "displacements.txt"), 'r')
                displacements = []
                for idx, line in enumerate(file):
                    try:
                        displacements.append(int(line.split(" ")[0]))
                    except:
                        # ! it means that data is in float form. because of other codes output
                        displacements.append(int(float(line.split(" ")[0])))
                images_displacements[subsubfolder] = displacements
        season_pairs = []
        for subsubfolder in lvl2_subfolders:
            res = [(a, b) for idx, a in enumerate(subsubfolder) for b in subsubfolder[idx + 1:]]
            season_pairs.extend(res)
        self.annotated_img_pairs = []
        for pair in season_pairs:
            generated_triplets = [(os.path.join(pair[0], str(idx).zfill(9)) + ".jpg",
                                   os.path.join(pair[1], str(idx).zfill(9)) + ".jpg",
                                   images_displacements[pair[1]][idx] - images_displacements[pair[0]][idx])
                                  for idx in range(len(images_displacements[pair[0]]))]
            self.annotated_img_pairs.extend(generated_triplets)

    def __len__(self):
        return len(self.annotated_img_pairs)

    def __getitem__(self, idx):
        # if random.random() > 0.0:
        a, b = 0, 1
        #    displacement = self.annotated_img_pairs[idx][2]
        # else:
        #    b, a = 0, 1
        displacement = -self.annotated_img_pairs[idx][2]

        source_img = read_image(self.annotated_img_pairs[idx][a]) / 255.0
        target_img = read_image(self.annotated_img_pairs[idx][b]) / 255.0
        return source_img, target_img, displacement

class CroppedImgPairDataset(ImgPairDataset):

    def __init__(self, crop_width, fraction, smoothness, path="/home/zdeeno/Documents/Datasets/grief_jpg",
                 transforms=None, dataset=None, allign_flag=False):
        super(CroppedImgPairDataset, self).__init__(path=path, dataset=dataset)
        self.crop_width = crop_width
        self.fraction = fraction
        self.smoothness = smoothness
        self.tr = transforms
        self.allign_flag = allign_flag

    def __getitem__(self, idx):
        source, target, displac = super(CroppedImgPairDataset, self).__getitem__(idx)
        displac = displac / 2
        if self.tr is not None:
            source = self.tr(source)
            target = self.tr(target)
        if self.allign_flag:
            source, target = self.allign(source, target, displac)
        cropped_target, crop_start = self.crop_img(target)
        if self.smoothness == 0:
            heatmap = self.get_heatmap(crop_start, displac)
        else:
            heatmap = self.get_smooth_heatmap(crop_start, displac)
        return source, cropped_target, heatmap

    def allign(self, source, target, displac):
        dsp = int(displac)
        mid = self.width // 2
        if dsp < 0:  # ! not sure about sign here // positive is displacement to right
            source = source[:, :, 0:dsp]
            target = target[:, :, -dsp:]
        else:
            source = source[:, :, dsp:]
            target = target[:, :, 0:-dsp]
        return source, target

    def crop_img(self, img):
        crop_start = random.randint(self.crop_width, self.width - 2 * self.crop_width - 1)
        return img[:, :, crop_start:crop_start + self.crop_width], crop_start

    def get_heatmap(self, crop_start, displacement):
        frac = self.width // self.fraction - 1
        heatmap = t.zeros(frac).long()
        idx = int((crop_start - displacement + self.crop_width // 2) / self.fraction)
        if 0 <= idx < 31:
            heatmap[idx] = 1
        return heatmap

    def get_smooth_heatmap(self, crop_start, displacement):
        surround = self.smoothness * 2
        frac = self.width // self.fraction - 1
        heatmap = t.zeros(frac + surround)
        idx = int((crop_start - displacement + self.crop_width // 2) / self.fraction) + 3
        idxs = t.tensor([-1, +1])
        for i in range(self.smoothness):
            for j in idx + i * idxs:
                if 0 <= j < heatmap.size(0):
                    heatmap[j] = 1 - i * (1 / self.smoothness)
        return heatmap[surround // 2:-surround // 2]

def test_annotations():
    data_name = "stromovka"
    dataset = ImgPairDataset(dataset=data_name)
    df = pd.read_csv("/home/zdeeno/Downloads/new_annot.csv")
    old_displac = np.zeros(500)
    new_displac = np.zeros(500)
    for pair in dataset.annotated_img_pairs:
        img_idx = int(pair[0].split("/")[-1].split(".")[0])
        old_displac[img_idx] = pair[2]
    for entry in df.iterrows():
        json_str = entry[1]["meta_info"].replace("'", "\"")
        entry_dict = json.loads(json_str)
        if entry_dict["dataset"] == data_name:
            img_idx = int(entry_dict["place"])
            diff = 0
            kp_dict1 = json.loads(entry[1]["kp-1"].replace("'", "\""))
            kp_dict2 = json.loads(entry[1]["kp-2"].replace("'", "\""))
            for kp1, kp2 in zip(kp_dict1, kp_dict2):
                diff += (kp1["x"] / 100) * 1024 - (kp2["x"] / 100) * 1024
            new_displac[img_idx] = diff // len(kp_dict1)
    old_displac = old_displac[new_displac != 0]
    new_displac = new_displac[new_displac != 0]
    return old_displac, new_displac

class RectifiedEULongterm(ImgPairDataset):

    def __init__(self, crop_width, fraction, smoothness, path=None):
        super(RectifiedEULongterm, self).__init__(path)
        self.crop_width = crop_width
        self.fraction = fraction
        self.smoothness = smoothness
        self.center_mask = 48
        self.flip = K.Hflip()

    def __getitem__(self, idx):
        source, target, displacement = super(RectifiedEULongterm, self).__getitem__(idx)
        # flipping with displacement?
        # source, target = self.augment(source, target)

        cropped_target, crop_start = self.crop_img(target, displacement)
        if self.smoothness == 0:
            heatmap = self.get_heatmap(crop_start)
        else:
            heatmap = self.get_smooth_heatmap(crop_start)
        return source, cropped_target, heatmap

    def set_crop_size(self, crop_size, smoothness=None):
        self.crop_width = crop_size
        if smoothness is None:
            self.smoothness = (crop_size // 8) - 1
        else:
            self.smoothness = smoothness

    def crop_img(self, img, displac):
        # crop - avoid asking for unavailable crop
        if displac >= 0:
            crops = [random.randint(0 + displac, int(self.width - self.crop_width - 1))]
        else:
            crops = [random.randint(0, int(self.width - self.crop_width - 1) + displac)]

        crop_start = random.choice(crops)
        crop_out = crop_start - displac
        # crop_start = random.randint(0, self.width - self.crop_width - 1)
        return img[:, :, crop_start:crop_start + self.crop_width], crop_out

    def get_heatmap(self, crop_start):
        frac = self.width // self.fraction
        heatmap = t.zeros(frac).long()
        idx = int((crop_start + self.crop_width // 2) * (frac / self.width))
        heatmap[idx] = 1
        heatmap[idx + 1] = 1
        return heatmap

    def get_smooth_heatmap(self, crop_start):
        surround = self.smoothness * 2
        frac = self.width // self.fraction
        heatmap = t.zeros(frac + surround)
        idx = int((crop_start + self.crop_width // 2) * (frac / self.width)) + self.smoothness
        heatmap[idx] = 1
        idxs = np.array([-1, +1])
        for i in range(1, self.smoothness + 1):
            indexes = list(idx + i * idxs)
            for j in indexes:
                if 0 <= j < heatmap.size(0):
                    heatmap[j] = 1 - i * (1 / (self.smoothness + 1))
        return heatmap[surround // 2:-surround // 2]

    def augment(self, source, target):
        # crop the logo - this for some reason makes the network diverge on evaluation set
        # source = source[:, 30:, :]
        # target = target[:, 30:, :]
        source[:, :32, -64:] = (t.randn((3, 32, 64)) / 4 + 0.5).clip(0.2, 0.8)
        if random.random() > 0.95:
            target = source.clone()
        if random.random() > 0.5:
            source = self.flip(source)
            target = self.flip(target)
        return source.squeeze(0), target

if __name__ == '__main__':
    # dataset = CroppedImgPairDataset(64, 8, 3)
    # a, b, c = dataset[0]
    # plot_samples(a, b, c)
    old, new = test_annotations()


from copy import copy

class Heap(object):

    def __init__(self, lst=None):
        if lst:
            if isinstance(lst, list):
                self._heap = lst
                self.heapify()
            else:
                raise TypeError('wrong input type')
        else:
            self._heap = []

    def __repr__(self):
        return str(self._heap)

    __str__ = __repr__

    def is_empty(self):
        return len(self._heap) == 0

    def find_min(self):
        if self._heap:
            return self._heap[0]

    def insert(self, e):
        self._heap.append(e)
        self._percolateup(0, len(self._heap) - 1)

    def _percolateup(self, start, pos):

        last = self._heap[pos]
        while pos > start and last < self._heap[(pos - 1) // 2]:
            self._heap[pos] = self._heap[(pos - 1) // 2]
            pos = (pos - 1) // 2
        self._heap[pos] = last

    def delete_min(self):
        last = self._heap.pop()
        if self._heap:
            item = self._heap[0]
            self._heap[0] = last
            self._percolatedown(0)
            return item
        return last

    def _percolatedown(self, pos):
        e = self._heap[pos]
        startpos = pos
        lastpos = len(self._heap) - 1
        childpos = 2*pos + 1
        while childpos < lastpos:
            rightchild = childpos + 1
            if rightchild <= lastpos and self._heap[rightchild] < self._heap[childpos]:
                childpos = rightchild
            if self._heap[childpos] < e:
                self._heap[pos] = self._heap[childpos]
            else:
                break
            pos = childpos
            childpos = 2*pos + 1
        self._heap[pos] = e

    def heapify(self):
        for i in reversed(range(len(self._heap)//2)):
            self._percolatedown(i)

    def heapsort(self):
        self.heapify()

        res = []
        for i in reversed(range(len(self._heap))):
            res.append(self.delete_min())
        return res

if __name__ == "__main__":
    # h = Heap()
    # h.insert(5)
    # h.insert(7)
    # h.insert(3)
    # h.insert(1)
    # h.insert(6)
    # print(h)
    # for _ in range(5):
    #     h.delete_min()
    #     print(h)

    # k = Heap([4, 3, 2, 7, 1])
    k = Heap([5, 7, 3, 1, 6])
    print(k)
    res = k.heapsort()
    print(res)


import os

import tornado.ioloop
import tornado.web

from nlp.luNlp import analyze_wod
from nlp.trackable import Builder

class MainHandler(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers', 'Content-Type')

    def get(self):
        self.write({'status': 'ok'})

    def post(self):
        wod = self.request.body
        if not wod:
          raise tornado.web.HTTPError(400)
        result = analyze_wod(wod)
        b = Builder.build(result)
        self.set_header('Content-Type', 'application/json')
        self.write({
          'input': wod,
          'output': b.todict(),
        })

    def options(self):
        self.set_status(204)
        self.finish()

def application():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = application()
    app.listen(os.environ.get('PORT', 8888))
    tornado.ioloop.IOLoop.current().start()


import streamlit as st
import spacy_streamlit
# spacy_model = "en_core_web_sm"
import spacy
nlp = spacy.load('en')

from PIL import Image
import os

@st.cache
def load_image(img):
	im = Image.open(img)
	return im

# Layout Templates
title_temp ="""

SpaCy Streamlit

	"""

def main():
	st.title("SpaCy Streamlit  APP")
	# st.markdown(title_temp,unsafe_allow_html=True)
	our_image = Image.open(os.path.join('SpaCy_logo.svg.png'))
	st.image(our_image)

	menu = ['Home','NER']
	choice = st.sidebar.selectbox('Menu',menu)

	if choice == 'Home':
		st.subheader('Home')
		raw_docx = st.text_area('Your Docs','Enter Text')
		docx = nlp(raw_docx)
		if st.button("Tokenize"):
			spacy_streamlit.visualize_tokens(docx, attrs=["text", "pos_", "dep_", "ent_type_"])

	elif choice == 'NER':
		st.subheader('Named Entity Recognizer')
		raw_docx = st.text_area('Your Text','Enter Text')
		docx = nlp(raw_docx)
		# if st.button('Analyze'):
		spacy_streamlit.visualize_ner(docx, labels=nlp.get_pipe("ner").labels)

		# raw_file = st.file_uploader('Upload Text',type=['txt'])
		# if raw_file is not None:
		# 	docx_file = nlp(open(raw_file).read())
		# 	if st.button('Ner Analyze'):
		# 		spacy_streamlit.visualize_ner(docx_file, labels=nlp.get_pipe("ner").labels)

if __name__ == '__main__':
	main()


import os
from operator import itemgetter
from datetime import datetime
from django.core.management.base import BaseCommand
from django.conf import settings
from inventory.models import Article

class Command(BaseCommand):
    """
    The script read the pictures and create instances of 'ArticleBase'.
    """
    help = 'Load the pictures to create one instance of "ArticleBase" per picture.'

    def handle(self, *args, **options):
        pictures_dir = os.path.join(settings.MEDIA_ROOT, 'tmp')
        target_dir = os.path.join(settings.MEDIA_ROOT, 'articles')
        files = os.listdir(pictures_dir)
        nb = 0
        for f in files:
            nb += 1
            print(f)
            source_path = os.path.join(pictures_dir, f)
            print('source path:', source_path)
            target_path = os.path.join(target_dir, f)
            print('target path:', target_path)
            a = ArticleBase(photo=os.path.join('articles', f))
            a.save()
            print('ArticleBase created.')
            print('Moving picture from tmp to articles...')
            os.rename(source_path, target_path)


from flask import Flask, jsonify
from flask_cors import CORS
from database import db
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import os

app = Flask(__name__)
CORS(app)              # Cross-origin enabled

@app.route('/', methods=['GET'])
def index():
    return "Hello from API"

@app.route('/all', methods=['GET'])
def all():
    db.Connect()
    mediciones = db.getCollection()
    if mediciones is None:
       return jsonify({'error': 'No se encontraron datos'})
    lista_mediciones = list(mediciones)
    dict_mediciones = [doc for doc in lista_mediciones]
    db.Close()
    # print(dict_mediciones)
    return jsonify(dict_mediciones)

@app.route('/last', methods=['GET'])
def last():
    db.Connect()
    medicion = db.getLastValue()
    if medicion is None:
       return jsonify({'error': 'No se encontraron datos'})
    # print(dict_mediciones)
    return jsonify(medicion)

@app.route('/graph', methods=['GET'])
def graph():
    name = 'frontend/Temperature.png'
    if os.path.exists(name):
        os.remove(name)
    name = 'frontend/Lumen.png'
    if os.path.exists(name):
        os.remove(name)
    name = 'frontend/Humidity.png'
    if os.path.exists(name):
        os.remove(name)
    name = 'frontend/CO2.png'
    if os.path.exists(name):
        os.remove(name)
    db.Connect()
    mediciones = db.getCollection()
    if mediciones is None:
        return jsonify({'error': 'No se encontraron datos'})
    lista_mediciones = list(mediciones)
    # Creamos nuestras listas para luego separar nuestras graficas
    fechas = []
    temps = []
    lums = []
    hums = []
    cos = []
    for medicion in lista_mediciones:
        fechas.append(medicion['Fecha'])
        temps.append(medicion['Temperature'])
        lums.append(medicion['Lumen'])
        hums.append(medicion['Humidity'])
        cos.append(medicion['CO2'])
    # Grafica de temperatura
    plt.plot(fechas, temps, marker='o', linestyle='-', color='r')
    plt.xlabel('Fecha')
    plt.ylabel('Temperatura')
    plt.title('Temperatura vs Tiempo')
    plt.savefig('frontend/Temperature.png')
    plt.close()
    # Grafica de Luminosidad
    plt.plot(fechas, lums, marker='o', linestyle='-', color='y')
    plt.xlabel('Fecha')
    plt.ylabel('Luminosidad')
    plt.title('Luminosidad vs Tiempo')
    plt.savefig('frontend/Lumen.png')
    plt.close()
    # Grafica de Humedades
    plt.plot(fechas, hums, marker='o', linestyle='-', color='b')
    plt.xlabel('Fecha')
    plt.ylabel('Humedad')
    plt.title('Humedad vs Tiempo')
    plt.savefig('frontend/Humidity.png')
    plt.close()
    # Grafica de Humedades
    plt.plot(fechas, cos, marker='o', linestyle='-', color='g')
    plt.xlabel('Fecha')
    plt.ylabel('Calidad del aire')
    plt.title('Calidad del aire vs Tiempo')
    plt.savefig('frontend/CO2.png')
    plt.close()
    return jsonify({'status': 'imagenes creadas correctamente'})

def start():
    app.run()


a= 'hgfgfhfgmonth01987uyghhgdate2jhgfhgfgyear2021987987'
# '2021-01-23'
a=a.lower()

b=a.index("month")+5
if a[b+1].isnumeric():
  month=a[b:b+2]
else:
  month=a[b]

c=a.index("year")+4
year=a[c:c+4]

d=a.index("date")+4
if a[d+1].isnumeric():
  date=a[d:d+2]
else:
  date=a[d]

print(f"{year}-{month}-{date}")


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('historias', '0002_auto_20170329_1809'),
        ('laboratorios', '0003_auto_20170610_1158'),
    ]

    operations = [
        migrations.CreateModel(
            name='Recepcion',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('estado', models.CharField(default='TO', max_length=2, verbose_name='Estado', choices=[('TO', 'TOMA DE MUESTRA'), ('EC', 'EN CURSO'), ('RE', 'RESULTADO EMITIDO')])),
                ('orden', models.OneToOneField(related_name='recepcion', verbose_name='Orden', to='historias.orden')),
            ],
        ),
    ]


"""
Adversarial in-processing algorithm by Zhang et al. (2018) [DOI: https://doi.org/10.1145/3278721.3278779]

Code adapted from https://github.com/choprashweta/Adversarial-Debiasing and
																https://github.com/abacusai/intraprocessing_debiasing
"""
import time

import logging

import copy

import numpy as np

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import optim

from models.networks_ChestXRay import (ChestXRayVGG16, ChestXRayResNet18)

from utils.evaluation import compute_empirical_bias, compute_accuracy_metrics

from models.networks_tabular import load_model
from utils.evaluation import get_valid_objective, get_test_objective, get_best_thresh

logger = logging.getLogger("Debiasing")

class Classifier(nn.Module):
	def __init__(self, config):
		super(Classifier, self).__init__()

		self.arch = config['default']['arch']

		if config['default']['arch'] == 'vgg':
			self.base_model = ChestXRayVGG16(pretrained=config['default']['pretrained'])
		elif config['default']['arch'] == 'resnet':
			self.base_model = ChestXRayResNet18(pretrained=config['default']['pretrained'])
		else:
			NotImplementedError('Architecture not supported!')

	def forward(self, t, training_mode=False):
		if training_mode:
			if self.arch == 'vgg':
				classifier_prev_output = torch.relu(self.base_model.vgg16(t))
			elif self.arch == 'resnet':
				classifier_prev_output = torch.relu(self.base_model.resnet18(t))

			classifier_output = self.base_model.out(classifier_prev_output)
			return classifier_output, classifier_prev_output
		else:
			if self.arch == 'vgg':
				classifier_prev_output = torch.relu(self.base_model.vgg16(t))
			elif self.arch == 'resnet':
				classifier_prev_output = torch.relu(self.base_model.resnet18(t))

			classifier_output = self.base_model.out(classifier_prev_output)
			return torch.sigmoid(classifier_output)

class Adversary(nn.Module):
	def __init__(self):
		super(Adversary, self).__init__()

		self.a1 = nn.Linear(1000, 512)
		self.a2 = nn.Linear(512, 1)

	def forward(self, input_ids):
		adversary = F.relu(self.a1(input_ids))
		adversary_output = self.a2(adversary)
		return adversary_output

def train_adversary(adv, clf, optimizer_adv, train_loader, loss_criterion, device, n_steps):
	adv_loss = 0
	steps = 0

	for i, data in enumerate(train_loader):
		if steps > n_steps:
			break
		# get the inputs and labels
		inputs, labels, attrs = data
		inputs = inputs.to(device)
		labels = labels.to(device)
		attrs = attrs.to(device)

		optimizer_adv.zero_grad()

		classifier_output, classifier_prev_output = clf(inputs, training_mode=True)
		adversary_output = adv(classifier_prev_output)
		# NOTE: this adversarial loss is only applicable for the EOD (see the original paper by Zhang et al.)
		adversary_loss = loss_criterion(torch.sigmoid(adversary_output[labels == 1])[:, 0], attrs[labels == 1].float())
		adversary_loss.backward()
		optimizer_adv.step()
		adv_loss += adversary_loss.item()
		steps += 1

	return adv

def train_classifier(clf, optimizer_clf, adv, train_loader, loss_criterion, lbda, device, n_steps):
	for i, data in enumerate(train_loader):
		if i > n_steps:
			break
		# get the inputs and labels
		inputs, labels, attrs = data
		inputs = inputs.to(device)
		labels = labels.to(device)
		attrs = attrs.to(device)

		optimizer_clf.zero_grad()

		classifier_output, classifier_prev_output = clf(inputs, training_mode=True)
		adversary_output = adv(classifier_prev_output)
		# NOTE: this adversarial loss is only applicable for the EOD (see the original paper by Zhang et al.)
		adversary_loss = loss_criterion(torch.sigmoid(adversary_output[labels == 1])[:, 0], attrs[labels == 1].float())
		classifier_loss = loss_criterion(torch.sigmoid(classifier_output)[:, 0], labels.float())
		total_classifier_loss = classifier_loss - lbda * adversary_loss
		total_classifier_loss.backward()

		optimizer_clf.step()

	return clf

def train_fair_ChestXRay_model(dataloaders, dataset_sizes, device, config, bias_metric='spd', batch_size=256,
							   num_epochs=25):
	since = time.time()

	model = Classifier(config=config)
	adversary = Adversary()

	model.to(device)
	adversary.to(device)

	optimizer_clf = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-8)
	optimizer_adv = optim.AdamW(adversary.parameters(), lr=1e-4, weight_decay=1e-8)

	loss_criterion_clf = nn.BCELoss()
	loss_criterion_adv = nn.BCELoss()

	# Best model parameters
	best_model_wts = copy.deepcopy(model.state_dict())
	best_loss = np.Inf
	best_acc = 0
	best_bias = 0
	train_acc = []
	val_acc = []
	train_loss = []
	val_loss = []
	train_bias = []
	val_bias = []

	# Iterate over epochs
	for epoch in range(num_epochs):
		print('Epoch {}/{}'.format(epoch, num_epochs - 1))
		print('-' * 10)

		# Each epoch has a training and validation phase
		for phase in ['train', 'val']:
			if phase == 'train':
				model.train()
				adversary.train()

				for param in model.parameters():
					param.requires_grad = False
				adversary = train_adversary(adv=adversary, clf=model, optimizer_adv=optimizer_adv,
											train_loader=dataloaders['train'], loss_criterion=loss_criterion_adv,
											device=device, n_steps=100)

				for param in model.parameters():
					param.requires_grad = True

				for param in adversary.parameters():
					param.requires_grad = False

				model = train_classifier(clf=model, optimizer_clf=optimizer_clf, adv=adversary,
										 train_loader=dataloaders['train'], loss_criterion=loss_criterion_clf,
										 lbda=config['mitigating']['lbda'], device=device, n_steps=100)

				for param in adversary.parameters():
					param.requires_grad = True
			else:

				model.eval()
				adversary.eval()

				# Running parameters
				running_loss = 0.0
				running_corrects = 0
				preds_vec = np.zeros(dataset_sizes[phase])
				labels_vec = np.zeros(dataset_sizes[phase])
				probs_mat = np.zeros((dataset_sizes[phase]))
				priv = np.zeros(dataset_sizes[phase])
				cnt = 0

				# Iterate over data
				for inputs, labels, attrs in dataloaders[phase]:
					# Send inputs and labels to the device
					inputs = inputs.to(device)
					labels = labels.to(device).to(torch.float)
					attrs = attrs.to(device)

					# Zero the gradients
					optimizer_clf.zero_grad()

					# Forward
					with torch.set_grad_enabled(False):
						outputs, outputs_prev = model(inputs, training_mode=True)
						adversary_output = adversary(outputs_prev)
						preds = outputs[:, 0] > 0.5
						loss = loss_criterion_clf(torch.sigmoid(outputs)[:, 0], labels.float())
						if torch.sum(labels == 1) > 0:
							loss = loss - config['mitigating']['lbda'] * loss_criterion_adv(
								torch.sigmoid(adversary_output[labels == 1])[:, 0], attrs[labels == 1].float())
						probs_mat[cnt * batch_size:(cnt + 1) * batch_size] = outputs[:, 0].cpu().detach().numpy()
						preds_vec[cnt * batch_size:(cnt + 1) * batch_size] = preds.cpu().detach().numpy()
						labels_vec[cnt * batch_size:(cnt + 1) * batch_size] = labels.cpu().detach().numpy()
						priv[cnt * batch_size:(cnt + 1) * batch_size] = attrs.cpu().detach().numpy()

					# Statistics
					running_loss += loss.item() * inputs.size(0)
					running_corrects += torch.sum(preds == labels.data)

					# Increment
					cnt = cnt + 1

				# Get the predictive performance metrics
				auroc, avg_precision, balanced_acc, f1_acc = compute_accuracy_metrics(preds_vec, labels_vec)
				bias = compute_empirical_bias(preds_vec, labels_vec, priv, bias_metric)
				print('Bias:', bias)
				acc = running_corrects.double() / dataset_sizes[phase]

				# Save
				epoch_loss = running_loss / dataset_sizes[phase]
				epoch_acc = balanced_acc

				# Print
				print('{} Loss: {:.4f} Acc: {:.4f} AUROC: {:.4f} Avg. Precision: {:.4f} Balanced Accuracy: {:.4f} '
					  'f1 Score: {:.4f}'.format(phase, epoch_loss, acc, auroc, avg_precision, balanced_acc, f1_acc))

				# Save the accuracy and loss
				val_acc.append(epoch_acc)
				val_loss.append(epoch_loss)

				# Deep copy the model
				if epoch_loss < best_loss:
					best_acc = epoch_acc
					best_loss = epoch_loss
					best_model_wts = copy.deepcopy(model.state_dict())

	# Time
	time_elapsed = time.time() - since
	print('Training complete in {:.0f}m {:.0f}s'.format(
		time_elapsed // 60, time_elapsed % 60))
	print('Best val Loss: {:4f}'.format(best_loss))
	print('Best val Acc: {:4f}'.format(best_acc))
	print('Best val Bias: {:4f}'.format(best_bias))

	# Load the best model weights
	model.load_state_dict(best_model_wts)
	return model, train_acc, train_loss, val_acc, val_loss

def mitigating_debiasing(model_state_dict, data, config, device):
	"""
	In-processing adversarial debiasing for tabular data.
	Adapted from https://github.com/abacusai/intraprocessing_debiasing
	"""
	logger.info('Training Mitigating model.')
	actor = load_model(data.num_features, config.get('hyperparameters', {}))
	actor.load_state_dict(model_state_dict)
	actor.to(device)
	critic = nn.Sequential(
		nn.Linear(32, 32),
		nn.Dropout(0.2),
		nn.ReLU(),
		nn.Linear(32, 32),
		nn.Dropout(0.2),
		nn.ReLU(),
		nn.Linear(32, 32),
		nn.Dropout(0.2),
		nn.ReLU(),
		nn.Linear(32, 2),
		nn.Softmax()
	)
	critic.to(device)
	critic_optimizer = optim.Adam(critic.parameters())
	critic_loss_fn = torch.nn.BCELoss()

	actor_optimizer = optim.Adam(actor.parameters(), lr=config['mitigating']['lr'])
	actor_loss_fn = torch.nn.BCELoss()

	for epoch in range(config['mitigating']['epochs']):
		for param in critic.parameters():
			param.requires_grad = True
		for param in actor.parameters():
			param.requires_grad = False
		actor.eval()
		critic.train()
		for step in range(config['mitigating']['critic_steps']):
			critic_optimizer.zero_grad()
			indices = torch.randint(0, data.X_valid.size(0), (config['mitigating']['batch_size'],))
			cy_valid = data.y_valid_gpu[indices]
			cX_valid = data.X_valid_gpu[indices]
			cp_valid = data.p_valid_gpu[indices]
			with torch.no_grad():
				scores = actor(cX_valid)[:, 0].reshape(-1).cpu().numpy()

			res = critic(actor.trunc_forward(cX_valid))
			loss = critic_loss_fn(res[:, 0], cp_valid.type(torch.float32))
			loss.backward()
			train_loss = loss.item()
			critic_optimizer.step()
			if (epoch % 5 == 0) and (step % 100 == 0):
				logger.info(f'=======> Critic Epoch: {(epoch, step)} loss: {train_loss}')

		for param in critic.parameters():
			param.requires_grad = False
		for param in actor.parameters():
			param.requires_grad = True
		actor.train()
		critic.eval()
		for step in range(config['mitigating']['actor_steps']):
			actor_optimizer.zero_grad()
			indices = torch.randint(0, data.X_valid.size(0), (config['mitigating']['batch_size'],))
			cy_valid = data.y_valid_gpu[indices]
			cX_valid = data.X_valid_gpu[indices]
			cp_valid = data.p_valid_gpu[indices]

			cx_predict = actor(cX_valid)
			loss_pred = actor_loss_fn(cx_predict[:, 0], cy_valid)

			cp_predict = critic(actor.trunc_forward(cX_valid))
			loss_adv = critic_loss_fn(cp_predict[:, 0], cp_valid.type(torch.float32))

			for param in actor.parameters():
				try:
					lp = torch.autograd.grad(loss_pred, param, retain_graph=True)[0]
					la = torch.autograd.grad(loss_adv, param, retain_graph=True)[0]
				except RuntimeError:
					continue
				shape = la.shape
				lp = lp.flatten()
				la = la.flatten()
				lp_proj = (lp.T @ la) * la
				grad = lp - lp_proj - config['mitigating']['alpha'] * la
				grad = grad.reshape(shape)
				param.backward(grad)

			actor_optimizer.step()
			if (epoch % 5 == 0) and (step % 100 == 0):
				logger.info(f'=======> Actor Epoch: {(epoch, step)}')

		if epoch % 5 == 0:
			with torch.no_grad():
				scores = actor(data.X_valid_gpu)[:, 0].reshape(-1, 1).cpu().numpy()
				_, best_mit_obj = get_best_thresh(scores, np.linspace(0, 1, 1001), data, config,
												  margin=config['mitigating']['margin'])
				logger.info(f'Objective: {best_mit_obj}')

	logger.info('Finding optimal threshold for Mitigating model.')
	with torch.no_grad():
		scores = actor(data.X_valid_gpu)[:, 0].reshape(-1, 1).cpu().numpy()

	best_mit_thresh, _ = get_best_thresh(scores, np.linspace(0, 1, 1001), data, config,
										 margin=config['mitigating']['margin'])

	logger.info('Evaluating Mitigating model on best threshold.')
	with torch.no_grad():
		labels = (actor(data.X_valid_gpu)[:, 0] > best_mit_thresh).reshape(-1, 1).cpu().numpy()
	results_valid = get_valid_objective(labels, data, config)
	logger.info(f'Results: {results_valid}')

	with torch.no_grad():
		labels = (actor(data.X_test_gpu)[:, 0] > best_mit_thresh).reshape(-1, 1).cpu().numpy()
	results_test = get_test_objective(labels, data, config)

	return results_valid, results_test


import json
from random import randint

import requests

from flask import Flask

app = Flask(__name__)

poke_url = "https://pokeapi.co/api/v2/berry/?limit=100"
response = requests.get(poke_url).json()

berry_list = []
for berry in response["results"]:
    add_item = []
    berry_name = berry["name"]
    parse_url = berry["url"].split("/")
    berry_id = parse_url[-2]
    add_item.append(berry_name)
    add_item.append(berry_id)
    berry_list.append(add_item)

@app.route('/')
def home_page():
    html = ""
    for berry in berry_list:
        html += r"
" + r'' + berry[0] + r''
    return html

@app.route('/<pokeid>')
def hello_world(pokeid):
    random_choce = pokeid
    selection = 'https://pokeapi.co/api/v2/berry/' + f'{random_choce}'
    request = requests.get(selection)
    mydict = request.json()
    berry_name = str(mydict['item']['name'])
    flavor = str(mydict['flavors'][0]['flavor']['name'])
    item_link = mydict['item']['url']
    item_request = requests.get(item_link)
    itemdict = item_request.json()
    sprite_link = itemdict['sprites']['default']
    my_page = f"""<!DOCTYPE html>

    <meta charset="UTF-8">

    <title>{berry_name}</title>

Berry Name:{berry_name}

Berry Flavor:{flavor}

HOME

"""
    return my_page


"""if-else문"""
print("Tell me your age?")  # 지시문 출력
myage = int(input())  # 입력받은 값을 정수형으로 변환한 후 myage에 할당
if myage<30:  # myage가 30 미만일 경우
    print("Welcome to the Club.")  # 해당 메세지 출력
else:  # 그 이외의 경우 (myage가 30 이상일 경우)
    print("Oh! No. You are not accepted.")  # 해당 메세지 출력
'''
파이썬 if문의 큰 장점 중 하나는 수행 명령의 구분 부분이다.
C언어의 경우 if문은 수행 명령어가 두개 이상일 경우 중괄호를 작성해야하지만 파이썬은 들여쓰기만 제대로 해준다면 중괄호가 필요없다.
여기서 들여쓰기는 space키나 tab키로 코드를 일정 간격을 안쪽으로 넣어주기만하면 정상적으로 작동한다.
'''

"""True와 False의 치환"""
print((3 > 5) < 10)  # True
'''
3은 5보다 작으니 3 > 5 는 False이다. False는 0으로 치환된다.
따라서 이것을 다시 치환하면 (0) < 10 은 True이기 때문에 True가 출력이 된다.
'''

"""논리 연산자"""
a = 8  # a에 8을 할당
b = 5  # b에 5를 할당
print(a == 8 and b == 4)  # False
# a값은 8과 동일하기에 True이지만 b값은 4가 아니기 때문에 False라서 and 연산에 의해 False 출력
print(a > 7 or b > 7)  # True : a값은 7보다 크기 때문에 True, b값은 7보다 작아서 False이지만 or 연산에 의해 True 출력
print(not (a > 7))  # False : a값은 7보다 커서 True이지만 not 연산에 의해 그의 반대인 False 출력

'''
and 연산은 두개의 조건문이 모두 True일 경우 True이고 그렇지 않으면 False이다.
or 연산은 두개의 조건문 중 하나라도 Ture일 경우 True이고 둘 다 False일 경우만 False이다.
not 연산은 True이면 False로 나오고 False이면 True로 나오는 연산결과의 반대값을 도출한다.
'''

"""if-elif-else문"""
score = int(input("Enter your score: "))  # 지시문과 함께 입력을 받고 입력값을 정수형으로 변환한 후 score 변수에 할당

if score >= 90:  # score가 90이상일 경우
    grade = 'A'  # grade 변수에 A 할당
elif score >= 80:  # 앞선 조건문 이외의 경우 중 score가 80이상일 경우 (90미만 80이상일 경우)
    grade = 'B'  # grade 변수에 B 할당
elif score >= 70:  # 앞선 조건문 이외의 경우 중 score가 70이상일 경우 (80미만 70이상일 경우)
    grade = 'C'  # grade 변수에 C 할당
elif score >= 60:  # 앞선 조건문 이외의 경우 중 score가 60이상일 경우 (70미만 60이상일 경우)
    grade = 'D'  # grade 변수에 D 할당
else:  # 앞선 조건문 이외의 모든 경우
    grade = 'F'  # grade 변수에 F 할당

print(grade)  # grade 출력

"""
실습02 : 어떤 종류의 학생인지 맞히기

나이는 (2020 - 태어난 연도 + 1) 로 계산
26세 이하 20세 이상이면 '대학생'
20세 미만 17세 이상이면 '고등학생'
17세 미만 14세 이상이면 '중학생'
14세 미만 8세 이상이면 '초등학생'
그 외의 경우는 '학생이 아닙니다.' 출력
---
당신이 태어난 연도를 입력하세요. <- 입력 대기
1982 <- 자신이 태어난 연도 입력
학생이 아닙니다. <- 어떤 종류의 학생인지 출력
"""

year = int(input("당신이 태어난 연도를 입력하세요.(4자리 수 입력)\n"))
# 연도를 입력받고 해당 입력을 정수형으로 변환 후 year 변수에 할당
age = 2020 - year + 1  # year 값을 사용하여 나이 계산 후 age 변수에 할당

if age < 1 or year < 1:  # age가 1미만이거나 year이 1 미만일 경우
    print("잘못된 입력입니다.")  # 해당 메세지 출력
elif age <= 26 and age >= 20:  # 앞선 조건문 이외의 경우 중 age가 26이하 20이상일 경우
    print("대학생")  # 해당 메세지 출력
elif age < 20 and age >= 17:  # 앞선 조건문 이외의 경우 중 age가 20미만 17이상일 경우
    print("고등학생")  # 해당 메세지 출력
elif age < 17 and age >= 14:  # 앞선 조건문 이외의 경우 중 age가 17미만 14이상일 경우
    print("중학생")  # 해당 메세지 출력
elif age < 14 and age >= 8:  # 앞선 조건문 이외의 경우 중 age가 14미만 8이상일 경우
    print("초등학생")  # 해당 메세지 출력
else:  # 앞선 조건문 이외의 모든 경우
    print("학생이 아닙니다.")  # 해당 메세지 출력

'''
기존의 문제에 작성되어 있는 입력문의 지시문에 조건을 더 추가해서 작성하였습니다.
아직 태어나지 않은 경우나 기원전 기준으로 작성할 시엔 잘못된 입력이라는 출력을 내도록 설정하였습니다.
또한 입력하는 사람을 배려하여 몇 글자를 입력할 지 작성해 두었습니다.
그 이외의 경우는 모두 실습문제에 적힌대로 작성하였습니다.
'''

"""for문"""

for looper in [1, 2, 3, 4, 5]:
    print("hello")  # hello 5번 반복 출력

for looper in [1, 2, 3, 4, 5]:
    print(looper)
'''
1
2
3
4
5
위와 같이 출력
'''

for looper in range(10):
    print("hello")  # hello 10번 반복 출력

'''
for 문에서 리스트를 이용하여 반복되는 범위를 설정할 수 있다.
이것은 [1, 2, 3, 4, 5] 루프에서 각각의 값을 변수 looper에 할당하는데, 할당할 때마다 명령문을 수행한다.
그러나 반복할 횟수가 많다면 리스트를 이용한 범위 설정은 큰 어려움에 부딪힌다.
따라서 사용하는 것이 range라는 키워드를 이용한다. range는 반복하는 횟수만 설정하면 그 횟수만큼 명령문을 수행한다.
range 문법 구조는 for 변수 in range(시작 번호, 마지막 번호, 증가값) 이다.
시작 번호와 마지막 번호는 그 시작 번호의 인덱스부터 마지막 번호의 인덱스까지의 리스트를 형성한다.
위에 작성한 예시와 같이 단독으로 작성되었을 땐, 마지막 번호로 인식하여 0부터 마지막 번호까지 1씩 증가하는 리스트를 형성한다.
여기서 마지막 번호는 '마지막 번호-1'까지 리스트를 생성하는 것임을 유의하자.
'''


import glob
import os
import pandas as pd
import svgwrite
from svgwrite import mm

'''
Description: require python >= 3.6
height bar is read counts (normalized); blue is exon; green is known donor; yellow is known acceptor; purple is novel donor; orange is novel acceptor.
'''

# ==========parameters to be changed==========
# ==========parameters for adjusting images==========
xoffset = 30
yoffset = 30
exon_height = 3
xaxis_length = 150
yaxis_length = 150
# ==========parameters for adjusting images==========
min_junction_reads_threshold = 10
splice_junction_file_suffix = 'SJ.out.tab'
wanted = 'FBgn0033159'  # wanted string which is contained in last column of the GTF file
path_gtf_file = '/home/user/data/dscam_fruit_fly.gtf'  # can be a plain text or compressed file (.gz, .zip, .xz, .bz2)
path_splice_junction_folder = '/home/user/data/fruit_fly_sj_data'
path_output_svg_folder = '/home/user/data/fruit_fly_plotting'
# ==========parameters to be changed==========

def draw_sj_image(df_gtf, df_splice_junction, path_output_svg_file, img_title):
    df_exon_drawing = df_gtf[['start', 'end', 'info']].copy()
    df_exon_drawing['exon_number'] = df_exon_drawing['info'].str.extract(r'exon_number "(.*?)";')
    df_exon_drawing['exon_number_count'] = df_exon_drawing['exon_number'].map(df_exon_drawing['exon_number'].value_counts())
    df_exon_drawing['exon_number_cumulative_count'] = df_exon_drawing.groupby('exon_number').cumcount() + 1    # need to add 1
    df_exon_drawing['exon_numering'] = df_exon_drawing['exon_number']
    df_exon_drawing.loc[df_exon_drawing['exon_number_count'] > 1, 'exon_numering'] = df_exon_drawing.loc[df_exon_drawing['exon_number_count'] > 1, 'exon_number'].astype('str') + '.' + df_exon_drawing.loc[df_exon_drawing['exon_number_count'] > 1, 'exon_number_cumulative_count'].astype('str')

    min_coordinate = df_exon_drawing['start'].min()
    max_coordinate = df_exon_drawing['end'].max()
    global xoffset, yoffset, exon_height, xaxis_length, yaxis_length
    df_exon_drawing['scaled_start'] = (df_exon_drawing['start'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_exon_drawing['scaled_end'] = (df_exon_drawing['end'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_exon_drawing['scaled_width'] = df_exon_drawing['scaled_end'] - df_exon_drawing['scaled_start']
    # draw exons
    dwg = svgwrite.Drawing(path_output_svg_file, profile='tiny')
    for idx_label in df_exon_drawing.index:
        dwg.add(dwg.text(df_exon_drawing.loc[idx_label, 'exon_numering'], insert=(df_exon_drawing.loc[idx_label, 'scaled_start'] + xoffset, yoffset - 2), fill='black', font_size="3.5px"))
        dwg.add(dwg.rect(insert=(df_exon_drawing.loc[idx_label, 'scaled_start'] + xoffset, yoffset), size=(df_exon_drawing.loc[idx_label, 'scaled_width'], exon_height), stroke='black', stroke_width=0.1, fill='lightblue', opacity=0.7))
    # process data for drawing splice junction reads
    df_sj_drawing = df_splice_junction[['start', 'end', 'number of uniquely mapping reads crossing the junction']].copy()
    df_sj_drawing['donor_exon_end'] = df_sj_drawing['start'] - 1
    df_sj_drawing['acceptor_exon_start'] = df_sj_drawing['end'] + 1
    df_sj_drawing.loc[df_sj_drawing['donor_exon_end'].isin(df_exon_drawing['end']), 'is_known_donor'] = True
    df_sj_drawing['is_known_donor'] = df_sj_drawing['is_known_donor'].fillna(False)
    df_sj_drawing.loc[df_sj_drawing['acceptor_exon_start'].isin(df_exon_drawing['start']), 'is_known_acceptor'] = True
    df_sj_drawing['is_known_acceptor'] = df_sj_drawing['is_known_acceptor'].fillna(False)
    df_temp_donor = pd.merge(df_exon_drawing[['start', 'end']], df_sj_drawing[['donor_exon_end']].reset_index(), how='right', left_on=['end'], right_on=['donor_exon_end'])
    df_temp_donor = df_temp_donor.rename({'start': 'donor_exon_start'}, axis=1)
    df_temp_donor = df_temp_donor.set_index('index').drop(['end', 'donor_exon_end'], axis=1)
    df_sj_drawing = pd.concat([df_sj_drawing, df_temp_donor], axis=1)
    df_temp_acceptor = pd.merge(df_exon_drawing[['start', 'end']], df_sj_drawing[['acceptor_exon_start']].reset_index(), how='right', left_on=['start'], right_on=['acceptor_exon_start'])
    df_temp_acceptor = df_temp_acceptor.rename({'end': 'acceptor_exon_end'}, axis=1)
    df_temp_acceptor = df_temp_acceptor.set_index('index').drop(['start', 'acceptor_exon_start'], axis=1)
    df_sj_drawing = pd.concat([df_sj_drawing, df_temp_acceptor], axis=1)

    df_sj_drawing['scaled_donor_exon_start'] = (df_sj_drawing['donor_exon_start'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_sj_drawing['scaled_donor_exon_end'] = (df_sj_drawing['donor_exon_end'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_sj_drawing['scaled_donor_exon_width'] = df_sj_drawing['scaled_donor_exon_end'] - df_sj_drawing['scaled_donor_exon_start']
    df_sj_drawing['scaled_acceptor_exon_start'] = (df_sj_drawing['acceptor_exon_start'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_sj_drawing['scaled_acceptor_exon_end'] = (df_sj_drawing['acceptor_exon_end'] - min_coordinate) / (max_coordinate - min_coordinate) * xaxis_length
    df_sj_drawing['scaled_acceptor_exon_width'] = df_sj_drawing['scaled_acceptor_exon_end'] - df_sj_drawing['scaled_acceptor_exon_start']

    df_sj_drawing['donor_exon_reads'] = df_sj_drawing.groupby('donor_exon_end')[['number of uniquely mapping reads crossing the junction']].transform('sum')
    df_sj_drawing['acceptor_exon_reads'] = df_sj_drawing.groupby('acceptor_exon_start')[['number of uniquely mapping reads crossing the junction']].transform('sum')

    total_reads = df_splice_junction['number of uniquely mapping reads crossing the junction'].sum()
    df_sj_drawing['scaled_donor_exon_reads'] = df_sj_drawing['donor_exon_reads'] / total_reads * yaxis_length
    df_sj_drawing['scaled_acceptor_exon_reads'] = df_sj_drawing['acceptor_exon_reads'] / total_reads * yaxis_length

    # draw splice junction reads. known donor: green; known acceptor: yellow; novel donor: purple; novel acceptor: orange
    for idx_label in df_sj_drawing[df_sj_drawing['is_known_donor'] == True].index:
        dwg.add(dwg.rect(insert=(df_sj_drawing.loc[idx_label, 'scaled_donor_exon_start'] + xoffset, exon_height + yoffset + 1), size=(df_sj_drawing.loc[idx_label, 'scaled_donor_exon_width'], df_sj_drawing.loc[idx_label, 'scaled_donor_exon_reads']), fill='lightgreen', fill_opacity=0.5))
    for idx_label in df_sj_drawing[df_sj_drawing['is_known_acceptor'] == True].index:
        dwg.add(dwg.rect(insert=(df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_start'] + xoffset, exon_height + yoffset + 1), size=(df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_width'], df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_reads']), fill='yellow', fill_opacity=0.5))
    for idx_label in df_sj_drawing[df_sj_drawing['is_known_donor'] == False].index:
        dwg.add(dwg.line(start=(df_sj_drawing.loc[idx_label, 'scaled_donor_exon_end'] + xoffset, exon_height + yoffset + 1), end=(df_sj_drawing.loc[idx_label, 'scaled_donor_exon_end'] + xoffset, df_sj_drawing.loc[idx_label, 'scaled_donor_exon_reads'] + exon_height + yoffset + 1), stroke='purple', stroke_width='0.25', stroke_opacity=1.0))
    for idx_label in df_sj_drawing[df_sj_drawing['is_known_acceptor'] == False].index:
        dwg.add(dwg.line(start=(df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_start'] + xoffset, exon_height + yoffset + 1), end=(df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_start'] + xoffset, df_sj_drawing.loc[idx_label, 'scaled_acceptor_exon_reads'] + exon_height + yoffset + 1), stroke='orange', stroke_width='0.25', stroke_opacity=1.0))
    # add title
    font_size = 5
    title_xstart = xaxis_length / 2 + xoffset - len(img_title) / 2 * font_size * 0.5
    dwg.add(dwg.text(img_title, insert=(title_xstart, yoffset / 2), fill='black', font_size=font_size))
    dwg.save()

os.chdir(path_splice_junction_folder)
list_sj_files = glob.glob(f'*{splice_junction_file_suffix}')

gtf_header_name = ['chr', 'annotation_source', 'feature_type', 'start', 'end', 'score', 'strand', 'genomic_phase', 'info']
df_gtf = pd.read_csv(path_gtf_file, sep='\t', header=None, names=gtf_header_name, dtype={'chr': 'str', 'start': 'Int64', 'end': 'Int64', 'strand': 'str'}, low_memory=False)
df_gtf = df_gtf[df_gtf['feature_type'] == 'exon']
df_gtf = df_gtf[df_gtf['info'].str.contains(wanted)]
df_gtf = df_gtf.drop_duplicates(subset=['chr', 'start', 'end', 'strand'], keep='first')	 # drop duplicate exons except for the first occurrence
chr_name = list(set(df_gtf['chr']))[0]  # splice junction should be at the same chromosome, so do not need to iterate it
strand_name = list(set(df_gtf['strand']))[0]
if strand_name == '-':
    df_gtf['start'], df_gtf['end'] = -df_gtf['end'], -df_gtf['start']

df_all_sj = pd.DataFrame()  # save all splice junction data
splice_junction_header_name = [
    'chr', 'start', 'end', 'strand', 'intron_motif', 'is_annoted', 'number of uniquely mapping reads crossing the junction',
    'number of multi-mapping reads crossing the junction', 'maximum spliced alignment overhang']
for sj_file in list_sj_files:
    path_splice_junction_file = os.path.join(path_splice_junction_folder, sj_file)
    sj_filename_prefix = sj_file[:-len(splice_junction_file_suffix)].rstrip('.')
    path_output_svg_file = os.path.join(path_output_svg_folder, f'{sj_file}.svg')
    df_sj_data = pd.read_csv(path_splice_junction_file, sep='\t', header=None, names=splice_junction_header_name, dtype={'chr': 'str', 'start': 'Int64', 'end': 'Int64', 'strand': 'Int64'}, low_memory=False)
    df_sj_data['image_title'] = sj_filename_prefix
    df_sj_data['path_output_svg_file'] = path_output_svg_file
    df_all_sj = pd.concat([df_all_sj, df_sj_data], axis=0)
df_all_sj = df_all_sj.loc[df_all_sj['strand'] != 0]  # drop splice junctions on undefined strand
df_all_sj = df_all_sj.replace({'strand': {1: '+', 2: '-'}})
df_all_sj = df_all_sj[df_all_sj['strand'] == strand_name]
if strand_name == '-':
    df_all_sj['start'], df_all_sj['end'] = -df_all_sj['end'], -df_all_sj['start']
df_all_sj = df_all_sj[df_all_sj['number of uniquely mapping reads crossing the junction'] >= min_junction_reads_threshold]    # remove low junction reads
# select exons and splice junctions in the right ranges
df_all_sj = df_all_sj[(df_all_sj['chr'] == chr_name) & (df_all_sj['start'] > df_gtf['start'].min()) & (df_all_sj['end'] < df_gtf['end'].max())]
df_gtf = df_gtf[(df_gtf['end'] + 2 > df_all_sj['start'].min()) & (df_gtf['start'] < (df_all_sj['end'].max() + 2))]
df_all_sj = df_all_sj[(df_all_sj['chr'] == chr_name) & (df_all_sj['start'] > df_gtf['start'].min()) & (df_all_sj['end'] < df_gtf['end'].max())]
df_gtf = df_gtf.sort_values(['start', 'end'], ascending=True)
df_all_sj = df_all_sj.sort_values(['start', 'end'], ascending=True)

for path_output_svg_file in set(df_all_sj['path_output_svg_file'].tolist()):
    df_splice_junction = df_all_sj[df_all_sj['path_output_svg_file'] == path_output_svg_file].copy()
    img_title = df_splice_junction['image_title'].tolist()[0]
    df_splice_junction = df_splice_junction.drop(['image_title', 'path_output_svg_file'], axis=1)
    draw_sj_image(df_gtf, df_splice_junction, path_output_svg_file, img_title)


import json
import logging
from functools import reduce
from os.path import join
from typing import Tuple, Iterator, List, Dict, Any

import numpy
import openslide
import pandas
from pandas import DataFrame

from antilles.block import Field, Step, Block
from antilles.pipeline.annotate import annotate_slides
from antilles.project import Project
from antilles.utils.image import get_mpp_from_openslide
from antilles.utils.io import DAO
from antilles.utils.math import pol2cart

def calc_bbox(
    dims: Tuple[int, int], center: Tuple[int, int], angle: float, **kwargs
) -> Tuple[Tuple[int, int], Tuple[int, int]]:
    width, height = dims
    c_x, c_y = center
    span = kwargs.get("span", 90.0)
    radius_inner = kwargs.get("radius_inner", 400)
    radius_outer = kwargs.get("radius_outer", 800)

    n_divisions = 200
    n_div0 = int(n_divisions * float(span) / 360.0)
    n_div1 = n_divisions - n_div0

    hspan = span / 2.0
    angles0 = numpy.linspace(angle - hspan, angle + hspan, n_div0)
    angles1 = numpy.linspace(angle + hspan, angle + 360 - hspan, n_div1)

    min_dx, max_dx = None, None
    min_dy, max_dy = None, None

    for r, angles in zip([radius_outer, radius_inner], [angles0, angles1]):
        for a in angles:
            dx, dy = pol2cart(r, a)

            if min_dx is None:
                min_dx = dx
            if max_dx is None:
                max_dx = dx

            if min_dy is None:
                min_dy = dy
            if max_dy is None:
                max_dy = dy

            min_dx, max_dx = min(min_dx, dx), max(max_dx, dx)
            min_dy, max_dy = min(min_dy, dy), max(max_dy, dy)

    min_x = int(max(c_x + min_dx, 0))
    max_x = int(min(c_x + max_dx, width))
    dx = max_x - min_x

    min_y = int(max(c_y + min_dy, 0))
    max_y = int(min(c_y + max_dy, height))
    dy = max_y - min_y

    return (min_x, min_y), (dx, dy)

def get_extraction_sequence(settings: Dict[str, Any]) -> Iterator[Dict[str, Any]]:
    for key in ["samples", "devices", "coords", "angles"]:
        if key not in settings.keys():
            raise ValueError(f"Key not found: {key}")

    regions = settings["coords"].to_dict("records")
    sample_angles = settings["angles"].to_dict("records")
    sample_angles = {s["sample"]: float(s["angle"]) for s in sample_angles}

    for region in regions:
        # get wells from payload
        device_name = next(
            s["device"] for s in settings["samples"] if s["name"] == region["sample"]
        )
        payload = next(
            d["payload"] for d in settings["devices"] if d["name"] == device_name
        )
        wells = next(l["wells"] for l in payload if l["level"] == region["level"])

        src = region["relpath"]

        for well in wells:
            fields = {
                "project": region["project"],
                "block": region["block"],
                "panel": region["panel"],
                "level": region["level"],
                "sample": region["sample"],
                "cohorts": region["cohorts"],
                "drug": well["drug"],
            }

            params = {
                "center": (int(region["center_x"]), int(region["center_y"])),
                "angle": sample_angles[region["sample"]] + well["angle"],
            }

            yield {"src": src, "fields": fields, "params": params}

def microns2pixels(dct: Dict[str, Any], keys: List[str], mpp: float):
    for key in keys:
        try:
            dct[key] /= mpp
        except TypeError:
            pass
    return dct

def get_filepath(step: Step, fields: Dict[str, Any], output_order: List[str]) -> str:
    for key in ["project", "block", "panel", "level", "sample", "drug"]:
        if key not in fields.keys():
            raise ValueError(f"Key not found: {key}")

    fields["level"] = "LVL" + str(fields["level"])

    dirpath = join(fields["project"], fields["block"], step.value)
    dirpath = join(dirpath, *(fields[o] for o in output_order))
    DAO.make_dir(dirpath)

    filename_order = ["project", "block", "panel", "level", "sample", "drug"]
    filename = "_".join(fields[f] for f in filename_order) + ".tif"
    filepath = join(dirpath, filename)

    fields["level"] = int(fields["level"][len("LVL") :])

    return filepath

def extract_image(src: str, dst: str, params: Dict[str, Any]) -> Dict[str, Any]:
    with openslide.OpenSlide(DAO.abs(src)) as obj:
        dims = obj.dimensions
        mpp = get_mpp_from_openslide(obj)
        params = microns2pixels(params, ["radius_inner", "radius_outer"], mpp)
        origin, size = calc_bbox(dims=dims, **params)

        image = obj.read_region(origin, 0, size)
        image = image.convert("RGB")
        image.save(DAO.abs(dst))

    cx = params["center"][0] - origin[0]
    cy = params["center"][1] - origin[1]

    r_init = 400 / mpp
    dx, dy = pol2cart(r_init, params["angle"])
    wx, wy = int(round(cx + dx)), int(round(cy + dy))

    return {"oxy": origin, "cxy": (cx, cy), "wxy": (wx, wy), "dims": size, "mpp": mpp}

def update_translate(df: DataFrame, using: DataFrame):
    buffer = 5

    cols = ["project", "block", "panel", "level", "sample", "drug"]
    for i, row in df.iterrows():
        ind = (row[col] == using[col] for col in cols)
        ind = reduce((lambda x, y: x & y), ind)
        using_row = using[ind]

        if len(using_row) == 1:
            oxy_old = using_row["origin_x"].values[0], using_row["origin_y"].values[0]
            cxy_old = using_row["center_x"].values[0], using_row["center_y"].values[0]
            wxy_old = using_row["well_x"].values[0], using_row["well_y"].values[0]

            oxy_new = row["origin_x"], row["origin_y"]
            diff_x = oxy_new[0] - oxy_old[0]
            diff_y = oxy_new[1] - oxy_old[1]

            cxy_new = cxy_old[0] - diff_x, cxy_old[1] - diff_y
            wxy_new = wxy_old[0] - diff_x, wxy_old[1] - diff_y
            cxy_new = max(cxy_new[0], buffer), max(cxy_new[1], buffer)
            wxy_new = max(wxy_new[0], buffer), max(wxy_new[1], buffer)

            df.loc[i, ["center_x"]] = cxy_new[0]
            df.loc[i, ["center_y"]] = cxy_new[1]
            df.loc[i, ["well_x"]] = wxy_new[0]
            df.loc[i, ["well_y"]] = wxy_new[1]
            df.loc[i, ["metadata"]] = using_row["metadata"].values[0]

    return df

class Extractor:
    def __init__(self, project: Project, block: Block):
        self.log = logging.getLogger(__name__)
        self.project = project
        self.block = block

    def adjust(self):
        coords = self.block.get(Field.IMAGES_COORDS)
        angles = self.block.get(Field.ANGLES_COARSE)

        annotate_slides(coords, angles)

        self.block.save(coords, Field.IMAGES_COORDS)
        self.block.save(angles, Field.ANGLES_COARSE)

    def extract(self, params: Dict[str, Any]) -> None:
        self.log.info("Extracting wedges ... ")
        self.block.clean()

        regions_prev = self.block.get(Field.IMAGES_COORDS_BOW)
        regions = self.extract_wedges(params["wedge"])
        regions = update_translate(regions, using=regions_prev)

        self.block.save(regions, Field.IMAGES_COORDS_BOW)
        self.log.info("Extracting wedges complete.")

    def extract_wedges(self, params: Dict[str, Any]) -> DataFrame:
        output_order = self.project.config["output_order"]

        settings = {
            "samples": self.block.samples,
            "devices": self.project.config["devices"],
            "coords": self.block.get(Field.IMAGES_COORDS),
            "angles": self.block.get(Field.ANGLES_COARSE),
        }

        regions = []
        regions_to_extract = get_extraction_sequence(settings)
        for region in regions_to_extract:
            src = region["src"]
            dst = get_filepath(Step.S1, region["fields"], output_order)
            props = extract_image(src, dst, {**params, **region["params"]})

            regions.append(
                {
                    **region["fields"],
                    **{
                        "relpath": dst,
                        "origin_x": props["oxy"][0],
                        "origin_y": props["oxy"][1],
                        "center_x": props["cxy"][0],
                        "center_y": props["cxy"][1],
                        "well_x": props["wxy"][0],
                        "well_y": props["wxy"][1],
                        "mpp": props["mpp"],
                        "width": props["dims"][0],
                        "height": props["dims"][1],
                        "metadata": json.dumps({}),
                    },
                }
            )

        columns = [
            "relpath",
            "project",
            "block",
            "panel",
            "level",
            "sample",
            "cohorts",
            "drug",
            "origin_x",
            "origin_y",
            "center_x",
            "center_y",
            "well_x",
            "well_y",
            "mpp",
            "metadata",
        ]
        regions = pandas.DataFrame(regions, columns=columns)
        return regions


from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
import numpy as np

train_images = np.load('train_images.npy')
train_labels = np.load('train_labels.npy')
test_images = np.load('test_images.npy')
test_labels = np.load('test_labels.npy')

train_images_reshaped = np.reshape(train_images, (30000, 28, 28))
test_images_reshaped = np.reshape(test_images, (test_images.shape[0], 28, 28))

scaled_images = np.empty(shape=(30000, 28, 28))
scaled_images_test = np.empty(shape=(5000, 28, 28))
# In order to scale pixel values to [-1, 1], The MaxAbsScaler was used.
counter = 0
scaler = MinMaxScaler(feature_range=(-1, 1))

for x in train_images_reshaped:
    scaled_images[counter, :, :] = scaler.fit_transform(x)
    counter += 1

counter = 0
for x in test_images_reshaped:
    scaled_images_test[counter, :, :] = scaler.fit_transform(x)
    counter += 1

# In order to split 10 percent of the training data set to validation
X_train, X_validate, y_train, y_validate = train_test_split(scaled_images,
                                                    train_labels, test_size=0.1,
                                                    random_state=40, stratify=train_labels)
number_of_epoch = 20

arch_fav = (16, 128, 16)

average_loss_1 = np.zeros(number_of_epoch)
average_loss_01 = np.zeros(number_of_epoch)
average_loss_001 = np.zeros(number_of_epoch)

average_valid_scheduled = np.zeros(number_of_epoch)

average_valid_accuracy_1 = np.zeros(number_of_epoch)
average_valid_accuracy_01 = np.zeros(number_of_epoch)
average_valid_accuracy_001 = np.zeros(number_of_epoch)

counter_for_list = 0
arc_dict = {}

for x in range(0, 1):
    mlp_1 = MLPClassifier(hidden_layer_sizes=arch_fav, activation='relu',
                        solver='sgd', max_iter=1, shuffle=True, learning_rate_init=0.1, momentum=0.0)

    mlp_01 = MLPClassifier(hidden_layer_sizes=arch_fav, activation='relu',
                        solver='sgd', max_iter=1, shuffle=True, learning_rate_init=0.01, momentum=0.0)

    mlp_001 = MLPClassifier(hidden_layer_sizes=arch_fav, activation='relu',
                        solver='sgd', max_iter=1, shuffle=True, learning_rate_init=0.001, momentum=0.0)

    mlp_scheduled = MLPClassifier(hidden_layer_sizes=arch_fav, activation='relu',
                    solver='sgd', max_iter=1, shuffle=True, learning_rate_init=0.1, momentum=0.0)

    # Flatten input data
    nsamples, nx, ny = X_train.shape
    d2_train_dataset = X_train.reshape((nsamples, nx*ny))

    nsamples, nx, ny = X_validate.shape
    d2_validate_dataset = X_validate.reshape((nsamples, nx*ny))

    nsamples, nx, ny = scaled_images_test.shape
    d2_test_dataset = scaled_images_test.reshape((nsamples, nx*ny))

    valid_accuracy_1 = np.empty(shape=(number_of_epoch,))
    valid_accuracy_01 = np.empty(shape=(number_of_epoch,))
    valid_accuracy_001 = np.empty(shape=(number_of_epoch,))

    valid_accuracy_scheduled = np.empty(shape=(number_of_epoch))

    Loss_1 = np.empty(shape=(number_of_epoch,))
    Loss_01 = np.empty(shape=(number_of_epoch,))
    Loss_001 = np.empty(shape=(number_of_epoch,))

    # For loop to 100 epochs
    counter_10_epoch = 0
    for i in range(1, 201):
        # Shuffle training set after each epoch
        random_perm = np.random.permutation(X_train.shape[0])
        mini_batch_index = 0

        if i == 70:
            mlp_scheduled.set_params(learning_rate_init=0.01)

        if i == 120:
            mlp_scheduled.set_params(learning_rate_init=0.001)

        while True:
            indices = random_perm[mini_batch_index:mini_batch_index + 500]
            mlp_1.partial_fit(d2_train_dataset[indices], y_train[indices], np.unique(y_train))
            mlp_01.partial_fit(d2_train_dataset[indices], y_train[indices], np.unique(y_train))
            mlp_001.partial_fit(d2_train_dataset[indices], y_train[indices], np.unique(y_train))
            mlp_scheduled.partial_fit(d2_train_dataset[indices], y_train[indices], np.unique(y_train))
            mini_batch_index += 500

            if mini_batch_index >= X_train.shape[0]:
                break

        if i % 10 == 0:
            valid_accuracy_1[counter_10_epoch] = mlp_1.score(d2_validate_dataset, y_validate)
            valid_accuracy_01[counter_10_epoch] = mlp_01.score(d2_validate_dataset, y_validate)
            valid_accuracy_001[counter_10_epoch] = mlp_001.score(d2_validate_dataset, y_validate)
            valid_accuracy_scheduled[counter_10_epoch] = mlp_scheduled.score(d2_validate_dataset, y_validate)

            Loss_1[counter_10_epoch] = mlp_1.loss_
            Loss_01[counter_10_epoch] = mlp_01.loss_
            Loss_001[counter_10_epoch] = mlp_001.loss_
            counter_10_epoch += 1

    average_loss_1 = (average_loss_1 + Loss_1)
    average_loss_01 = (average_loss_01 + Loss_01)
    average_loss_001 = (average_loss_001 + Loss_001)

    average_valid_accuracy_1 = (average_valid_accuracy_1 + valid_accuracy_1)
    average_valid_accuracy_01 = (average_valid_accuracy_01 + valid_accuracy_01)
    average_valid_accuracy_001 = (average_valid_accuracy_001 + valid_accuracy_001)
    average_valid_scheduled = (average_valid_scheduled + valid_accuracy_scheduled)

arc_dict['name'] = 'arch_3'
arc_dict['loss_curve_1'] = average_loss_1
arc_dict['loss_curve_01'] = average_loss_01
arc_dict['loss_curve_001'] = average_loss_001
arc_dict['val_acc_curve_1'] = average_valid_accuracy_1
arc_dict['val_acc_curve_01'] = average_valid_accuracy_01
arc_dict['val_acc_curve_001'] = average_valid_accuracy_001

from utils import part4Plots
part4Plots(arc_dict, save_dir='', filename='', show_plot=True)

plt.plot(average_valid_scheduled)
plt.ylabel('Validation Accuracy')
plt.show()

print(mlp_scheduled.score(d2_train_dataset, y_train))


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('',include('pages.urls')),
    path('instagram/',include('instagram.urls')),
    path('facebook/',include('facebook.urls')),
    path('twitter/',include('twitter.urls')),
    path('admin/', admin.site.urls),

]


"""
31. 下一个排列
实现获取 下一个排列 的函数，算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列，则将数字重新排列成最小的排列（即升序排列）。
必须 原地 修改，只允许使用额外常数空间。
示例 1：
输入：nums = [1,2,3]
输出：[1,3,2]

示例 2：
输入：nums = [3,2,1]
输出：[1,2,3]

示例 3：
输入：nums = [1,1,5]
输出：[1,5,1]

示例 4：
输入：nums = [1]
输出：[1]
提示：
1 <= nums.length <= 100
0 <= nums[i] <= 100
"""
# 看到这题，并没有想到什么常规的解法来解决。然后在纸上画了画，感觉可能发现了规律，如果我们真的把所有排列罗列出来，太慢了，应该会超时啊
# 规律就是，我罗列了个例子，下一个排列时，我们从后往前找到第一个从小到大的对子， 也就是一对数，左边数小于右边数，那么下一个排列就是从左边这个数开始改
# 比如 [1, 2, 3, 4]，从后往前看，第一对数就是 3和4，所以就从3开始改，如果是 [1, 2, 4, 3]，那么就是2和4这一对数，从2开始改动。怎么改动呢，
# 我的想法就是把改动的所有数中排个序，第一个数是比原来的数大的最小数，然后剩下其他的数就是从小到大排列。
# 另外如果是倒序排列了，也就是如果从后往前找，没找到这一对数，那么返回的就是所有数从小到大的排列。
from typing import List
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        n = len(nums)
        if n == 1:
            return
        cur, pre = n-1, n-2
        # 找到从后往前数，第一个顺序对
        while pre >= 0:
            if nums[cur] > nums[pre]:  # 注意这里应该没有等于号
                break
            pre -= 1
            cur -= 1
        if pre < 0:
            nums.sort()
        else:
            num = nums[pre]
            tmp = nums[pre:].copy()
            tmp.sort()
            for i in range(len(tmp)):
                if tmp[i] > num:
                    idx = i
                    break   # 这里要写break，因为我们要找的是比num大的最小元素哈哈哈

            nums[pre] = tmp[idx]
            nums[pre+1:] = tmp[:idx] + tmp[idx+1:]

if __name__ == '__main__':
    solu = Solution()
    nums = [1, 3, 2]
    solu.nextPermutation(nums)
    print(nums)


import django.db.models.deletion
from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [("contenttypes", "0001_initial")]

    operations = [
        migrations.CreateModel(
            name="ContentItem",
            fields=[
                (
                    "id",
                    models.AutoField(
                        verbose_name="ID",
                        serialize=False,
                        auto_created=True,
                        primary_key=True,
                    ),
                ),
                ("parent_id", models.IntegerField(null=True)),
                (
                    "language_code",
                    models.CharField(default="", max_length=15, editable=False, db_index=True),
                ),
                ("sort_order", models.IntegerField(default=1, db_index=True)),
                (
                    "parent_type",
                    models.ForeignKey(to="contenttypes.ContentType", on_delete=models.CASCADE),
                ),
            ],
            options={
                "ordering": ("placeholder", "sort_order"),
                "verbose_name": "Contentitem link",
                "verbose_name_plural": "Contentitem links",
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name="Placeholder",
            fields=[
                (
                    "id",
                    models.AutoField(
                        verbose_name="ID",
                        serialize=False,
                        auto_created=True,
                        primary_key=True,
                    ),
                ),
                (
                    "slot",
                    models.SlugField(
                        help_text="A short name to identify the placeholder in the template code.",
                        verbose_name="Slot",
                    ),
                ),
                (
                    "role",
                    models.CharField(
                        default="m",
                        help_text="This defines where the object is used.",
                        max_length=1,
                        verbose_name="Role",
                        choices=[
                            ("m", "Main content"),
                            ("s", "Sidebar content"),
                            ("r", "Related content"),
                        ],
                    ),
                ),
                ("parent_id", models.IntegerField(null=True)),
                (
                    "title",
                    models.CharField(max_length=255, verbose_name="Admin title", blank=True),
                ),
                (
                    "parent_type",
                    models.ForeignKey(
                        blank=True,
                        to="contenttypes.ContentType",
                        null=True,
                        on_delete=models.CASCADE,
                    ),
                ),
            ],
            options={
                "verbose_name": "Placeholder",
                "verbose_name_plural": "Placeholders",
            },
            bases=(models.Model,),
        ),
        migrations.AlterUniqueTogether(
            name="placeholder",
            unique_together={("parent_type", "parent_id", "slot")},
        ),
        migrations.AddField(
            model_name="contentitem",
            name="placeholder",
            field=models.ForeignKey(
                related_name="contentitems",
                on_delete=django.db.models.deletion.SET_NULL,
                to="fluent_contents.Placeholder",
                null=True,
            ),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name="contentitem",
            name="polymorphic_ctype",
            field=models.ForeignKey(
                related_name="polymorphic_fluent_contents.contentitem_set+",
                editable=False,
                to="contenttypes.ContentType",
                on_delete=models.CASCADE,
                null=True,
            ),
            preserve_default=True,
        ),
    ]