import random, os.path
import pygame
from pygame.locals import *

SCREENRECT = Rect(0, 0, 640, 480)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
ENEMYODDS = 60
ENEMYRELOAD = 12
FPS = 60

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        width = 40
        height = 60
        self.image = pygame.Surface([width, height])
        self.image.fill(GREEN)

        self.rect = self.image.get_rect()

        self.change_x = 1
        self.change_y = 0
        self.x = 1 * 5
        self.y = 100
        self.rect.top = 100
        self.rect.left = 100

    def update(self):
        self.rect.top += 1

    def lift(self):
        self.rect.bottom = self.rect.bottom - 14
        self.rect.x += 1

class Enemy(pygame.sprite.Sprite):
    speed = 13
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        img_height = random.randint(100, 200)
        self.image = pygame.Surface([40, img_height])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.x = -1
        self.y = 0
        self.rect.top = random.choice([0, (SCREENRECT.height - img_height)])
        self.rect.right = 640

    def update(self):
        self.rect.move_ip(self.x, 0)
        if self.rect.right <= 0:
            self.kill()

def main(winstyle = 0):
    pygame.init()
    winstyle = 0
    bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32)
    screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth)
    pygame.display.set_caption('blockplane')
    pygame.mouse.set_visible(0)

    #bgtile = pygame.image.load('../data/background.gif') #chnage this to default color
    background = pygame.Surface(SCREENRECT.size)

    screen.blit(background, (0, 0))
    pygame.display.flip()

    #images

    #initalize game objects

    enemies = pygame.sprite.Group()
    all = pygame.sprite.RenderUpdates()
    Player.containers = all
    Enemy.containers = enemies, all

    global score
    enemyreload = ENEMYRELOAD

    clock = pygame.time.Clock()

    #initialize starting sprites of game

    global SCORE
    player = Player()

    going = True
    while player.alive():

        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                going = False
                return
            if event.type == KEYDOWN:
                if event.key == K_UP:
                    player.lift()

        all.clear(screen, background)
        all.update()

        if enemyreload: #reload
            enemyreload = enemyreload - 1
        elif not int(random.random() * ENEMYODDS): #check ENEMYODDS
            Enemy()
            enemyreload = ENEMYRELOAD #check ENEMYRELOAD

        for enemy in pygame.sprite.spritecollide(player, enemies, 1):
            player.kill()

        dirty = all.draw(screen)
        pygame.display.update(dirty)

        clock.tick(FPS)

    pygame.time.wait(1000)
    pygame.quit()

if __name__ == '__main__':
    main()


import sys
sys.path.append('src')
import scipy.sparse as sparse
from scipy.sparse import csgraph
import scipy.sparse as sp
import numpy as np
import theano
from theano import tensor as T
from utils.errors import MissingParamError
from utils.utils import log_filter, binary_filter
import math

def direct_compute_deepwalk_matrix(A, args, logger):
    res = {}
    try:
        windows = args["prox_params"]['window']
        b = args["prox_params"]['negative']
        transform = args["prox_params"]['transform']
        threshold = args["prox_params"]['threshold']
    except KeyError:
        raise MissingParamError
    for window in windows:
        n = A.shape[0]
        vol = float(A.sum())
        L, d_rt = csgraph.laplacian(A, normed=True, return_diag=True)
        # X = D^{-1/2} A D^{-1/2}
        X = sparse.identity(n) - L
        S = np.zeros_like(X)
        X_power = sparse.identity(n)
        for i in range(window):
            logger.info("Deep Walk %d-th power, %d/%d", i+1, i+1, window)
            X_power = X_power.dot(X)
            S += X_power
        S *= vol / window / b
        D_rt_inv = sparse.diags(d_rt ** -1)
        M = D_rt_inv.dot(D_rt_inv.dot(S).T)
        m = T.matrix()
        if transform == 1:
            logger.info("log transform")
            res[window] = log_filter(M, threshold)
            # res[window] = log_filter(M)
        elif transform == 2:
            res[window] = binary_filter(M, logger, threshold)
        else:
            logger.info("no transform")
            res[window] = sparse.csr_matrix(M)
    return res


# -*- coding: ISO-8859-1 -*-
from django.shortcuts import get_object_or_404
from django.db.models import Q, Sum
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView

from tsm.core.base.core_base_datatable import CoreBaseDatatableView
from tsm.core.mixins.core_mixin_login import CoreMixinLoginRequired
from tsm.core.mixins.core_mixin_json import JSONResponseMixin
from tsm.core.mixins.core_mixin_form import CoreMixinForm, CoreMixinDel
from tsm.core.mixins.core_mixin_json import JSONResponseMixin
from tsm.core.util import getParamByName, get_filiais, format_number
from tsm.core import constants

from tsm.acesso.models.usuario import Usuario
from tsm.equipe.models.membro import Membro
from tsm.equipe.models.membrometa import MembroMeta

from tsm.oportunidade.models.oportunidade import Oportunidade
from tsm.oportunidade.models.resposta import Resposta

class VisaoGerencial(CoreMixinLoginRequired, TemplateView):
    """
    Visão gerencial da oportunidade
    """
    template_name = 'visaogerencial_list.html'

class VisaoGerencialData(CoreMixinLoginRequired, CoreBaseDatatableView):
    """
    Json da lista
    """
    model = Oportunidade
    columns = [
        'cliente',
        'receita',
        'situacao',
        'valor',
        'ponderado',
        'temperatura_auto',
        'tipotemperatura',
        'responsavel',
        'lider',
        'dtFechamento',
        'respostas',
        'id',
    ]
    order_columns = [
        'cliente',
        'receita',
        'situacao',
        'valor',
        'ponderado',
        'temperatura_auto',
        'tipotemperatura',
        'responsavel',
        'lider',
        'dtFechamento',
    ]
    max_display_length = 500
    url_base_form = '/oportunidade/visaogerencial/'

    def render_column(self, row, column):
        if column == 'cliente':
            sReturn = row.cliente.nome
            return sReturn
        elif column == 'receita':
            sReturn = row.receita.nome
            return sReturn
        elif column == 'situacao':
            sReturn = row.situacao.nome
            return sReturn
        elif column == 'tipotemperatura':
            sReturn = row.tipotemperatura.nome
            return sReturn
        elif column == 'responsavel':
            sReturn = row.responsavel.first_name + ' ' + row.responsavel.last_name
            return sReturn
        elif column == 'lider':
            sReturn = row.lider.first_name
            return sReturn
        elif column == 'valor':
            sReturn = format_number(row.valor)
            return sReturn
        elif column == 'ponderado':
            sReturn = format_number(row.ponderado)
            return sReturn
        elif column == 'respostas':
            dictReturn = []
            respostas = Resposta.objects.filter(oportunidade__id=row.id).order_by('questao__ordem')
            for resposta in respostas:
                dictReturn.append({
                    "pergunta": resposta.questao.pergunta,
                    "resposta": "Sim" if resposta.resposta else "Não",
                })
            return dictReturn
        elif column == 'dtFechamento':
            sReturn = row.dtFechamento.strftime("%d/%m/%Y")
            return sReturn
        elif column == 'buttons' and self.url_base_form and self.use_buttons:
            sReturn = '
'
            sReturn +='     '
            sReturn +='
'
            return sReturn
        else:
            return super(VisaoGerencialData, self).render_column(row, column)

    def filter_queryset(self, qs):
        """
        Filtros da query baseado no datatable
        """
        qs_base_filter = None

        #Verifica pode ver todas oportunidades, senão só exibe as que ele pode ver
        if not self.request.user.has_perm('oportunidade.list_all_opportunities'):
            membros = Membro.objects.filter(usuario__id=self.request.user.id)
            if membros.exists():
                membro = membros[0]
                children = Membro.objects.filter(lider__id=membro.id)
                qs_base_filter = Q(responsavel__id=membro.usuario.id) | Q(lider__id=membro.usuario.id)

                if children:
                    while children:
                        for item in children:
                            qs_base_filter = qs_base_filter | Q(lider__id=item.usuario.id)

                        children = Membro.objects.filter(
                            lider__id__in = children.values_list('id', flat=True)
                        )
        else:
            qs_base_filter = Q(
                filial__id__in=Usuario.objects \
                                      .filter(id=self.request.user.id) \
                                      .values_list('filiais__id', flat=True)
            )

        sSearch = self.request.GET.get('sSearch', None)
        qs_params = None
        if sSearch:
            search_parts = sSearch.split('+')
            for part in search_parts:
                q = Q(cliente__nome__istartswith=part)| \
                    Q(receita__nome__istartswith=part)| \
                    Q(situacao__nome__istartswith=part)| \
                    Q(temperatura_auto__istartswith=part)| \
                    Q(tipotemperatura__nome__istartswith=part)| \
                    Q(responsavel__first_name__istartswith=part)|Q(responsavel__last_name__istartswith=part)| \
                    Q(lider__first_name__istartswith=part)|Q(lider__last_name__istartswith=part)

                qs_params = qs_params | q if qs_params else q

            qs = qs.filter(qs_params)

        if qs_base_filter:
            qs = qs.filter(qs_base_filter)

        return qs

class VisaoGerencialIndicador(CoreMixinLoginRequired, JSONResponseMixin, TemplateView):
    """
    Retorna os indicadores relativos a cada oportunidade
    """

    def get(self, request, *args, **kwargs):
        oportunidade = get_object_or_404(Oportunidade,id=self.kwargs.get('pk', None))

        #calcula percentual em relacao a todas as oportunidades ainda não fechadas
        valOportunidade = 0
        oportunidades = Oportunidade.objects.filter(tipotemperatura__tipo__isnull=True) \
                                            .aggregate(Sum('valor'))

        if oportunidades:
            valOportunidade = (oportunidade.valor * 100)/oportunidades['valor__sum']

        if valOportunidade == 100:
            valOportunidade = valOportunidade

        #percentual do responsavel em relacao a sua meta
        valResponsavel = 0
        membroMeta = MembroMeta.objects.filter(
                                    membro__usuario__id=oportunidade.responsavel.id,
                                    is_Visible=True,
                                    mesVigencia=oportunidade.dtFechamento.strftime("%m"),
                                    anoVigencia=oportunidade.dtFechamento.strftime("%Y"),
                                    tipometa__id=getParamByName(constants.PAR_TPMETA, request.user.id)
                     )

        if membroMeta:
            valResponsavel = (oportunidade.valor * 100)/ membroMeta[0].valor
            if valResponsavel > 100:
                valResponsavel = 100

        #percentual do lider em relacao a sua meta
        valLider = 0
        membroMeta = MembroMeta.objects.filter(
                                    membro__usuario__id=oportunidade.lider.id,
                                    is_Visible=True,
                                    mesVigencia=oportunidade.dtFechamento.strftime("%m"),
                                    anoVigencia=oportunidade.dtFechamento.strftime("%Y"),
                                    tipometa__id=getParamByName(constants.PAR_TPMETA, request.user.id)
                     )

        if membroMeta:
            valLider = (oportunidade.valor * 100)/ membroMeta[0].valor
            if valLider > 100:
                valLider = 100

        data = {
            "result" : "ok",
            "data" : {
                "oportunidade": oportunidade.id,
                "values":{
                    "oportunidade": round(valOportunidade,2),
                    "responsavel": round(valResponsavel,2),
                    "lider" : round(valLider,2)
                }
            }
        }
        context = data

        return self.render_to_response(context)

class VisaoGerencialIndicadorTop(CoreMixinLoginRequired, JSONResponseMixin, TemplateView):
    """
    Retorna os indicadores do topo da tela
    """

    def get(self, request, *args, **kwargs):
        from datetime import date
        from tsm.oportunidade.models.situacao import Situacao
        from tsm.oportunidade.models.tipotemperatura import TipoTemperatura

        if not self.request.user.has_perm('oportunidade.list_all_opportunities'):
            membros = Membro.objects.filter(usuario__id=request.user.id)
            if membros.exists():
                membro = membros[0]
                children = Membro.objects.filter(lider__id=membro.id)
                qs_base_filter = Q(responsavel__id=membro.usuario.id) | Q(lider__id=membro.usuario.id)

                if children:
                    while children:
                        for item in children:
                            qs_base_filter = qs_base_filter | Q(lider__id=item.usuario.id)

                        children = Membro.objects.filter(
                            lider__id__in = children.values_list('id', flat=True)
                        )
        else:
            qs_base_filter = Q(
                filial__id__in=Usuario.objects \
                                      .filter(id=request.user.id) \
                                      .values_list('filiais__id', flat=True)
            )

        oportunidades = Oportunidade.objects.filter(qs_base_filter)
        oportunidades.exclude(dtFechamento__lt=date(date.today().year, date.today().month, 1)) \
                     .exclude(tipotemperatura__tipo='P')

        #FCST Gross
        temperaturas = [
            [1,6], #Adiada + Alta
            [2], #Media
            [3], #Fechada
        ]
        totalGross = float(0)
        gross = []
        for item in temperaturas:
            temps = TipoTemperatura.objects.filter(id__in=item)
            strTitle = ''
            for temp in temps:
                if strTitle:
                    strTitle = strTitle + ' + '
                strTitle = strTitle + temp.nome

            gross.append({
                'id': item,
                'title': strTitle,
                'value': 0,
            })

        #lideres
        lideres = []
        for oportunidade in oportunidades:
            if not oportunidade.lider.id in lideres:
                lideres.append(oportunidade.lider.id);

        #FCST Ponderado
        totalPonderado = float(0)
        ponderado = []
        for item in lideres:
            ponderado.append({
                'id': item,
                'name': '',
                'value':0,
            })

        #FATOR Situacao (P/U)
        totalSituacao = 0
        situacoes = Situacao.objects.all()
        fatorSit = []
        for item in lideres:
            fatorSit.append({
                'id': item,
                'name': '',
                'situacoes':[],
            })

            for situacao in situacoes:
                fatorSit[len(fatorSit)-1]['situacoes'].append({
                    'id': situacao.id,
                    'name': situacao.nome,
                    'perc': situacao.perc,
                    'value': 0,
                })

        #FATOR Calculo (66)
        totalFator = float(0)
        fatorCal = []
        for item in lideres:
            fatorCal.append({
                'id': item,
                'name': '',
                'value': 0,
            })

        #Realiza calculo
        for oportunidade in oportunidades:
            #atualiza dados do Gross
            totalGross += oportunidade.valor
            i = 0
            for item in gross:
                if oportunidade.tipotemperatura.id in item['id']:
                    gross[i]['value'] += oportunidade.valor
                i += 1

            #atualiza dados do ponderado
            totalPonderado += oportunidade.ponderado
            i = 0
            for item in ponderado:
                if oportunidade.lider.id == item['id']:
                    ponderado[i]['value'] += oportunidade.ponderado
                    ponderado[i]['name'] = oportunidade.lider.first_name
                i += 1

            #atualiza dados do fator situacao
            totalSituacao += round((oportunidade.valor * oportunidade.situacao.perc)/100,2)
            i = 0
            for item in fatorSit:
                if oportunidade.lider.id == item['id']:
                    fatorSit[i]['name'] = oportunidade.lider.first_name

                    j = 0
                    for situacao in item['situacoes']:
                        if situacao['id'] == oportunidade.situacao.id:
                            fatorSit[i]['situacoes'][j]['value'] = round((oportunidade.valor * oportunidade.situacao.perc)/100,2)
                        j += 1
                i += 1

            #atualiza dados do cálculo 66
            fator = float(getParamByName(constants.PAR_POND66, request.user.id))
            totalFator += round(oportunidade.ponderado * fator,2)
            i=0
            for item in fatorCal:
                if oportunidade.lider.id == item['id']:
                    fatorCal[i]['value'] += round(oportunidade.ponderado * fator,2)
                    fatorCal[i]['name'] = oportunidade.lider.first_name
                i += 1

        data = {
            "result" : "ok",
            "data" : {
                "gross": {
                    "total" : totalGross,
                    "items" : gross,
                },
                "ponderado":{
                    "total": totalPonderado,
                    "items": ponderado,
                },
                "situacao":{
                    "total": totalSituacao,
                    "items": fatorSit,
                },
                "fator":{
                    "perc": fator,
                    "total": totalFator,
                    "items": fatorCal,
                },
            },
        }
        context = data

        return self.render_to_response(context)


# example of scaling the dataset
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
# prepare dataset
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)
# split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# define scaler
scaler = MinMaxScaler()
# fit scaler on the training dataset
scaler.fit(X_train)
# transform both datasets
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
# summarize the scale of each input variable
for i in range(X_test.shape[1]):
	print('>%d, train: min=%.3f, max=%.3f, test: min=%.3f, max=%.3f' %
		(i, X_train_scaled[:, i].min(), X_train_scaled[:, i].max(),
			X_test_scaled[:, i].min(), X_test_scaled[:, i].max()))


import pandas as pd
import numpy as np
import sklearn.metrics.pairwise as pw
from scipy import sparse
from sklearn.metrics.pairwise import pairwise_distances
from flask import Flask, render_template, request

def itembased_movie_recommender(movie_name):
    final_movies=pd.read_csv(r"movies_final.csv")
    movie_name=movie_name.lower()
    if movie_name not in final_movies['title'].unique():
        return "This movie is not in Database. Please enter an other movie"
    else:

        pivot_ibr = pd.pivot_table(final_movies,
                                          index='title',
                                          columns=['userId'], values='rating')
        sparse_ibr = sparse.csr_matrix(pivot_ibr.fillna(0))
        recommender_ibr = pw.cosine_similarity(sparse_ibr)
        recommender_ibr_df=pd.DataFrame(recommender_ibr,
                                      columns=pivot_ibr.index,
                                      index=pivot_ibr.index)
        cosine_ibr_df = pd.DataFrame(recommender_ibr_df[movie_name].sort_values(ascending=False))
        cosine_ibr_df.reset_index(level=0, inplace=True)
        cosine_ibr_df.columns = ['title','cosine_sim']
        list_movies=list((cosine_ibr_df['title'].values))
        return list_movies[1:11]
# print(itembased_movie_recommender('inception'))

app = Flask(__name__)
@app.route("/")
def home():
    return render_template('home.html')

@app.route("/recommend")
def recommend():
    movie = request.args.get('movie')
    movie_list=itembased_movie_recommender(movie)
    movie=movie.upper()
    if type(movie_list)==type('string'):
        return render_template('recommend.html',movie=movie,movie_list=movie_list,t='s')
    else:
        return render_template('recommend.html',movie=movie,movie_list=movie_list,t='l')

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


from mnist import load_dataset
from pdt.train_tf import compile_fns
from pdt.common import gen_sfx_key
from wacacore.util.io import  mk_dir
from wacacore.util.misc import *
from wacacore.util.generators import infinite_samples, infinite_batches
from pdt.types import (Type, Interface, Const, Axiom, ForAllVar,
                       AbstractDataType, ProbDataType)
from common import handle_options, load_train_save
import tensorflow as tf
import sys

def gen_queue_adt(train_data,
                  options,
                  queue_shape=(28, 28, 1),
                  enqueue_args={},
                  dequeue_args={},
                  empty_queue_args={},
                  item_shape=(28, 28, 1),
                  batch_size=512,
                  nitems=3):
    """Construct a queue abstract data type"""
    # Types - a Queue of Item
    Queue = Type(queue_shape, 'Queue')
    Item = Type(item_shape, 'Item')

    # Interface

    # Push an Item onto a Queue to create a new queue
    enqueue = Interface([Queue, Item], [Queue], 'enqueue', **enqueue_args)
    # Pop an Item from a queue, returning a new queue and the item
    dequeue = Interface([Queue], [Queue, Item], 'dequeue', **dequeue_args)
    interfaces = [enqueue, dequeue]

    # train_outs
    train_outs = []
    gen_to_inputs = identity

    # Consts
    # The empty queue is the queue with no items
    empty_queue = Const(Queue, 'empty_queue', batch_size, **empty_queue_args)
    consts = [empty_queue]

    # Vars
    # queue1 = ForAllVar(Queue)
    items = [ForAllVar(Item, str(i)) for i in range(nitems)]
    forallvars = items

    generators = [infinite_batches(train_data, batch_size, shuffle=True)
                  for i in range(nitems)]

    # Axioms
    axioms = []
    queue = empty_queue.batch_input_var
    for i in range(nitems):
        (queue,) = enqueue(queue, items[i].input_var)
        dequeue_queue = queue
        for j in range(i + 1):
            (dequeue_queue, dequeue_item) = dequeue(dequeue_queue)
            axiom = Axiom((dequeue_item,), (items[j].input_var,))
            axioms.append(axiom)
    train_fn, call_fns = compile_fns(interfaces, consts, forallvars, axioms,
                                     train_outs, options)
    queue_adt = AbstractDataType(interfaces, consts, forallvars, axioms,
                                 name='queue')
    queue_pdt = ProbDataType(queue_adt, train_fn, call_fns,
                             generators, gen_to_inputs, train_outs)
    return queue_adt, queue_pdt

def main(argv):
    global queue_adt, queue_pdt, sess, X_train, sfx
    options = handle_options('queue', argv)

    mnist_data = load_dataset()
    X_train = mnist_data[0].reshape(-1, 28, 28, 1)
    sfx = gen_sfx_key(('adt', 'nblocks', 'block_size'), options)

    empty_queue_args = {'initializer': tf.random_uniform_initializer}
    queue_adt, queue_pdt = gen_queue_adt(X_train,
                                         options,
                                         enqueue_args=options,
                                         nitems=options['nitems'],
                                         dequeue_args=options,
                                         empty_queue_args=empty_queue_args,
                                         batch_size=options['batch_size'])

    savedir = mk_dir(sfx)
    sess = load_train_save(options, queue_adt, queue_pdt, sfx, savedir)

if __name__ == "__main__":
    main(sys.argv[1:])


import os
import json
import random
import subprocess
import urllib.parse

with open('folders.txt', 'r') as fold_file:
    class_paths = [line.strip() for line in fold_file if line.strip()]

classes = dict()

for idx, class_path in enumerate(class_paths):
    class_name = os.path.basename(class_path)
    for root, _, filenames in os.walk(class_path):
        for filename, ext in map(os.path.splitext, filenames):
            if ext != '.mp4':
                continue
            filename_q = urllib.parse.unquote(filename)
            if filename_q not in classes:
                duration = str(subprocess.check_output(
                    ('ffprobe', '-v', 'error', '-show_entries',
                     'format=duration', '-of',
                     'default=noprint_wrappers=1:nokey=1',
                     os.path.join(root, filename + ext))),
                               encoding='utf-8').strip()
                classes[filename_q] = {
                    'subset':
                    'training' if random.random() < 0.8 else 'testing',
                    'duration': float(duration),
                    'actions': [[idx, 0, float(duration)]]
                }

print(json.dumps(classes, indent=2))


"""Helper functions for scraping options data"""
__docformat__ = "numpy"

import argparse
import os
from typing import List
import configparser
import requests
import pandas as pd
from tabulate import tabulate
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn

presets_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "presets/")

def view_available_presets(other_args: List[str]):
    """View available presets.

    Parameters
    ----------
    other_args: List[str]
        Other arguments to be parsed
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        prog="view",
        description="""View available presets under presets folder.""",
    )
    parser.add_argument(
        "-p",
        "--preset",
        action="store",
        dest="preset",
        type=str,
        help="View specific preset",
        default="",
        choices=[
            preset.split(".")[0]
            for preset in os.listdir(presets_path)
            if preset[-4:] == ".ini"
        ],
    )

    try:
        if other_args:
            if "-" not in other_args[0]:
                other_args.insert(0, "-p")
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        if ns_parser.preset:
            preset_filter = configparser.RawConfigParser()
            preset_filter.optionxform = str  # type: ignore
            preset_filter.read(presets_path + ns_parser.preset + ".ini")

            filters_headers = ["FILTER"]

            print("")
            for filter_header in filters_headers:
                print(f" - {filter_header} -")
                d_filters = {**preset_filter[filter_header]}
                d_filters = {k: v for k, v in d_filters.items() if v}
                if d_filters:
                    max_len = len(max(d_filters, key=len))
                    for key, value in d_filters.items():
                        print(f"{key}{(max_len-len(key))*' '}: {value}")
                print("")

        else:
            presets = [
                preset.split(".")[0]
                for preset in os.listdir(presets_path)
                if preset[-4:] == ".ini"
            ]

            for preset in presets:
                with open(
                    presets_path + preset + ".ini",
                    encoding="utf8",
                ) as f:
                    description = ""
                    for line in f:
                        if line.strip() == "[FILTER]":
                            break
                        description += line.strip()
                print(f"\nPRESET: {preset}")
                print(description.split("Description: ")[1].replace("#", ""))
            print("")
    except Exception as e:
        print(e)

def screener_output(other_args: List[str]):
    """screener filter output"""
    parser = argparse.ArgumentParser(
        add_help=False,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        prog="scr",
        description="""Sreener filter output from https://ops.syncretism.io/index.html.
Where: CS: Contract Symbol; S: Symbol, T: Option Type; Str: Strike; Exp v: Expiration;
IV: Implied Volatility; LP: Last Price; B: Bid; A: Ask; V: Volume; OI: Open Interest;
Y: Yield; MY: Monthly Yield; SMP: Regular Market Price; SMDL: Regular Market Day Low;
SMDH: Regular Market Day High; LU: Last Trade Date; LC: Last Crawl; ITM: In The Money;
PC: Price Change; PB: Price-to-book. """,
    )
    parser.add_argument(
        "-p",
        "--preset",
        action="store",
        dest="preset",
        type=str,
        default="template",
        help="Filter presets",
        choices=[
            preset.split(".")[0]
            for preset in os.listdir(presets_path)
            if preset[-4:] == ".ini"
        ],
    )

    try:
        if other_args:
            if "-" not in other_args[0]:
                other_args.insert(0, "-p")

        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        d_cols = {
            "contractsymbol": "CS",
            "symbol": "S",
            "opttype": "T",
            "strike": "Str",
            "expiration": "Exp ∨",
            "impliedvolatility": "IV",
            "lastprice": "LP",
            "bid": "B",
            "ask": "A",
            "volume": "V",
            "openinterest": "OI",
            "yield": "Y",
            "monthlyyield": "MY",
            "regularmarketprice": "SMP",
            "regularmarketdaylow": "SMDL",
            "regularmarketdayhigh": "SMDH",
            "lasttradedate": "LU",
            "lastcrawl": "LC",
            "inthemoney": "ITM",
            "pchange": "PC",
            "pricetobook": "PB",
        }

        preset_filter = configparser.RawConfigParser()
        preset_filter.optionxform = str  # type: ignore
        preset_filter.read(presets_path + ns_parser.preset + ".ini")

        d_filters = {k: v for k, v in dict(preset_filter["FILTER"]).items() if v}
        s_filters = str(d_filters)
        s_filters = s_filters.replace(": '", ": ").replace("',", ",").replace("'}", "}")
        s_filters = s_filters.replace("'", '"')

        link = "https://api.syncretism.io/ops"

        res = requests.get(
            link, headers={"Content-type": "application/json"}, data=s_filters
        )

        if res.status_code == 200:
            df_res = pd.DataFrame(res.json())

            if df_res.empty:
                print(f"No options data found for preset: {ns_parser.preset}", "\n")
                return

            df_res = df_res.rename(columns=d_cols)[list(d_cols.values())[:17]]

            print(
                tabulate(
                    df_res,
                    headers=df_res.columns,
                    showindex=False,
                    tablefmt="fancy_grid",
                )
            )
        else:
            print("Wrong arguments specified. Error " + str(res.status_code))
        print("")

    except Exception as e:
        print(e, "\n")


from bson import ObjectId
from collections import OrderedDict
from datetime import datetime

from django.http import StreamingHttpResponse, FileResponse, Http404, QueryDict
from django.utils.http import urlquote
from django.db.models import Q as dQ
from rest_framework import viewsets, status, generics, mixins
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser, BasePermission
from rest_framework.schemas import AutoSchema
from rest_framework.compat import coreapi, coreschema
from rest_framework.serializers import Serializer

from buckets.utils import BucketFileManagement
from users.views import send_active_url_email
from utils.storagers import FileStorage, PathParser
from utils.oss.rados_interfaces import CephRadosObject
from .models import User, Bucket, BucketFileInfo
from . import serializers
from . import paginations

# Create your views here.

class IsSuperUser(BasePermission):
    '''
    Allows access only to super users.
    '''
    def has_permission(self, request, view):
        return request.user and request.user.is_superuser

class CustomAutoSchema(AutoSchema):
    '''
    自定义Schema
    '''
    def get_manual_fields(self, path, method):
        '''
        重写方法，为每个方法自定义参数字段
        '''
        extra_fields = []
        if type(self._manual_fields) is dict and method in self._manual_fields:
            extra_fields = self._manual_fields[method]

        return extra_fields

class CustomGenericViewSet(viewsets.GenericViewSet):
    '''
    自定义GenericViewSet类，重写get_serializer方法，以通过context参数传递自定义参数
    '''
    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        context.update(kwargs.get('context', {}))
        kwargs['context'] = context
        return serializer_class(*args, **kwargs)

def get_bucket_collection_name_or_response(bucket_name, request):
    '''
    获取存储通对应集合名称，或者Response对象
    :param bucket_name: 存储通名称
    :return: (collection_name, response)
            collection_name=None时，存储通不存在，response有效；
            collection_name!=''时，存储通存在，response=None；
    '''
    bucket = Bucket.get_bucket_by_name(bucket_name)
    if not bucket:
        response = Response(data={'code': 404, 'code_text': 'bucket_name参数有误，存储通不存在'}, status=status.HTTP_404_NOT_FOUND)
        return (None, response)
    if not bucket.check_user_own_bucket(request):
        response = Response(data={'code': 404, 'code_text': 'bucket_name参数有误，存储通不存在'}, status=status.HTTP_404_NOT_FOUND)
        return (None, response)

    collection_name = bucket.get_bucket_mongo_collection_name()
    return (collection_name, None)

class UserViewSet(mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   viewsets.GenericViewSet):
    '''
    用户类视图
    list:
    获取用户列表,需要管理员权限

    retrieve:
    获取一个用户详细信息，需要管理员权限，或当前用户信息

    create:
    注册一个用户

    destroy:
    删除一个用户，需要管理员权限
    '''
    queryset = User.objects.all()

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        if not send_active_url_email(request._request, user.email, user):
            return Response('激活链接邮件发送失败', status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        data = {
            'code': 201,
            'code_text': '用户注册成功，请登录邮箱访问收到的连接以激活用户',
            'data': serializer.validated_data,
        }
        return Response(data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        if not (request.user.id == instance.id):
            return Response(data={"detail": "您没有执行该操作的权限。"}, status=status.HTTP_403_FORBIDDEN)
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    def get_serializer_class(self):
        '''
        动态加载序列化器
        '''
        if self.action == 'create':
            return serializers.UserCreateSerializer

        return serializers.UserDeitalSerializer

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        if self.action in ['list', 'delete']:
            return [IsAuthenticated(), IsSuperUser()]
        elif self.action == 'create':
            return []
        elif self.action == 'retrieve':
            return [IsAuthenticated()]
        return [IsSuperUser()]

class BucketViewSet(viewsets.GenericViewSet):
    '''
    存储桶视图

    list:
    获取存储桶列表

    >>Http Code: 状态码200：无异常时，返回所有的存储桶信息；
        {
            'code': 200,
            'buckets': [], // bucket对象列表
        }

    retrieve:
    获取一个存储桶详细信息

    >>Http Code: 状态码200：无异常时，返回存储桶的详细信息；
        {
            'code': 200,
            'bucket': {}, // bucket对象
        }

    create:
    创建一个新的存储桶

    >>Http Code: 状态码201；
        创建成功时：
        {
            'code': 201,
            'code_text': '创建成功',
            'data': serializer.data, //请求时提交数据
            'bucket': {}             //bucket对象信息
        }
    >>Http Code: 状态码400,参数有误：
        {
            'code': 400,
            'code_text': 'xxx',      //错误码表述信息
            'data': serializer.data, //请求时提交数据
        }

    delete:
    删除一个存储桶

    >>Http Code: 状态码204,存储桶删除成功
    >>Http Code: 状态码400
        {
            'code': 400,
            'code_text': '存储桶id有误'
        }
    >>Http Code: 状态码404：
        {
            'code': 404,
            'code_text': 'xxxxx'
        }

    '''
    queryset = Bucket.objects.filter(soft_delete=False).all()
    permission_classes = [IsAuthenticated]
    pagination_class = paginations.BucketsLimitOffsetPagination

    # api docs
    schema = CustomAutoSchema(
        manual_fields={
            'DELETE': [
                coreapi.Field(
                    name='ids',
                    required=False,
                    location='body',
                    schema=coreschema.String(description='存储桶id列表或数组，删除多个存储桶时，通过此参数传递其他存储桶id'),
                ),
            ]
        }
    )

    def list(self, request, *args, **kwargs):
        self.queryset = Bucket.objects.filter(dQ(user=request.user) & dQ(soft_delete=False)).all() # user's own

        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        else:
            serializer = self.get_serializer(queryset, many=True)
            data = {'code': 200, 'buckets': serializer.data,}
        return Response(data)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, context={'request': request})
        if not serializer.is_valid(raise_exception=False):
            data = {
                'code': 400,
                'code_text': serializer.errors.get('non_field_errors', '参数验证未通过'),
                'data': serializer.data,
            }
            return Response(data, status=status.HTTP_400_BAD_REQUEST)
        serializer.save()
        data = {
            'code': 201,
            'code_text': '创建成功',
            'data': serializer.data,
            'bucket': serializers.BucketSerializer(serializer.instance).data
        }
        return Response(data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response({'code': 200, 'bucket': serializer.data})

    def destroy(self, request, *args, **kwargs):
        id = kwargs.get(self.lookup_field, None)
        ids = request.POST.getlist('ids')

        if id and id not in ids:
            ids.append(id)
        try:
            ids = [int(i) for i in ids]
        except ValueError:
            return Response(data={'code': 400, 'code_text': '存储桶id有误'}, status=status.HTTP_400_BAD_REQUEST)

        if ids:
            buckets = Bucket.objects.filter(id__in=ids)
            if not buckets.exists():
                return Response(data={'code': 404, 'code_text': '未找到要删除的存储桶'}, status=status.HTTP_404_NOT_FOUND)
            for bucket in buckets:
                # 只删除用户自己的buckets
                if bucket.user.id == request.user.id:
                    bucket.do_soft_delete()  # 软删除

            return Response(status=status.HTTP_204_NO_CONTENT)

        return Response(data={'code': 404, 'code_text': '存储桶id为空'}, status=status.HTTP_404_NOT_FOUND)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        Custom serializer_class
        """
        if self.action in ['create', 'delete']:
            return serializers.BucketCreateSerializer

        return serializers.BucketSerializer

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        if self.action in ['list', 'create', 'delete']:
            return [IsAuthenticated()]
        return [permission() for permission in self.permission_classes]

class ObjViewSet(viewsets.GenericViewSet):
    '''
    文件对象视图集

    create:
    创建一个文件对象，并返回文件对象的id：

    	Http Code: 状态码201：无异常时，返回数据：
    	{
            data: 客户端请求时，携带的数据,
            id: 文件id，上传文件块时url中需要,
        }
        Http Code: 状态码400：参数有误时，返回数据：
            对应参数错误信息;

    update:
    通过文件对象绝对路径（以存储桶名开始）分片上传文件对象

    注意：
        文件对象已存在，数据上传会覆盖原数据，文件对象不存在，会自动创建文件对象，并且文件对象的大小只增不减；
        当chunk_offset=0时会被认为一次新文件对象上传，如果文件对象已存在，此时overwrite参数有效，
            overwrite=False时为不覆盖上传，会返回400错误码和已存在同名文件的错误提示。
            overwrite=True时会重置原文件对象大小为0，相当于删除已存在的同名文件对象，创建一个新同名文件对象，

        Http Code: 状态码200：上传成功无异常时，返回数据：
        {
            data: 客户端请求时，携带的参数,不包含数据块；
        }
        Http Code: 状态码400：参数有误时，返回数据：
            对应参数错误信息;
        Http Code: 状态码500
            {
                'code': 500,
                'code_text': '文件块rados写入失败'
            }

    retrieve:
        通过文件对象绝对路径（以存储桶名开始）,下载文件对象,可通过query参数获取文件对象详细信息，或者自定义读取对象数据块

        *注：参数优先级判定顺序：info > chunk_offset && chunk_size
        1. info=true时,返回文件对象详细信息，其他忽略此参数；
        2. chunk_offset && chunk_size 参数校验失败时返回状态码400和对应参数错误信息，无误时，返回bytes数据流
        3. 不带参数或者info无效时，返回整个文件对象；

    	>>Http Code: 状态码200：
            * info=true,返回文件对象详细信息：
            {
                'code': 200,
                'bucket_name': 'xxx',   //所在存储桶名称
                'dir_path': 'xxxx',      //所在目录
                'obj': {},              //文件对象详细信息
                'breadcrumb': [[xxx, xxx],]    //路径面包屑
            }
            * 自定义读取时：返回bytes数据流，其他信息通过标头headers传递：
            {
                evhb_chunk_size: 返回文件块大小
                evhb_obj_size: 文件对象总大小
            }
            * 其他,返回FileResponse对象,bytes数据流；

        >>Http Code: 状态码400：文件路径参数有误：对应参数错误信息;
            {
                'code': 400,
                'code_text': 'xxxx参数有误'
            }
        >>Http Code: 状态码404：找不到资源;
        >>Http Code: 状态码500：服务器内部错误;

    destroy:
        通过文件对象绝对路径（以存储桶名开始）,删除文件对象；

        >>Http Code: 状态码204：删除成功，NO_CONTENT；
        >>Http Code: 状态码400：文件路径参数有误：对应参数错误信息;
            {
                'code': 400,
                'code_text': 'objpath参数有误'
            }
        >>Http Code: 状态码404：找不到资源;
        >>Http Code: 状态码500：服务器内部错误;

    partial_update:
    对象共享或私有权限设置

        Http Code: 状态码200：上传成功无异常时，返回数据：
        {
            'code': 200,
            'code_text': '对象共享设置成功'，
            'share': xxx,
            'days': xxx
        }
        Http Code: 状态码400：参数有误时，返回数据：
            对应参数错误信息;
        Http Code: 状态码404;

    '''
    queryset = {}
    # permission_classes = [IsAuthenticated]
    lookup_field = 'objpath'
    lookup_value_regex = '.+'

    # api docs
    VERSION_METHOD_FEILD = [
        coreapi.Field(
            name='version',
            required=True,
            location='path',
            schema=coreschema.String(description='API版本（v1, v2）')
        ),
    ]

    OBJ_PATH_METHOD_FEILD = [
        coreapi.Field(
            name='objpath',
            required=True,
            location='path',
            schema=coreschema.String(description='以存储桶名称开头的文件对象绝对路径，类型String'),
        ),
    ]

    schema = CustomAutoSchema(
        manual_fields={
            'GET': VERSION_METHOD_FEILD + OBJ_PATH_METHOD_FEILD + [
                coreapi.Field(
                    name='info',
                    required=False,
                    location='query',
                    schema=coreschema.String(description='可选参数，info=true时返回文件对象详细信息，不返回文件对象数据，其他值忽略，类型boolean'),
                ),
                coreapi.Field(
                    name='chunk_offset',
                    required=False,
                    location='query',
                    schema=coreschema.String(description='要读取的文件块在整个文件中的起始位置（bytes偏移量), 类型int'),
                ),
                coreapi.Field(
                    name='chunk_size',
                    required=False,
                    location='query',
                    schema=coreschema.String(description='要读取的文件块的字节大小, 类型int'),
                ),
            ],
            'DELETE': VERSION_METHOD_FEILD + OBJ_PATH_METHOD_FEILD,
            'PUT': VERSION_METHOD_FEILD + OBJ_PATH_METHOD_FEILD,
            'POST': VERSION_METHOD_FEILD,
            'PATCH': VERSION_METHOD_FEILD + [
                coreapi.Field(
                    name='share',
                    required=False,
                    location='query',
                    schema=coreschema.Boolean(description='是否分享，用于设置对象公有或私有, true(公开)，false(私有)'),
                ),
                coreapi.Field(
                    name='days',
                    required=False,
                    location='query',
                    schema=coreschema.String(description='对象公开分享天数(share=true时有效)，0表示永久公开，默认为0'),
                ),
            ],
        }
    )

    # def create(self, request, *args, **kwargs):
    #     serializer = self.get_serializer(data=request.data)
    #     serializer.is_valid(raise_exception=True)
    #     serializer.save()
    #     return Response(serializer.response_data, status=status.HTTP_201_CREATED)

    def update(self, request, *args, **kwargs):
        objpath = kwargs.get(self.lookup_field, '')

        # 对象路径分析
        pp = PathParser(filepath=objpath)
        bucket_name, path, filename = pp.get_bucket_path_and_filename()
        if not bucket_name or not filename:
            return Response(data={'code': 400, 'code_text': 'objpath参数有误'}, status=status.HTTP_400_BAD_REQUEST)

        # 数据验证
        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid(raise_exception=False):
            return Response({
                'code': 400,
                'code_text': serializer.errors.get('non_field_errors', '参数有误，验证未通过'),
            }, status=status.HTTP_400_BAD_REQUEST)

        # 存储桶验证和获取桶对象mongodb集合名
        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        data = serializer.data
        chunk_offset = data.get('chunk_offset')
        chunk = request.data.get('chunk')
        overwrite = data.get('overwrite')

        obj, created = self.get_file_obj_or_create_or_404(collection_name, path, filename)
        rados = CephRadosObject(str(obj.id))
        if not created: # 对象存在 ，
            if chunk_offset == 0:
                if not overwrite: # 不覆盖
                    return Response({'code': 400, 'code_text': 'objpath参数有误，已存在同名文件'}, status=status.HTTP_400_BAD_REQUEST)
                else:
                    rados.delete()
                    obj.si = 0

        # 存储文件块
        ok, bytes = rados.write(offset=chunk_offset, data_block=chunk.read())
        if ok:
            # 更新文件修改时间
            obj.upt = datetime.utcnow()
            obj.si = max(chunk_offset + chunk.size, obj.si if obj.si else 0)  # 更新文件大小（只增不减）
            obj.switch_collection(collection_name)
            try:
                obj.save()
            except:
                pass
        else:
            return Response({'code': 500, 'code_text': '文件块rados写入失败'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(data, status=status.HTTP_200_OK)

    def retrieve(self, request, *args, **kwargs):
        info = request.query_params.get('info', None)
        objpath = kwargs.get(self.lookup_field, '')

        pp = PathParser(filepath=objpath)
        bucket_name, path, filename = pp.get_bucket_path_and_filename()
        if not bucket_name or not filename:
            return Response(data={'code': 400, 'code_text': 'objpath参数有误'}, status=status.HTTP_400_BAD_REQUEST)

        validated_param, valid_response = self.custom_read_param_validate_or_response(request)
        if not validated_param and valid_response:
            return valid_response

        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        fileobj = self.get_file_obj_or_404(collection_name, path, filename)

        # 返回文件对象详细信息
        if info == 'true':
            return self.get_obj_info_response(request=request, fileobj=fileobj, bucket_name=bucket_name, path=path)

        # 自定义读取文件对象
        if validated_param:
            offset = validated_param.get('offset')
            size = validated_param.get('size')
            return self.get_custom_read_obj_response(obj=fileobj, offset=offset, size=size, collection_name=collection_name)

        # 下载整个文件对象
        response = self.get_file_download_response(str(fileobj.id), filename)
        if not response:
            return Response(data={'code': 500, 'code_text': '服务器发生错误，获取文件返回对象错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 增加一次下载次数
        fileobj.download_cound_increase(collection_name)
        return response

    def destroy(self, request, *args, **kwargs):
        objpath = kwargs.get(self.lookup_field, '')
        bucket_name, path, filename = PathParser(filepath=objpath).get_bucket_path_and_filename()
        if not bucket_name or not filename:
            return Response(data={'code': 400, 'code_text': 'objpath参数有误'}, status=status.HTTP_400_BAD_REQUEST)

        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        fileobj = self.get_file_obj_or_404(collection_name, path, filename)
        fileobj.do_soft_delete(collection_name)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def partial_update(self, request, *args, **kwargs):
        objpath = kwargs.get(self.lookup_field, '')

        validated_param, valid_response = self.shared_param_validate_or_response(request)
        if not validated_param and valid_response:
            return valid_response
        share = validated_param.get('share')
        days = validated_param.get('days')

        pp = PathParser(filepath=objpath)
        bucket_name, path, filename = pp.get_bucket_path_and_filename()
        if not bucket_name or not filename:
            return Response(data={'code': 400, 'code_text': 'objpath参数有误'}, status=status.HTTP_400_BAD_REQUEST)

        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        fileobj = self.get_file_obj_or_404(collection_name, path, filename)
        fileobj.switch_collection(collection_name)
        fileobj.set_shared(sh=share, days=days)

        data = {
            'code': 200,
            'code_text': '对象共享权限设置成功',
            'share': share,
            'days': days
        }
        return Response(data=data, status=status.HTTP_200_OK)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        Custom serializer_class
        """
        if self.action == 'create':
            return serializers.ObjPostSerializer
        elif self.action == 'update':
            return serializers.ObjPutSerializer
        return Serializer

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        context = super(ObjViewSet, self).get_serializer_context()
        context['kwargs'] = self.kwargs
        return context

    def get_file_obj_or_404(self, collection_name, path, filename):
        """
        获取文件对象
        """
        bfm = BucketFileManagement(path=path, collection_name=collection_name)
        ok, obj = bfm.get_file_exists(file_name=filename)
        if not ok or not obj:
            raise Http404
        return obj

    def get_file_obj_or_create_or_404(self, collection_name, path, filename):
        '''
        获取文件对象, 不存在则创建，其他错误(如对象父路径不存在)会抛404错误

        :param collection_name:
        :param path:
        :param filename:
        :return: (obj, created)
                obj: 对象
                created: 指示对象是否是新创建的，True(是)
        '''
        bfm = BucketFileManagement(path=path, collection_name=collection_name)
        ok, did = bfm.get_cur_dir_id()
        if not ok:
            raise Http404 # 目录路径不存在

        ok, obj = bfm.get_file_exists(file_name=filename)
        if not ok:
            raise Http404

        if not obj:
            # 创建文件对象
            bfinfo = BucketFileInfo(na=filename,  # 文件名
                                    fod=True,  # 文件
                                    si=0)  # 文件大小
            # 有父节点
            if did:
                bfinfo.did = ObjectId(did)

            bfinfo.switch_collection(collection_name)
            try:
                obj = bfinfo.save()
            except:
                raise Http404
            return obj, True
        return obj, False

    def get_file_download_response(self, file_id, filename):
        '''
        获取文件下载返回对象
        :param file_id: 文件Id, type: str
        :filename: 文件名， type: str
        :return:
            success：http返回对象，type: dict；
            error: None
        '''
        cro = CephRadosObject(file_id)
        file_generator = cro.read_obj_generator
        if not file_generator:
            return None

        filename = urlquote(filename)# 中文文件名需要
        response = FileResponse(file_generator())
        response['Content-Type'] = 'application/octet-stream'  # 注意格式
        response['Content-Disposition'] = f'attachment; filename="{filename}"; filename*=utf-8 ${filename}'  # 注意filename 这个是下载后的名字
        return response

    def get_obj_info_response(self, request, fileobj, bucket_name, path):
        '''
        文件对象信息Response
        :param request:
        :param fileobj: 文件对象
        :param bucket_name: 存储桶
        :param path: 文件对象所在目录路径
        :return: Response
        '''
        serializer = serializers.ObjInfoSerializer(fileobj, context={'request': request,
                                                                           'bucket_name': bucket_name,
                                                                           'dir_path': path})
        return Response(data={
            'code': 200,
            'bucket_name': bucket_name,
            'dir_path': path,
            'obj': serializer.data,
            'breadcrumb': PathParser(path).get_path_breadcrumb()
        })

    def custom_read_param_validate_or_response(self, request):
        '''
        自定义读取文件对象参数验证
        :param request:
        :return:
                (None, None) -> 未携带参数
                (None, response) -> 参数有误
                ({data}, None) -> 参数验证通过

        '''
        chunk_offset = request.query_params.get('chunk_offset', None)
        chunk_size = request.query_params.get('chunk_size', None)

        validated_data = {}
        if chunk_offset is not None and chunk_size is not None:
            try:
                offset = int(chunk_offset)
                size = int(chunk_size)
                if offset < 0 or size < 0:
                    raise Exception()
                validated_data['offset'] = offset
                validated_data['size'] = size
            except:
                response = Response(data={'code': 400, 'code_text': 'chunk_offset或chunk_size参数有误'},
                                status=status.HTTP_400_BAD_REQUEST)
                return None, response
        else:
            return None, None
        return validated_data, None

    def shared_param_validate_or_response(self, request):
        '''
        文件对象共享或私有权限参数验证
        :param request:
        :return:
            (None, response) -> 参数有误
            ({data}, None) -> 参数验证通过

        '''
        days = request.query_params.get('days', 0)
        share = request.query_params.get('share', '').lower()

        validated_data = {}
        if share == 'true':
            share = True
        elif share == 'false':
            share = False
        else:
            response = Response(data={'code': 400, 'code_text': 'share参数有误'}, status=status.HTTP_400_BAD_REQUEST)
            return (None, response)

        try:
            days = int(days)
            if days < 0:
                raise Exception()
        except:
            response = Response(data={'code': 400, 'code_text': 'days参数有误'}, status=status.HTTP_400_BAD_REQUEST)
            return (None, response)

        validated_data['share'] = share
        validated_data['days'] = days
        return (validated_data, None)

    def get_custom_read_obj_response(self, obj, offset, size, collection_name):
        '''
        文件对象自定义读取response
        :param obj: 文件对象
        :param offset: 读起始偏移量
        :param size: 读取大小
        :param collection_name: 对象所在mongodb集合名
        :return: HttpResponse
        '''
        if size == 0:
            chunk = bytes()
        else:
            rados = CephRadosObject(str(obj.id))
            ok, chunk = rados.read(offset=offset, size=size)
            if not ok:
                data = {'code':500, 'code_text': 'server error,文件块读取失败'}
                return Response(data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 如果从0读文件就增加一次下载次数
        if offset == 0:
            obj.download_cound_increase(collection_name)

        reponse = StreamingHttpResponse(chunk, content_type='application/octet-stream', status=status.HTTP_200_OK)
        reponse['evob_chunk_size'] = len(chunk)
        reponse['evob_obj_size'] = obj.si
        return reponse

class DirectoryViewSet(viewsets.GenericViewSet):
    '''
    目录视图集

    retrieve:
    获取一个目录下的文件和文件夹信息

        >>Http Code: 状态码200:
            {
                'code': 200,
                'files': [fileobj, fileobj, ...],//文件信息对象列表
                'bucket_name': xxx,             //存储桶名称
                'dir_path': xxx,                //当前目录路径
                'breadcrumb': [[xxx, xxx],],    //路径面包屑
            }
        >>Http Code: 状态码400:
            {
                'code': 400,
                'code_text': 'ab_path参数有误'
            }
        >>Http Code: 状态码404:
            {
                'code': xxx,      //404
                'code_text': xxx  //错误码描述
            }

    create:
        创建一个目录

        >>Http Code: 状态码400, 请求参数有误:
            {
                "code": 400,
                "code_text": {
                    "error_text": ["xxxx", ] // 错误列表
                }
            }
        >>Http Code: 状态码201,创建文件夹成功：
            {
                'code': 201,
                'code_text': '创建文件夹成功',
                'data': {},      //请求时提交的数据
                'dir': {}，      //新目录对象信息
            }

    destroy:
        删除一个目录

        >>Http Code: 状态码204,成功删除;
        >>Http Code: 状态码400,参数无效;
            {
                'code': 400,
                'code_text': 'ab_path参数无效'
            }
        >>Http Code: 状态码404;
            {
                'code': 404,
                'code_text': '文件不存在
            }
    '''
    queryset = []
    permission_classes = [IsAuthenticated]
    lookup_field = 'ab_path'
    lookup_value_regex = '.+'
    pagination_class = paginations.BucketFileCursorPagination

    # api docs
    BASE_METHOD_FIELDS = [
        coreapi.Field(
            name='version',
            required=True,
            location='path',
            schema=coreschema.String(description='API版本（v1, v2）')
        ),
        coreapi.Field(
            name='ab_path',
            required=True,
            location='path',
            schema=coreschema.String(description='以存储桶名称开头的目录绝对路径')
        ),
    ]
    schema = CustomAutoSchema(
        manual_fields={
            'GET': BASE_METHOD_FIELDS,
            'DELETE': BASE_METHOD_FIELDS,
        }
    )

    def retrieve(self, request, *args, **kwargs):
        ab_path = kwargs.get(self.lookup_field, '')
        pp = PathParser(filepath=ab_path)
        bucket_name, dir_path = pp.get_bucket_and_dirpath()
        if not bucket_name:
            return Response(data={'code': 400, 'code_text': 'ab_path参数有误'}, status=status.HTTP_400_BAD_REQUEST)

        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        bfm = BucketFileManagement(path=dir_path, collection_name=collection_name)
        ok, files = bfm.get_cur_dir_files()
        if not ok:
            return Response({'code': 404, 'code_text': '参数有误，未找到相关记录'}, status=status.HTTP_404_NOT_FOUND)

        data_dict = OrderedDict([
            ('code', 200),
            ('bucket_name', bucket_name),
            ('dir_path', dir_path),
            ('breadcrumb', pp.get_path_breadcrumb(dir_path))
        ])

        queryset = files
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True, context={'bucket_name': bucket_name, 'dir_path': dir_path})
            data_dict['files'] = serializer.data
            return self.get_paginated_response(data_dict)

        serializer = self.get_serializer(queryset, many=True, context={'bucket_name': bucket_name, 'dir_path': dir_path})

        data_dict['files'] = serializer.data
        return Response(data_dict)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, context={'request': request})
        if not serializer.is_valid(raise_exception=False):
            return Response({'code': 400, 'code_text': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

        validated_data = serializer.validated_data
        dir_path = validated_data.get('dir_path', '')
        dir_name = validated_data.get('dir_name', '')
        did = validated_data.get('did', None)
        collection_name = validated_data.get('collection_name')

        bfm = BucketFileManagement(dir_path, collection_name=collection_name)
        dir_path_name = bfm.build_dir_full_name(dir_name)
        bfinfo = BucketFileInfo(na=dir_path_name,  # 目录名
                                fod=False,  # 目录
                                )
        # 有父节点
        if did:
            bfinfo.did = did
        bfinfo.switch_collection(collection_name)
        try:
            bfinfo.save()
        except:
            return Response(data={'code': 500, 'code_text': '数据存入数据库错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        data = {
            'code': 201,
            'code_text': '创建文件夹成功',
            'data': serializer.data,
            'dir': serializers.ObjInfoSerializer(bfinfo).data
        }
        return Response(data, status=status.HTTP_201_CREATED)

    def destroy(self, request, *args, **kwargs):
        ab_path = kwargs.get(self.lookup_field, '')
        bucket_name, path, dir_name = PathParser(filepath=ab_path).get_bucket_path_and_dirname()

        if not bucket_name or not dir_name:
            return Response(data={'code': 400, 'code_text': 'ab_path参数无效'}, status=status.HTTP_400_BAD_REQUEST)

        collection_name, response = get_bucket_collection_name_or_response(bucket_name, request)
        if not collection_name and response:
            return response

        obj = self.get_dir_object(path, dir_name, collection_name)
        if not obj:
            return Response(data = {'code': 404, 'code_text': '文件不存在'}, status=status.HTTP_404_NOT_FOUND)
        else:
            obj.do_soft_delete(collection_name)

        return Response(status=status.HTTP_204_NO_CONTENT)

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        context.update(kwargs.get('context', {}))
        kwargs['context'] = context
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        Custom serializer_class
        """
        if self.action in ['create', 'delete']:
            return serializers.DirectoryCreateSerializer
        return serializers.ObjInfoSerializer

    def get_dir_object(self, path, dir_name, collection_name):
        """
        Returns the object the view is displaying.
        """
        bfm = BucketFileManagement(path=path, collection_name=collection_name)
        ok, obj = bfm.get_dir_exists(dir_name=dir_name)
        if not ok:
            return None
        return obj


"""
   最值
"""
import numpy as np
import matplotlib.pyplot as mp
import datetime as dt
def dmy2ymd(dmy):
    dmy = str(dmy, encoding="utf-8")
    # 从指定字符串返回一个日期时间对象
    dat = dt.datetime.strptime(dmy, "%d-%m-%Y").date()
    tm = dat.strftime("%Y-%m-%d")  # 格式化
    return tm
datas,opening_prices,highest_prices,lowest_prices,closing_prices,volumes = \
    np.loadtxt('aapl.csv',delimiter=',',usecols=(1,3,4,5,6,7),
           dtype=('M8[D],f8,f8,f8,f8,f8'),unpack=True,converters={1:dmy2ymd})
max_val = np.max(highest_prices)
min_val = np.min(lowest_prices)
print(max_val,'~',min_val)
#获取最大值和最小值的下标
max_ind = np.argmax(highest_prices)
min_ind = np.argmin(lowest_prices)
print(max_ind,'~',min_ind)
print(datas[max_ind],datas[min_ind])

#maximum minimum
a = np.arange(1,10).reshape(3,3)
b = np.arange(1,10)[::-1].reshape(3,3)
print(a)
print(b)
print(np.maximum(a,b))
print(np.minimum(a,b))