import PIL as pil
from PIL import Image  # Pas encore de sauvegarde implémentée
from PIL import ImageTk  # Idem
import tkinter as tk
from tkinter.filedialog import askopenfilename
import os

C_H, C_W = 250, 250
C_RAY = C_H/25
check = False  # Variable qui vérifie si un QR est bien chargé
dir_frame = os.path.realpath(os.getcwd()+"\DM IN202\\Exemples\\frame.png")  # Si vide : askopenfile request

def nbrCol(matrice):
    return(len(matrice[0]))

def nbrLig(matrice):
    return len(matrice)

###################################INUTILE#####################################

def saving(matPix, filename):
    # sauvegarde l'image contenue dans matpix dans le fichier filename
	# utiliser une extension png pour que la fonction fonctionne sans perte d'information
    toSave = pil.Image.new(mode="1", size=(nbrCol(matPix), nbrLig(matPix)))
    for i in range(nbrLig(matPix)):
        for j in range(nbrCol(matPix)):
            toSave.putpixel((j, i), matPix[i][j])
    toSave.save(filename)

###############################################################################

def loading(filename):
    # charge le fichier image filename et renvoie une matrice de 0 et de 1 qui représente
    # l'image en noir et blanc
    global dimension_qr
    toLoad = pil.Image.open(filename)
    mat = [[0]*toLoad.size[0] for k in range(toLoad.size[1])]
    dimension_qr = toLoad.size[1], toLoad.size[0]
    for i in range(dimension_qr[0]):
        for j in range(dimension_qr[1]):
            mat[i][j] = 0 if toLoad.getpixel((j, i)) == 0 else 1
    return mat

def affiche_mat(matrice):
    """Permet d'afficher la matrice en argument dans le terminal en deux
    dimensions"""
    label = str()
    for i in matrice:
        if len(label) != 0:
            label += '\n'
        for y in i:
            label += str(y)+' '
    return label

def qr_coin():
    '''Cette fonction ne prend rien en argument et retourne un coin de QR code
    sous forme de matrice ! '''
    coin = []
    for i in range(7):
        coin.append([])
        for j in range(7):
            coin[i].append(0)
    for i in range(5):
        i += 1
        for j in range(5):
            j += 1
            coin[i][j] = 1
    for i in range(3):
        i += 2
        for j in range(3):
            j += 2
            coin[i][j] = 0
    return coin

def mat_rotate(matrice, rotation):
    '''Fonction qui prend en entrée une matrice, la rotation désirée
    et qui rend la matrice retournée.'''
    if rotation == 90:
        for i in range(3):
            matrice = list(reversed(list(zip(*matrice))))  # inspiration lib py
    elif rotation == 180:
        for i in range(2):
            matrice = list(reversed(list(zip(*matrice))))
    elif rotation == -90:
        matrice = list(reversed(list(zip(*matrice))))
    return(matrice)

def dir_image():
    file_dir = askopenfilename(initialdir=os.path.realpath(os.getcwd())+"\DM IN202\\Exemples")
    return file_dir

def load_canvas():
    """Cette fonction affiche la matrice présente dans le programme
    sous le nom de 'mat' dans un canevas (gadget mais panache)"""
    canvas.delete("all")
    for line in range(len(mat)):
        for i in range(len(mat[line])):
            if mat[line][i] == 1:
                canvas.create_rectangle(C_RAY*i, C_RAY*line, C_RAY*i+C_RAY,
                                        C_RAY*line+C_RAY, fill="white")
    canvas.update()
    print("qr code affiché")

def load_image():
    """Charge une image de QR Code, vérifie ses dimensions car 25x25 oblige
    dans le cadre de ce projet."""
    global mat, check
    mat = loading(dir_image())
    print(affiche_mat(mat))
    if dimension_qr[0] == 25 and dimension_qr[1] == 25:
        load_canvas()
        check = True

def check_corner():
    """Vérifie si 'mat' est orientée dans le sens de lecture que
    veut la convention et le corrige sinon."""
    global check, mat
    if check:
        coin_ref = qr_coin()
        coins = [[], [], [], []]
        for i in range(7):
            coins[0].append([])
            coins[1].append([])
            for j in range(7):
                coins[0][i].append(mat[i][j])
            for j in range(18, 25):
                coins[1][i].append(mat[i][j])
        for i in range(18, 25):
            coins[2].append([])
            coins[3].append([])
            for j in range(7):
                coins[2][i-18].append(mat[i][j])
            for j in range(18, 25):
                coins[3][i-18].append(mat[i][j])
        print('\n'+affiche_mat(coins[0])+'UP left corner')
        print('\n'+affiche_mat(coins[1])+'UP right corner')
        print('\n'+affiche_mat(coins[2])+'DOWN left corner')
        print('\n'+affiche_mat(coins[3])+'DOWN right corner')
        print('\n'+affiche_mat(coin_ref)+'Référence\n\n')
        if coins[0] != coin_ref:
            print('UP Left corner should be the DOWN Right')
            mat = mat_rotate(mat, 180)
            print('Correction...')
        elif coins[1] != coin_ref:
            print('UP Right corner should be the DOWN Right')
            mat = mat_rotate(mat, 90)
            print('Correction...')
        elif coins[2] != coin_ref:
            print('DOWN Left corner should be the DOWN Right')
            mat = mat_rotate(mat, -90)
            print('Correction...')
        elif coins[3] != coin_ref:
            print('Le QR code est bien placé')
        load_canvas()
    else:
        canvas.delete("all")

def check_line():
    """Fonction qui vérifie que les lignes qui joignent chaques coins
    sont présentes, sinon erreur"""
    global mat, check
    if check:
        erreur = False
        if dir_frame == '':
            print('Il faut localiser le QR frame.png')
            qr_frame = loading(dir_image())
        else:
            qr_frame = loading(dir_frame)
        print('QR Frame chargé')
        for i in range(6, 18):
            if mat[6][i] != qr_frame[6][i] or mat[i][6] != qr_frame[i][6]:
                erreur = True
        if erreur:
            print('Les lignes de coins du QR Code ne sont pas identifiées')
        else:
            print('Les lignes de coins du QR Code sont identifiées')

def parité(bits: list, indices: list):
    """Fonction de calcul de parité basique qui prend en argument une
    quelconque liste composée en integer puis une autre liste qui correspond
    aux indices de comparaison."""
    out = 0
    for bit in indices:
        out += bits[bit]
    return out % 2

def switch_bin(binary: int):
    """return le bit opposé de celui en entrée"""
    if binary == 0:
        binary = 1
    else:
        binary = 0
    return binary

def hamming(bits):
    '''Prend 7 bits en entrée et rend 4 bits de données
    (C'est un code de correction d'erreur de Hamming)'''
    c1, c2, c3 = parité(bits, [0, 1, 3]), parité(bits, [0, 2, 3]), parité(bits, [2, 1, 3])
    if c1 != bits[-3] and c2 != bits[-2] and c3 != bits[-1]:
        bits[3] = switch_bin(bits[3])
    elif c2 != bits[-2] and c3 != bits[-1]:
        bits[2] = switch_bin(bits[2])
    elif c1 != bits[-3] and c3 != bits[-1]:
        bits[1] = switch_bin(bits[1])
    elif c1 != bits[-3] and c2 != bits[-2]:
        bits[0] = switch_bin(bits[0])
    return [bits[0], bits[1], bits[2], bits[3]]

def sorting_qr():
    '''Cette fonction lit une matrice de 25x25 représentant
    un QR code correctement orienté afin de retourner
    les blocs de lecture binaire associés'''
    global mat
    liste_binaire = []
    blocs = []
    size = 24
    for zigzag in range(4):
        zigzag += 1
        liste_binaire.append([])
        for i in range(14):
            x = 24-i
            for j in range(2):
                y = size-j
                liste_binaire[(zigzag*2)-2].append(mat[y][x])
        size -= 2
        liste_binaire.append([])
        for i in range(14):
            x = 11+i
            for j in range(2):
                y = size-j
                liste_binaire[(zigzag*2)-1].append(mat[y][x])
        size -= 2
    for ligne in liste_binaire:
        if ligne[0:len(ligne)//2].count(1) != 14:
            blocs.append(ligne[0:len(ligne)//2])
        if ligne[len(ligne)//2:len(ligne)].count(1) != 14:
            blocs.append(ligne[len(ligne)//2:len(ligne)])
    return blocs

def blocs_to_hamming(blocs):
    """Prend chaque bloc à part puis le décompose en deux sous blocs
    qui sont passés en Hamming, on obtient donc 8 bits de données sous forme
    de liste"""
    output = []
    for bloc in blocs:
        output.append(hamming(bloc[:7]) + hamming(bloc[7:]))
    return output

def bits_to_ascii(bits: list):
    '''Prend du binaire en argument (list) et retourne
    le character ASCII associé'''
    value = str()
    for i in bits:
        value += str(i)
    print(value)
    value = chr(int(value, 2))
    print(value)
    return value

def bits_to_hex(bits: list):
    """Prend une liste binaire en argument pour
    retourner l'hexadécimal associé"""
    value = str()
    for i in bits:
        value += str(i)
    print(value)
    return hex(int(value, 2))[2:]

def check_filter():
    '''Identifie le type de filtre appliqué au QR Code
    pour sa lisibilité puis retire de la matrice le filtre.
    \nLe paramètre du filtre dépend de 2 bits de la matrice:
    (23,8) et (22,8). \n\nSi "00" : entièrement noire (pas de changement).
    \nSi "01" : damier dont la case en haut à gauche est noire.
    \nSi "10" des lignes horizontales alternées noires et blanches,
    la plus haute étant noire.
    \nSi "11" des lignes verticales alternées noires et blanches,
    la plus à gauche étant noire.'''
    global mat
    filtre = str(mat[22][8]) + str(mat[23][8])
    xor_bool = False
    if filtre == '01':
        print("Filtre : Damier")
        for i in range(len(mat)):
            for j in range(len(mat[i])):
                if xor_bool:
                    if mat[i][j] == 0:
                        mat[i][j] = 1
                    else:
                        mat[i][j] = 0
                    xor_bool = False
                else:
                    xor_bool = True
        blocs = sorting_qr()
    elif filtre == '10':
        print("Filtre : Lignes horizontales")
        for i in range(len(mat)):
            if xor_bool:
                for j in range(len(mat[i])):
                    if mat[i][j] == 0:
                        mat[i][j] = 1
                    else:
                        mat[i][j] = 0
            else:
                xor_bool = True
        blocs = sorting_qr()
    elif filtre == '11':
        print("Filtre : Lignes Verticales")
        mat = mat_rotate(mat, 90)
        for i in range(len(mat)):
            if xor_bool:
                for j in range(len(mat[i])):
                    if mat[i][j] == 0:
                        mat[i][j] = 1
                    else:
                        mat[i][j] = 0
            else:
                xor_bool = True
        mat = mat_rotate(mat, -90)
        blocs = sorting_qr()
    else:
        print("Pas de Filtre")
        blocs = sorting_qr()
    return blocs

def check_qr():
    """Fonction maîtresse liée au check & process, c'est elle
    qui exécute la majorité des fonctions du programme,
    d'où l'appellation de son bouton"""
    if check:
        check_corner()
        check_line()
        blocs = check_filter()
        blocs = blocs_to_hamming(blocs)
        if mat[24][8] == 1:  # ASCII
            output = str()
            for bloc in blocs:
                output += bits_to_ascii(bloc)
            print('Contenu du QR Code : ', output)
        else:
            output = str()
            for bloc in blocs:
                output += bits_to_hex(bloc)
            print("Sortie Hexadécimale : ", output)
    else:
        canvas.delete("all")

# Initialisation du GUI

root = tk.Tk()
root.title('QR Code')
root.geometry('420x490')

# Ajout de fonctionnalités

button_charger = tk.Button(root, text='Charger', command=load_image, bg='gray')
button_check_qr = tk.Button(root, text='Check & Process', command=check_qr,
                            bg='gray')
canvas = tk.Canvas(root, width=C_W, height=C_H, bg="black")

button_charger.pack(side=tk.BOTTOM, ipadx=150, ipady=6)
button_check_qr.pack(side=tk.BOTTOM, ipadx=70)
canvas.pack(pady=20)

# Maintien de la fenêtre

root.mainloop()


import os
import shutil
import logging

from django.test import Client
from django.test import TestCase
from django.conf import settings
from django.test.utils import override_settings

from .models import InlineImage
from .models import HostedImage
from .exceptions import TooBigMedia
from .exceptions import InvalidMimeType

from munch.apps.users.tests.factories import UserFactory
from munch.apps.campaigns.tests.factories import MessageFactory

logging.disable(logging.CRITICAL)

class TestHostedMedia(TestCase):
    """ Deletes the media dir once tests are completed

    WARNING: use that only if you are in tests, with an overloaded  MEDIA_ROOT
    Normally it's fine within munch, defined in munch/test_settings.py
    """
    def classSetUp(self):
        # mktemp will only be used for the 1st Case using this parent class
        if not os.path.exists(settings.MEDIA_ROOT):
            os.mkdir(settings.MEDIA_ROOT)

    def classTearDown(self):
        try:
            shutil.rmtree(settings.MEDIA_ROOT)
        except Exception:
            pass

class TestHostedImage(TestHostedMedia):
    def setUp(self):
        self.user = UserFactory()

    def test_image_toobig(self):
        class SmallHostedImage(HostedImage):
            # very small
            MAX_SIZE = 100
        img_url = 'http://www.oasiswork.fr/uploads/logo_oasiswork.jpg'

        with self.assertRaises(TooBigMedia):
            img = SmallHostedImage(
                img_url, organization=self.user.organization)
            img.store()

    def test_wrong_mimetype(self):
        html_document = 'data:image/gif;base64,PGh0bWw+PC9odG1sPg=='
        image = InlineImage(html_document, organization=self.user.organization)
        with self.assertRaises(InvalidMimeType):
            image.store()

class TestHostedImageClient(TestHostedMedia):
    def setUp(self):
        self.client = Client()
        self.user = UserFactory()

    def test_fetch_hosted_image_ok(self):
        img_url = 'http://www.oasiswork.fr/uploads/logo_oasiswork.jpg'
        img = HostedImage(img_url, organization=self.user.organization)
        stored_url = img.store()
        self.assertTrue(stored_url.startswith('http://munch.example.com'))

    def test_fetch_inline_image_ok(self):
        img_data = (
            "data:image/gif;base64,R0lGODlhDwAPAKECAAAAzMzM/////"
            "wAAACwAAAAADwAPAAACIISPeQHsrZ5ModrLlN48CXF8m2iQ3YmmKqVlRtW4ML"
            "wWACH+H09wdGltaXplZCBieSBVbGVhZCBTbWFydFNhdmVyIQAAOw==")
        img = InlineImage(img_data, organization=self.user.organization)
        stored_url = img.store()
        self.assertTrue(stored_url.startswith('http://munch.example.com'))

@override_settings(SECRET_KEY='123412341234')
class TestHostedMessage(TestCase):
    def setUp(self):
        self.user = UserFactory()
        self.message = MessageFactory(author=self.user)

    def test_hosted_mail_without_key_works(self):
        response = self.client.get('/archive/{}/'.format(
            self.message.identifier))
        self.assertEqual(response.status_code, 200)

    def test_hosted_mail_bad_uuid(self):
        response = self.client.get('/archive/1584498-148484/')
        self.assertEqual(response.status_code, 404)


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Extract MozReview patch data and write out the patch data as a HTML directory.
"""

import argparse
from collections import namedtuple

import os
from time import sleep

import requests

API_BASE_URL = 'https://reviewboard.mozilla.org/api'

PATCHNAME_TEMPLATE = "r{}-diff{}.patch"

REVISION_INDEX_TEMPLATE = """
<!DOCTYPE html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>MozReview patch archive for revision {revision}</title>

MozReview patch archive for revision {revision}

{latest_link}
{difflist_links}

"""

DIFF_LINK_TEMPLATE = """
{text}
"""

DiffData = namedtuple('DiffData', 'review_id diff_id patch')

def call_api(path):
    response = requests.get(API_BASE_URL + path)
    response.raise_for_status()
    return response

def get_diff_count(review_id):
    path = '/review-requests/{}/diffs/'.format(review_id)
    data = call_api(path).json()
    return int(data['total_results'])

def get_patch_for_diff(review_id, diff_id):
    """Fetch a revision diff's contents as a string of bytes."""
    # Fetch the raw diff via the regular site.  The raw diff we want isn't
    # accessible via the Review Board API.
    url = 'https://reviewboard.mozilla.org/r/{}/diff/{}/raw'.format(review_id,
                                                                    diff_id)
    response = requests.get(url)
    response.raise_for_status()
    return response.content

def gather_diff_data(review_id, diff_id):
    patch = get_patch_for_diff(review_id, diff_id)
    return DiffData(review_id, diff_id, patch)

def save_patch(diffdata, output_dir):
    path = filepath_for_record(diffdata, output_dir)
    with open(path, 'wb') as fd:
        fd.write(diffdata.patch)
    print('wrote: {} {} bytes'.format(path, len(diffdata.patch)))

def filepath_for_record(diffdata, output_dir):
    filename = PATCHNAME_TEMPLATE.format(diffdata.review_id, diffdata.diff_id)
    path = os.path.join(
        revision_directory_name(output_dir, diffdata.review_id), filename)
    return path

def revision_directory_name(output_dir, revision_id):
    return os.path.join(output_dir, str(revision_id))

def ensure_revision_directory(output_dir, revision_id):
    dirname = revision_directory_name(output_dir, revision_id)
    if not os.path.isdir(dirname):
        os.mkdir(dirname)

def write_revision_index(output_dir, revision_id, diff_count):
    """Write an index.html for a directory of patches."""
    latest_diff_id = diff_count

    diff_links = []
    for diff_id in range(1, diff_count + 1):
        filename = PATCHNAME_TEMPLATE.format(revision_id, diff_id)
        link_html = DIFF_LINK_TEMPLATE.format(target=filename, text=filename)
        diff_links.append(link_html)

    # We want the list displayed newest to oldest
    diff_links.reverse()
    difflist_links = '\n'.join(diff_links)

    latest_filename = PATCHNAME_TEMPLATE.format(revision_id, latest_diff_id)
    latest_link = DIFF_LINK_TEMPLATE.format(
        target=latest_filename, text='latest.patch')

    index_html = REVISION_INDEX_TEMPLATE.format(
        revision=revision_id,
        latest_link=latest_link,
        difflist_links=difflist_links)

    index_filename = os.path.join(
        revision_directory_name(output_dir, revision_id), 'index.html')
    with open(index_filename, 'w') as index:
        index.write(index_html)
    print('wrote:', index_filename)

def diff_already_downloaded(revision_id, diff_id, output_dir):
    diff_filename = PATCHNAME_TEMPLATE.format(revision_id, diff_id)
    diff_filepath = os.path.join(output_dir, str(revision_id), diff_filename)
    return os.path.exists(diff_filepath)

def record_revision(revision_id, output_dir, rate_limit, skip_existing):
    """Fetch a revision's patches and save them to the output directory."""
    sleep(rate_limit)

    try:
        diff_count = get_diff_count(revision_id)
    except requests.ConnectionError as err:
        print("error: r{} {}".format(revision_id, err))
        return
    except requests.HTTPError as err:
        if err.response.status_code == 404:
            print("skipped: r{} (not found)".format(revision_id))
        else:
            print("error: r{} {}".format(revision_id, err))
        return

    ensure_revision_directory(output_dir, revision_id)

    updates_made = False

    for diff_id in range(1, diff_count + 1):
        if skip_existing and diff_already_downloaded(revision_id, diff_id, output_dir):
            continue
        else:
            updates_made = True

        sleep(rate_limit)

        try:
            diff_data = gather_diff_data(revision_id, diff_id)
        except (requests.HTTPError, requests.ConnectionError) as err:
            print("error: fetching diff r{} {}: {}".format(revision_id,
                                                           diff_id, err))
            continue

        save_patch(diff_data, output_dir)

    if updates_made:
        write_revision_index(output_dir, revision_id, diff_count)
    else:
        print("skipped: r{} (no new diffs)".format(revision_id))

def parse_revision_range_str(rev_range_str):
    """Parse a revision range string into range start and range end integers.

    Range strings have two forms: 'X', which returns (X,X), or 'X..Y',
    which returns (X,Y).
    """
    rev_range_parts = rev_range_str.split('..')
    if len(rev_range_parts) == 2:
        start_rev = int(rev_range_parts[0])
        end_rev = int(rev_range_parts[1])
    else:
        rev = int(rev_range_str)
        start_rev = rev
        end_rev = rev
    return start_rev, end_rev

def ensure_output_directory(output_dir):
    if not os.path.isdir(output_dir):
        os.mkdir(output_dir)

def parse_args():
    parser = argparse.ArgumentParser(
        description='Save MozReview patches to a directory')
    parser.add_argument(
        'revision', help='The revision or revision range to save.  Can be a single revision of the form \'XXXX\' or a range of revisions of the form \'XXXX..YYYY\'')
    parser.add_argument(
        '--skip-existing',
        action='store_true',
        help='Only download new diffs. (default: %(default)s)'
    )
    parser.add_argument(
        '--limit',
        type=float,
        default=1.0,
        help='Rate-limit API requests to one request every LIMIT seconds.  Accepts floating-point numbers like \'0.5\'. (default: %(default)s seconds)'
    )
    parser.add_argument(
        '--output-dir',
        default='site',
        help='Output directory for HTML pages and patches. (default: \'%(default)s\')'
    )
    return parser.parse_args()

def main(opts):
    start_rev, end_rev = parse_revision_range_str(opts.revision)

    ensure_output_directory(opts.output_dir)

    print("Outputting files to directory '{}'".format(opts.output_dir))
    print("Rate-limiting to {} seconds between requests".format(opts.limit))
    print()

    for rev_id in range(start_rev, end_rev + 1):
        record_revision(rev_id, opts.output_dir, opts.limit, opts.skip_existing)

    print()
    print('Done.')

if __name__ == '__main__':
    opts = parse_args()
    main(opts)


# -*- coding: utf-8 -*-
import os, sys, json, time, random, urllib
project_dir = os.path.dirname(os.path.abspath(os.path.curdir))
sys.path.append(
    project_dir
)
sys.path.append(
    os.path.join(project_dir, 'lib')
)
print(sys.path)

# import unittest, requests, mimetypes
import tornado_server as entry
# import asr_server.http_get_info as
from functools import partial
from uuid import uuid4
import tornado.ioloop
import unittest
import mimetypes

import tornado.httpclient
import tornado.ioloop
from tornado.testing import *

'''
POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------8721656041911415653955004498
Content-Length: 465

-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myTextField"

Test
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myCheckBox"

on
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myFile"; filename="test.txt"
Content-Type: text/plain

Simple file.
-----------------------------8721656041911415653955004498--
'''

@gen.coroutine
def multipart_producer(boundary, keyname, filenames, params, write):
    boundary_bytes = boundary.encode()

    for filename in filenames:
        filename_bytes = filename.encode()
        mtype = mimetypes.guess_type(filename)[0] or "application/octet-stream"
        buf = (
            (b"--%s\r\n" % boundary_bytes)
            + (
                b'Content-Disposition: form-data; name="%s"; filename="%s"\r\n'
                % (keyname.encode(), filename_bytes)
            )
            + (b"Content-Type: %s\r\n" % mtype.encode())
            + b"\r\n"
        )
        yield write(buf)
        with open(filename, "rb") as f:
            while True:
                # 16k at a time.
                chunk = f.read(16 * 1024)
                if not chunk:
                    break
                yield write(chunk)

        yield write(b"\r\n")
    for k, v in params.items():
        buf = (
                (b"--%s\r\n" % boundary_bytes)
                + (
                        b'Content-Disposition: form-data; name="%s"\r\n'
                        % (k.encode())
                )
                + b"\r\n"
        )
        yield write(buf)
        yield write(v.encode())
        yield write(b"\r\n")

    yield write(b"--%s--\r\n" % (boundary_bytes,))

class Badmusictest(AsyncHTTPTestCase):
    def setUp(self):
        super(Badmusictest, self).setUp()

    def get_app(self):
        # 设置参数
        self.port, self.start_num = "7894", 11
        args = ['--port', self.port, '--start_num', str(self.start_num)]
        old_sys_argv = sys.argv
        sys.argv = [old_sys_argv[0]] + args
        self.args = entry.getArg()
        return entry.make_app(self.args)

    def test_getArg(self):
        args = entry.getArg()
        self.assertEqual(args.port, self.port)
        self.assertEqual(args.start_num, self.start_num)

    def test_checkMusicHandler_POST(self):
        url = '/checkbadmusic'
        boundary = uuid4().hex
        headers = {"Content-Type": "multipart/form-data; boundary=%s" % boundary}
        params = {'vid': str(int(time.time()))+''.join(random.choice('abcdef') for _ in range(8))}
        producer = partial(multipart_producer, boundary, 'sound', ['test.wav'], params)
        response = self.fetch(
            path=url,
            method="POST",
            headers=headers,
            body_producer=producer,
        )
        self.assertEqual(response.code, 200)
        res = json.loads(response.body)
        self.assertEqual(res['error'], '')

    def test_checkMusicHandler_GET(self):
        url = '/checkbadmusic'
        params = {
            'vid': str(int(time.time()))+''.join(random.choice('abcdef') for _ in range(8)),
            'sound_url': 'https://file-examples.com/wp-content/uploads/2017/11/file_example_WAV_2MG.wav',
        }
        response = self.fetch(
            path=url,
            body=urllib.urlencode(params)
        )
        self.assertEqual(response.code, 200)

import HtmlTestRunner

if __name__ == "__main__":
    runner = HtmlTestRunner.HTMLTestRunner(
            report_title='Badmusic Test',
            report_name='BadMusic'
        )
    itersuite = unittest.TestLoader().loadTestsFromTestCase(Badmusictest)
    runner.run(itersuite)


from sqlalchemy_utils import create_database, database_exists

def create_dbs(db_uri: str | None) -> None:
    """создать БД если отсутствует"""
    # для SQLite БД создавать не нужно
    if db_uri is None:
        return
    sync_url = db_uri.replace("+asyncpg", "")
    if not database_exists(sync_url):
        create_database(sync_url)


import math

def rectangle_area(length, width):
    """
    Функція для обчислення площі прямокутника.
    Приймає довжину та ширину прямокутника як аргументи.
    Повертає площу прямокутника.
    """
    return length * width

def triangle_area(base, height):
    """
    Функція для обчислення площі трикутника.
    Приймає основу та висоту трикутника як аргументи.
    Повертає площу трикутника.
    """
    return 0.5 * base * height

def circle_area(radius):
    """
    Функція для обчислення площі кола.
    Приймає радіус кола як аргумент.
    Повертає площу кола.
    """
    return math.pi * radius**2

# Основна програма
print("Обчислення площі фігур")
print("1. Прямокутник")
print("2. Трикутник")
print("3. Коло")

choice = int(input("Виберіть фігуру (1/2/3): "))

if choice == 1:
    length = float(input("Введіть довжину прямокутника: "))
    width = float(input("Введіть ширину прямокутника: "))
    area = rectangle_area(length, width)
    print("Площа прямокутника: %.2f" % area)
elif choice == 2:
    base = float(input("Введіть основу трикутника: "))
    height = float(input("Введіть висоту трикутника: "))
    area = triangle_area(base, height)
    print("Площа трикутника: %.2f" % area)
elif choice == 3:
    radius = float(input("Введіть радіус кола: "))
    area = circle_area(radius)
    print("Площа кола: %.2f" % area)
else:
    print("Невірний вибір.")


from discord.ext import commands
import discord

from aiohttp import request
import os
import json

def nextrank(score):
    #Masters+
    if score >= 10000:
        return "RP"
    #Diamond
    elif score >= 7200:
        return "/" + str(((score - 7200) // 700 + 1) * 700 + 7200) + "RP"
    #Platinum
    elif score >= 4800:
        return "/" + str(((score - 4800) // 600 + 1) * 600 + 4800) + "RP"
    #Gold
    elif score >= 2800:
        return "/" + str(((score - 2800) // 500 + 1) * 500 + 2800) + "RP"
    #Silver
    elif score >= 1200:
        return "/" + str(((score - 1200) // 400 + 1) * 400 + 1200) + "RP"
    #Broze
    else:
        return "/" + str(((score // 300 + 1) * 300)) + "RP"

class Apex(commands.Cog):
    def __init__(self, client):
        self.client = client
        with open('data/apex/weapons.txt') as file:
            self.weapon = json.load(file)
        self.color = {
            "Energy": [
                0x636f1d,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/2/2d/Energy_Ammo.svg/revision/latest/scale-to-width-down/256?cb=20190827163648"
            ],
            "Heavy": [
                0x376654,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/5/55/Heavy_Rounds.svg/revision/latest/scale-to-width-down/254?cb=20190827144859"
            ],
            "Unique": [
                0xcb003c,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/7/7f/Supply_Drop_Sniper_Ammo.svg/revision/latest/scale-to-width-down/273?cb=20200819185728"
            ],
            "Light": [
                0xf49b49,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/a/a5/Light_Rounds.svg/revision/latest/scale-to-width-down/254?cb=20190827144754"
            ],
            "Shotgun": [
                0x831600,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/4/42/Shotgun_Shells.svg/revision/latest/scale-to-width-down/254?cb=20190827163750"
            ],
            "Sniper": [
                0x7c80fb,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/3/3e/Sniper_Ammo.svg/revision/latest/scale-to-width-down/256?cb=20200207225134"
            ],
            "Arrow": [
                0xf3d674,
                "https://static.wikia.nocookie.net/apexlegends_gamepedia_en/images/f/fe/Arrows.svg/revision/latest/scale-to-width-down/512?cb=20210430161624"
            ]
        }

    async def getdata(self, ctx, URL):
        async with request(
                "GET", URL
        ) as response:
            data = await response.json(content_type='text/plain')
            return data

    @commands.command(aliases=["alstats", "stat", "stats"])
    async def apex_stats(self, ctx, id, platform='PC'):
        """`stats <id>` gets apex stats from origin id"""
        URL = "https://api.mozambiquehe.re/bridge?version=5&platform={}&player={}&auth={}".format(
            platform, id, os.getenv('APEX')).replace("'", "")
        data = await self.getdata(ctx, URL)
        rank = data['global']['rank']
        legend = data['legends']['selected']

        embed = discord.Embed(title="", color=0xcdcfbb)
        embed.set_author(name=f"{data['global']['name']}'s stats",
                         icon_url=f"{data['global']['rank']['rankImg']}")
        embed.set_thumbnail(url=f"{legend['ImgAssets']['icon']}")
        embed.add_field(name="Rank",
                        value=f"`{rank['rankName']} {rank['rankDiv']}`\
        \n`{rank['rankScore']}{nextrank(rank['rankScore'])}`",
                        inline=False)
        embed.add_field(name="Active Legend",
                        value=f"`{legend['LegendName']}`",
                        inline=False)

        for index in range(len(legend['data'])):
            embed.add_field(name=f"{legend['data'][index]['name'].title()}",
                            value=f"`{legend['data'][index]['value']}`",
                            inline=False)

        embed.set_footer(
            text=
            f"Level {data['global']['level']} ({data['global']['toNextLevelPercent']}%)"
        )
        await ctx.send(embed=embed)

    @commands.command(aliases=['wep', 'weap'])
    async def weapon(self, ctx, weapon=None):
        """`weapon <weapon>` return weapon stats."""
        if not weapon:
            all = ""
            embed = discord.Embed(title="Apex Legends Weapon List",
                                  color=0xcdcfbb)
            for category in self.weapon:
                for name in self.weapon[category]:
                    all += self.weapon[category][name][
                        'displayName'] + f" `{name}`" + "\n"
                embed.add_field(name=f"{category}",
                                value=f"{all}",
                                inline=False)
                all = ""
            await ctx.send(embed=embed)

        else:
            weapon = weapon.capitalize()
            for category in self.weapon:
                if weapon in self.weapon[category]:
                    type = category
                    data = self.weapon[category][weapon]

                    embed = discord.Embed(
                        title=f"{data['displayName']}",
                        color=self.color[data['ammoType']]
                        [0])  #description=f"{data['ammoType']}",)
                    embed.set_author(name=f"{type}",
                                     icon_url=self.color[data['ammoType']][1])
                    embed.set_image(url=f"{data['imgUrl']}")
                    embed.add_field(name="Damage",
                                    value=f"`{data['damage']}`",
                                    inline=False)
                    embed.add_field(name="Magazine Size",
                                    value=f"`{data['magSize']}`",
                                    inline=True)
                    embed.add_field(name="Reload Speed",
                                    value=f"`{data['reload']}`",
                                    inline=True)
                    embed.add_field(name="RPM",
                                    value=f"`{data['RPM']}`",
                                    inline=True)
                    await ctx.send(embed=embed)
                    break

def setup(client):
    client.add_cog(Apex(client))


import xlrd
import xlwt
from xlutils.copy import copy
MAXPHONE=100 # 最多支持100部手机
from functools import cmp_to_key
path = 'E:\data.xls'

def cmp(a,b):
    if a.final_score < b.final_score:
        return -1
    else:
        return 1

class phone_class():
    name = ""
    price = 0
    power = 0
    RAM = 0
    ROM = 0
    screen_size = 0.0
    unlock_location = ""
    resolution = 0
    charging = 0
    time = ""
    band_width = ""
    refresh_rate = 0
    curve = 0
    thick = 0
    weight = 0
    icon = ""
    CPU_brand = ""
    CPU_mark = ""
    NFC = 0
    typec = 0
    game = 0
    special = ""

    X = 0
    Y = 0
    Z = 0

    final_score = 0

    def init_phone_data(self,index):
        '''
        读取当前手机的excel数据，记录在本地.index代表行数
        '''
        data = xlrd.open_workbook('E:\data.xls')
        table = data.sheet_by_name('Sheet1')
        info_index = table.row_values(index)
        self.name = info_index[0]
        self.price = info_index[1]
        self.power = info_index[2]
        self.RAM = info_index[3]
        self.ROM = info_index[4]
        self.screen_size = info_index[5]
        self.unlock_location = info_index[6]
        self.resolution = info_index[7]
        self.charging = info_index[8]
        self.time = info_index[9]
        self.band_width = info_index[10]
        self.refresh_rate = info_index[11]
        self.curve = info_index[12]
        self.thick = info_index[13]
        self.weight = info_index[14]
        self.icon = info_index[15]
        self.CPU_brand = info_index[16]
        self.CPU_mark = info_index[17]
        self.NFC = info_index[18]
        self.typec = info_index[19]
        self.game = info_index[20]
        self.special = info_index[21]

    def getresult():
        return self.final_score

    def calculate_score(self):
        '''
        静态计算手机自身得分，储存在本地变量X，Y，Z里面
        X 代表性能
        Y 代表外观
        Z 代表实用性
        计算方法:
        X = (RAM/中位数)*100 + (CPU/中位数)*(100+game*10)
        Y = (screen_size/中位数)*100 + (refresh_rate/中位数)*100 + (thick/中位数)^(-1)*100 + (weight/中位数)^(-1)*100
        Z = (power/中位数)*100 + (ROM/中位数)*100 + (resolution/中位数)*100 + (charging/中位数)*100
        其他参数:
        刷新率(当选定游戏)
        曲面屏(外观)
        time(上市时间)
        price: 用户特指
        unlock location: 用户特指(后面看要不要删掉)
        NFC: 用户特指
        '''

    def show(self):
        print("手机名:" + self.name)
        print("手机价格" + self.price)

def write_excel_xls_append(path, value):
    index = len(value)  # 获取需要写入数据的行数
    workbook = xlrd.open_workbook(path)  # 打开工作簿
    sheets = workbook.sheet_names()  # 获取工作簿中的所有表格
    worksheet = workbook.sheet_by_name(sheets[0])  # 获取工作簿中所有表格中的的第一个表格
    rows_old = worksheet.nrows  # 获取表格中已存在的数据的行数
    new_workbook = copy(workbook)  # 将xlrd对象拷贝转化为xlwt对象
    new_worksheet = new_workbook.get_sheet(0)  # 获取转化后工作簿中的第一个表格
    for i in range(0, index):
        for j in range(0, len(value[i])):
            new_worksheet.write(i+rows_old, j, value[i][j])  # 追加写入数据，注意是从i+rows_old行开始写入
    new_workbook.save(path)  # 保存工作簿
    print("xls格式表格【追加】写入数据成功！")

class all_phone:
    all = []
    med_ram = 0
    med_cpu = 0
    med_screen_size = 0
    med_refresh_rate = 0
    med_thick = 0
    med_weight = 0
    med_power = 0
    med_rom = 0
    med_resolution = 0
    med_charging = 0

    def read_all_phone(self):
        '''
        从excel文件中读取所有行中的手机，储存在all数组中
        '''
        data = xlrd.open_workbook('E:\data.xls')
        table = data.sheet_by_name('Sheet1')
        all_row = table.nrows
        for i in range (1,all_row):
            tmp_phone = phone_class()
            tmp_phone.init_phone_data(i) # 逐行读取并保存
            all_phone.all.append(tmp_phone)
        all_phone.all.pop(0) # 去除头部元素重复出现的bug
        print("数据库读取完成")

    def median(data):
        # print(data)
        data.sort()
        half = len(data) // 2
        return (data[half] + data[~half])/2

    def __RAM_medium(self):
        '''
        计算所有手机RAM中位数
        '''
        ram_sum = []
        for item in all_phone.all:
            ram_sum.append(int(item.RAM))
        med = all_phone.median(ram_sum)
        return med

    def __CPU_medium(self):
        '''
        计算所有手机CPU中位数
        '''
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.CPU_mark))
        med = all_phone.median(cpu_sum)
        return med

    def __SCREEN_SIZE_medium(self):
        '''
        屏幕尺寸中位数
        '''
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.screen_size))
        med = all_phone.median(cpu_sum)
        return med

    def __RESOLUTION_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.resolution))
        med = all_phone.median(cpu_sum)
        return med

    def __refresh_rate_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.refresh_rate))
        med = all_phone.median(cpu_sum)
        return med

    def __THICK_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.thick))
        med = all_phone.median(cpu_sum)
        return med

    def __WEIGHT_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.weight))
        med = all_phone.median(cpu_sum)
        return med

    def __POWER_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.power))
        med = all_phone.median(cpu_sum)
        return med

    def __ROM_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.ROM))
        med = all_phone.median(cpu_sum)
        return med

    def __RESOLUTION_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.resolution))
        med = all_phone.median(cpu_sum)
        return med

    def __CHARGING_medium(self):
        cpu_sum = []
        for item in all_phone.all:
            cpu_sum.append(int(item.charging))
        med = all_phone.median(cpu_sum)
        return med

    def set_all_medium(self):
        all_phone.med_charging = self.__CHARGING_medium()
        all_phone.med_cpu = self.__CPU_medium()
        all_phone.med_power = self.__POWER_medium()
        all_phone.med_ram = self.__RAM_medium()
        all_phone.med_refresh_rate = self.__refresh_rate_medium()
        all_phone.med_resolution = self.__RESOLUTION_medium()
        all_phone.med_rom = self.__ROM_medium()
        all_phone.med_screen_size = self.__SCREEN_SIZE_medium()
        all_phone.med_thick = self.__THICK_medium()
        all_phone.med_weight = self.__WEIGHT_medium()

    def calculate_all_argument(self):
        '''
        计算all里面每个手机的xyz参数
        '''
        for item in all_phone.all:
            item.X = (int(item.RAM)/self.med_ram)*50 + (int(item.CPU_mark)/self.med_cpu)*(50+int(item.game)*10)
            item.Y = (int(item.screen_size)/self.med_screen_size)*25 + (int(item.refresh_rate/self.med_refresh_rate))*25\
                +(1/(int(item.thick)/self.med_thick))*25 + (1/(int(item.weight)/self.med_weight))*25
            item.Z = (int(item.power)/self.med_power)*25 + (int(item.ROM)/self.med_rom)*25 + (int(item.resolution)/self.med_resolution)*25\
                 + (int(item.charging)/self.med_charging)*25

    def output_mark(self):
        for item in all_phone.all:
            print(item.name)
            print("性能指数: " + str(item.X))
            print("外观指数: " + str(item.Y))
            print("实用指数:" + str(item.Z))
            print("\n")
    def recommand_index(self,case):
        '''
        根据专家系统算出来的结果计算推荐系数,case为传入的专家系统的参数
        '''
        for item in all_phone.all:
            item.X = item.X * case[0] # 性能
            item.Y = item.Y * case[1] # 外观指数
            item.Z = item.Z * case[2] # 实用指数
        print("处理推荐指数完成")

    def add_phone(name,price,power,ram,rom,screen_size,cpuname_mark,resolution,charge,refresh_rate,thick,weight,brand,remark,add_case):
        fourG_or_5G = ""
        curve_screen = ""
        typec = ""
        nfc = ""
        game = ""
        cpu_name = ""
        cpu_mark = ""
        tmp = [0,0]
        if(add_case[0] == 1):
            fourG_or_5G = "5G"
        else:
            fourG_or_5G = "4G"
        if(add_case[1]==1):
            curve_screen = "1"
        else:
            curve_screen = "0"
        if(add_case[2] == 1):
            nfc = "1"
        else:
            nfc = "0"
        if(add_case[3] == 1):
            typec = "1"
        else:
            typec = "0"
        if(add_case[4] == 1):
            game = "1"
        else:
            game = "0"
        tmp = cpuname_mark.split('/')
        cpu_name = tmp[0]
        cpu_mark = tmp[1]
        value = [name,price,power,ram,rom,screen_size,'no',resolution,charge,'0',fourG_or_5G,refresh_rate,curve_screen,thick,weight,brand,cpu_name,cpu_mark,nfc,typec,game,remark]
        all = []
        all.append(value)
        write_excel_xls_append(path,all)
        #pass

    def wrap_arrange(self,case,budget,G5,curve,icon_case,nfc):
        '''
        对已经计算好推荐指数的手机做最后的筛选，根据用户输入的是否需要5G，nfc,品牌，曲面屏等参数
        '''
        self.read_all_phone()
        self.set_all_medium()
        self.calculate_all_argument()
        self.recommand_index(case) # case 是专家系统传来的参数
        result_case = []
        for item in self.all:
            # 根据用户特定需求筛选价格
            if(budget == 1):
                # 0到1000
                if(item.price < 1000):
                    result_case.append(item)
            elif(budget == 2):
                # 2000以下
                if(item.price < 2000):
                    result_case.append(item)
            elif(budget == 3):
                # 3000以下
                if(item.price < 3000):
                    result_case.append(item)
            elif(budget == 4):
                # 3000以下
                if(item.price < 4000):
                    result_case.append(item)
            elif(budget == 5):
                result_case.append(item)

        # 在价格需求中选择包含5G
        for item in result_case:
            if(G5 == 1):
                # 要求5G
                if(item.band_width != '5G'):
                    result_case.remove(item)
                    continue

            if(curve == 1):
                # 要求曲面屏
                if(item.curve!=1):
                    result_case.remove(item)
                    continue

            if(nfc == 1):
                # 要求nfc
                if(item.NFC !=1):
                    result_case.remove(item)
                    continue

            if(len(icon_case)!=0):
                # 去除相应品牌
                if(item.icon in icon_case):
                    result_case.remove(item)
                    continue

        # 最后得到result_case，将其按照得分高度排序,只推荐得分最高的
        for item in result_case:
            item.final_score = item.X + item.Y + item.Z
        result_case.sort(key=cmp_to_key(cmp))
        result_case.reverse()

        return result_case

#if __name__ == "__main__":
#    all_phone_class = all_phone()
#    all_phone_class.read_all_phone()
#    # print(all_phone_class._all_phone__CPU_medium())
#    all_phone_class.set_all_medium()
#    all_phone_class.calculate_all_argument()
#    all_phone_class.output_mark()


import csv
import django_rq

from io import StringIO

from django.db.models import Q
from django import forms
from django.conf import settings
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.urls import reverse

from academy.backoffice.users.forms import BaseStatusTrainingFormSet
from academy.apps.students.models import Student, Training, TrainingStatus, TrainingMaterial
from academy.apps.accounts.models import User, Inbox
from academy.core.email_utils import construct_email_args
from academy.core.fields import TrainingMaterialField
from academy.apps.graduates.models import Graduate, Rating

from post_office import mail
from model_utils import Choices

class ParticipantsRepeatForm(forms.Form):
    batch = forms.ModelChoiceField(
        label='Angkatan', queryset=Training.objects.order_by('batch'), empty_label="Pilih Angkatan",
        help_text="Pemberitahuan akan dikirim kesemua peserta tidak " \
        "lulus pada angkatan yang dipilih"
    )

    def __init__(self, *args, **kwargs):
        self.users_repeat = []
        super().__init__(*args, **kwargs)

    def clean(self):
        batch = self.cleaned_data['batch']
        students = Student.objects.filter(status=Student.STATUS.participants, training=batch)
        if not students:
            raise forms.ValidationError("Maaf tidak ada peserta pada angkatan ini")

        user_ids = students.values_list("user_id", flat=True)
        users = User.objects.filter(id__in=user_ids)

        for user in users:
            status = user.get_count_training_status()
            if status['graduate'] < settings.INDICATOR_GRADUATED and status['not_yet'] == 0 \
                    and status['repeat'] >= settings.INDICATOR_REPEATED:
                self.users_repeat.append({'user': user, 'status': status})

        if not self.users_repeat:
            raise forms.ValidationError("Tidak ada peserta yang mengulang")

        return batch

    def send_notification(self):
        # get setting appearance
        from academy.apps.offices import utils
        sett = utils.get_settings(serializer=True)

        title = "Maaf, Anda belum bisa lanjut"

        context = {
            'host': settings.HOST,
            'email_title': title,
            'indicator': settings.INDICATOR_GRADUATED
        }
        context.update(sett)

        for data in self.users_repeat:
            context['user'] = data['user']
            context['graduate'] = data['status']['graduate']
            html_message = render_to_string('emails/participants_repeat.html', context=context)

            Inbox.objects.create(user=data['user'], subject=title, content=html_message)

            kwargs = construct_email_args(
                recipients=data['user'].email,
                subject=title,
                content=html_message
            )
            student = data['user'].get_student()
            student.status = Student.STATUS.repeat
            student.save(update_fields=['status'])
            django_rq.enqueue(mail.send, **kwargs)

        return len(self.users_repeat)

class AddTrainingStatus(forms.ModelForm):
    training_material = TrainingMaterialField()
    STATUS = Choices(
        (1, 'not_yet', 'Belum'),
        (2, 'graduate', 'Lulus'),
        (3, 'repeat', 'Ulang'),
    )
    status = forms.ChoiceField(choices=STATUS, required=False, label="")

    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)

    class Meta:
        model = TrainingStatus
        fields = ('training_material', 'status')

    def clean(self):
        cleaned_data = super().clean()
        if TrainingStatus.objects.filter(user=self.user, training_material=cleaned_data['training_material']).exists():
            raise forms.ValidationError(
                f"{self.user.name} sudah memiliki materi {cleaned_data['training_material']}"
            )
        return cleaned_data

    def save(self, *args, **kwargs):
        training_status = super().save(commit=False)
        training_status.user = self.user
        training_status.save()
        return training_status

class GraduateTrainingStatusFormSet(BaseStatusTrainingFormSet):

    def __init__(self, graduate, *args, **kwargs):
        """
        create label any button to action delete training status
        training material data get form user, not from batch
        """
        self.graduate = graduate
        self.user = graduate.user
        self.trainig = {
            'materials': [],
            'status': []
        }
        for ts in self.user.training_status.all():
            if ts.training_material:
                self.trainig['status'].append(ts)
                self.trainig['materials'].append(ts.training_material)

        super().__init__(training_materials=self.trainig['materials'], *args, **kwargs)
        for i, form in enumerate(self.forms):
            # skip or continue last loop
            if i > len(self.training_materials) - 1:
                continue
            material = self.trainig['materials'][i]
            status = self.trainig['status'][i]

            # create button to combine with label
            url = reverse('backoffice:graduates:delete_training_status', args=[status.id, graduate.id])
            html_button = ''
            label = mark_safe(f"{html_button} {material.code} - {material.title}")

            form.fields['training_material'].label = label

class BaseFilterForm(forms.Form):
    name = forms.CharField(
        widget=forms.TextInput(attrs={'placeholder': 'Cari: nama/username'}),
        required=False
    )
    batch = forms.ModelChoiceField(
        queryset=Training.objects.order_by('batch'), empty_label="Pilih Angkatan", required=False
    )
    STATUS = (
        (None, 'Pilih Status'),
        (True, 'Tersalurkan'),
        (False, 'Belum Tersalurkan'),
    )
    is_channeled = forms.ChoiceField(label='Tersalurkan', required=False, choices=STATUS)

    def __init__(self, *args, **kwargs):
        self.graduates = None
        super().__init__(*args, **kwargs)

    def get_data(self):
        name = self.cleaned_data['name']
        batch = self.cleaned_data['batch']
        is_channeled = self.cleaned_data['is_channeled']
        graduates = Graduate.objects.select_related('user')

        if batch:
            graduates = graduates.filter(student__training=batch)
        if is_channeled:
            graduates = graduates.filter(is_channeled=is_channeled)
        if name:
            graduates = graduates.filter(
                Q(user__first_name__icontains=name) | Q(user__last_name__icontains=name) |
                Q(user__username__icontains=name)
            )

        self.graduates = graduates
        return self.graduates

    def generate_to_csv(self):
        csv_buffer = StringIO()
        writer = csv.writer(csv_buffer)
        writer.writerow([
            'No.', 'Nama', 'No.Sertifikat', 'Email', 'No. Ponsel', 'Tersalurkan'
        ])
        for index, graduate in enumerate(self.graduates, 1):
            writer.writerow([
                index, graduate.user.name, graduate.certificate_number,
                graduate.user.email, graduate.user.phone,
                'Ya' if graduate.is_channeled else 'Tidak'
            ])

        return csv_buffer

class GraduateHasChanneledForm(forms.Form):
    channeled_at = forms.CharField(label='Tersalurkan pada', max_length=150, help_text='Nama Perusahaan')
    graduate_id = forms.CharField(max_length=100, label='', widget=forms.HiddenInput())

    def save(self):
        cleaned_data = super().clean()
        graduate = Graduate.objects.get(id=cleaned_data['graduate_id'])
        graduate.channeled_at = cleaned_data['channeled_at']
        graduate.is_channeled = True
        graduate.save()

class RatingForm(forms.ModelForm):

    class Meta:
        model = Rating
        exclude = ('graduate',)
        help_texts = {
            'respondent_name': 'Nama reponden bisa perusahaan, personal, atau jabatan.'
        }


# -*- coding: utf-8 -*-
"""
Created on Thu Aug 12 19:04:25 2021

"""

cadena = 'jamón'
capadepenapa = ''
for c in cadena:
        capadepenapa = capadepenapa + c
        if c == 'a':
            capadepenapa = capadepenapa + 'pa'
        elif c == 'e':
            capadepenapa = capadepenapa + 'pe'
        elif c == 'i':
            capadepenapa = capadepenapa + 'pi'
        elif c == 'o':
            capadepenapa = capadepenapa + 'po'
        if c == 'u':
            capadepenapa = capadepenapa + 'pu'
        print (capadepenapa)

# =============================================================================
# Buenas estimade.
# El ejercicio funciona, pero la salida con la sentencia print(capadepenapa)
# deberia estar fuera del ciclo for para que no imprima el proceso de formacion
# de capadepenapa, sino solamente su resultado final.
#
# Otra observación es que está demasiado limitado
# a lo pedido en la consigna y no tiene en cuenta otras cadenas de ingreso. Por
# ejemplo, si se ingresara la cadena 'Cualquiera', la salida seria
# "Cupuapalqupuipieperapa", la cual tiene un error al agregar la silaba 'pu'
# luego de la letra u que sucede a la letra q. Lo mismo sucederia en palabras
# con diptongos, y no se agregarian las silabas correspondientes si la cadena
# tuviera palabras con tilde o dieresis.
# Si bien el planteo del ejercicio no pedia contemplar las
# distintas posibilidades (diptongos, tildes, dieresis, etc.) es una buena
# forma de mejorar y aprender intentar que cada ejercicio sea lo mas
# completo posible.
# =============================================================================


import numpy as np
import matplotlib.pyplot as plt
import os

def chi2(exp, obs):
    return np.sum((exp - obs)**2 / exp)

def Sort2Array(x, y):
    temp = np.array([x,y]).T
    #  print(temp)
    temp = temp[temp[:,0].argsort()].T
    return temp[0], temp[1]

k = 0
directory = "./"
chi2Array = np.array([])
timedelArray = np.array([])
for filename in os.listdir(directory):
    if filename.startswith('disp.') and filename.endswith('.dat'):
        #  print(filename)
        timedelBin = filename.replace('disp.out_magshiftbin_', '').replace('.dat', '')
        timedelBin = int(timedelBin)
        timedelArray = np.append(timedelArray, timedelBin*0.1 - 2.5)
        data = np.genfromtxt(filename, delimiter=" ").T
        #  print(data)
        timedel = data[0]
        measDisp = data[1]
        measFit = data[2]
        chi2Array = np.append(chi2Array, chi2(measFit, measDisp))

        plt.figure(num=k, figsize=(10,6))
        plt.plot(timedel, measDisp, label="measured", marker="o", color="black")
        plt.plot(timedel, measFit, label="fitted", color="black")
        plt.legend()
        plt.xlabel("Time delay [days]")
        plt.ylabel("Dispersion")

        ax = plt.gca()
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)

        plt.savefig(filename + ".pdf", bbox_inches="tight")
        plt.close(k)
        k+=1

timedelArray, chi2Array = Sort2Array(timedelArray, chi2Array)
#  print(chi2Array)
#  print(timedelArray)
plt.figure(num=k, figsize=(10,6))
plt.plot(timedelArray, chi2Array, marker="o", color="black")
plt.xlabel("Magnitude difference")
plt.ylabel("$\chi^2$")
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.savefig("chi2.pdf", bbox_inches="tight")
plt.close(k)
k += 1