#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
d2lstat.py

python d2lstat.py usage_data.csv full_time.csv part_time.csv semester_name number_of_courses_running

Given a correct data set and lists of full- and part-time teachers, this program will generate usage statistics on a
given template, which can be edited under the generate_document function.

Authors:
Dan Ricker <daniel.ricker@scranton.edu>
Sean Batzel <sean.batzel@scranton.edu>

This program is the property of the UofS-CTLE.
"""
from __future__ import division

import csv
import os

from django.conf import settings

# GLOBAL SETTINGS - DO NOT CHANGE UNLESS ABSOLUTELY NECESSARY.
DELIMITER = '|'  # Determines what character the program wil break data rows on.
ASSIGNMENTS = 13  # The column that assignments data is located in.
GRADE = 15  # The column that grade item data is located in.
GRADED = 16  # The column that graded grade item data is located in.
DISCUSSION = 18  # The column for discussion post usage.
USAGE_ROYAL = 3  # The column where the instructor's R number is located in the usage file.
FAC_ROYAL = 0  # Where the faculty member's R number is located in the full-/part-time CSV files.

def filter_for_semester(files_data, semester):
    """
    Used to filter the entire set of usage data for a given semester.

    :param files_data: The list of all course data taken from the usage CSV file.
    :param semester: The year and semester (e.g. 2018_Fall) to search for in the usage data.
    :return: The list of courses pertaining to the given semester.
    """
    final = list()
    for x in files_data:
        y = x.split(DELIMITER)
        if semester in y[9]:  # NOTE This will always work, provided the semester string is given correctly.
            final.append(x)
    return final

def get_rows_with_usage(files_data):
    """
    Filters the semester's courses on which ones had activity in D2L.

    :param files_data: The filtered list of courses for the semester.
    :return: The list of courses that had relevant usage in D2L.
    """
    final = list()
    for x in files_data:
        y = x.split(DELIMITER)
        if int(y[ASSIGNMENTS]) > 0 or int(y[GRADE]) > 2 or int(y[GRADED]) > 0 or int(y[DISCUSSION]) > 0:
            final.append(x)
    return final

def remove_duplicate_crn(files_data):
    """
    Makes sure that none of the courses in the list repeat.

    :param files_data: Filtered data passed in from get_rows_with_usage.
    :return: The list of coures without any repeated.
    """
    seen_crns = []
    ret_val = []
    for x in files_data:
        y = x.split(DELIMITER)
        # This checks the last 5 characters of y[9] for a CRN.
        # Make sure this is where the CRN is still located before running.
        if y[9][-5:] not in seen_crns:
            seen_crns.append(y[9][-5:])
            ret_val.append(x)
    return ret_val

def remove_duplicate_royal(files_data):
    """
    Filters and removes duplicate instructor RIDs.

    :param files_data: Filtered data passed in from get_rows_with_usage.
    :return: The list of courses with no instructor R numbers duplicated.
    """
    seen_royal = []
    ret_val = []
    for x in files_data:
        y = x.split(DELIMITER)
        if y[USAGE_ROYAL] not in seen_royal:
            seen_royal.append(y[USAGE_ROYAL])
            ret_val.append(x)
    return ret_val

def parse_files(usage, full_time, part_time, semester, total_courses):
    """
    Reads the given CSV files and splits up the contents into digestible data structures.

    :param usage: The name of the usage CSV file.
    :param full_time: The name of the CSV of full-time faculty members.
    :param part_time: The name of the CSV of part-time faculty members.
    :param semester: The semester in the form YYYY_Session (e.g. 2018_Fall).
    :param total_courses: (Provided externally) The number of courses running for the given semester.
    :return: A data structure containing all of the data required for the rest of the program.
    """
    with open(usage, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(usage, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    with open(full_time, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(full_time, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    with open(part_time, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(part_time, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    one = filter_for_semester(open(usage, 'r').readlines(), semester)
    two = get_rows_with_usage(one)
    usage_file = remove_duplicate_crn(two)
    no_dup_r = remove_duplicate_royal(two)
    full_time_file = open(full_time, 'r').readlines()
    full_r = list()
    part_r = list()
    for x in full_time_file:
        y = x.split(DELIMITER)
        full_r.append(y[FAC_ROYAL])
    part_time_file = open(part_time, 'r').readlines()
    for x in part_time_file:
        y = x.split(DELIMITER)
        part_r.append(y[FAC_ROYAL])
    full = list()
    part = list()
    staff = list()
    for x in range(len(part_r)):
        part_r[x] = part_r[x].strip("\"")
    for x in range(len(full_r)):
        full_r[x] = full_r[x].strip("\"")
    for x in no_dup_r:
        y = x.split(DELIMITER)
        if y[USAGE_ROYAL] in full_r:
            full.append(y)
        elif y[USAGE_ROYAL] in part_r:
            part.append(y)
        else:
            staff.append(y)
    return {'semester_no_dup_crn': usage_file,
            'semester_no_dup_r': no_dup_r,
            'semester': two,
            'full_time': full,
            'len_full': len(full_time_file),
            'part_time': part,
            'len_part': len(part_time_file),
            'staff': staff,
            'total_courses': total_courses}

def calculate_stats(file_data):
    """
    Carries out the actual logic for generating the numbers that will be included in the report.

    :param file_data: The data produced by the parse function.
    :return: The statistics data required for the report generator.
    """
    specifics = {
        'assignments': 0,
        'grade': 0,
        'graded': 0,
        'discussion': 0
    }
    for course in file_data['semester_no_dup_crn']:
        x = course.split(DELIMITER)
        if int(x[ASSIGNMENTS]) > 0:
            specifics['assignments'] += 1
        if int(x[GRADE]) > 2:
            specifics['grade'] += 1
        if int(x[GRADED]) > 0:
            specifics['graded'] += 1
        if int(x[DISCUSSION]) > 0:
            specifics['discussion'] += 1
    return {'semester': file_data['semester'],
            'courses_with_usage': len(file_data['semester_no_dup_crn']),
            'faculty_with_usage': len(file_data['semester_no_dup_r']),
            'full_time': len(file_data['full_time']),
            'total_full_time': file_data['len_full'],
            'part_time': len(file_data['part_time']),
            'total_part_time': file_data['len_part'],
            'staff': len(file_data['staff']),
            'specifics': specifics,
            'total_courses': file_data['total_courses']}

def generate_document(stats, semester):
    """
    Generates an HTML document and PDF with the requested stats for the semester's D2L usage.

    :param stats: The dictionary returned by the generate_stats function.
    :param semester: The semester value passed in as a command-line argument.
    """
    with open(os.path.join(settings.BASE_DIR, 'd2lstat/templates/d2lstat/raw_html.html'), 'r') as f:
        string = f.read()
    string = string.format(semester,
                           stats['faculty_with_usage'],
                           stats['full_time'],
                           stats['total_full_time'],
                           round((stats['full_time'] / stats['total_full_time']) * 100, 1),
                           stats['part_time'],
                           stats['total_part_time'],
                           round((stats['part_time'] / stats['total_part_time']) * 100, 1),
                           stats['staff'],
                           stats['courses_with_usage'],
                           stats['total_courses'],
                           round((stats['courses_with_usage'] / int(stats['total_courses'])) * 100, 1),
                           stats['specifics']['assignments'],
                           stats['specifics']['grade'],
                           stats['specifics']['graded'],
                           stats['specifics']['discussion'])
    with open(os.path.join(settings.BASE_DIR, 'd2lstat/templates/d2lstat/report.html'), 'w') as f:
        f.write(string)
        f.close()

def process_file(usage, full_time, part_time, semester, total_courses):
    """

    :param usage:
    :param full_time:
    :param part_time:
    :param total_courses
    :param semester:
    """
    res = parse_files(usage, full_time, part_time, semester, total_courses)
    res = calculate_stats(res)
    generate_document(res, semester)

def calculateVirtualClassroomStats(usage, fullTime, partTime, VCDataFile):
    resultList = []
    # read in the data from the lti (learning tools integration) file
    virtualClassroomDataFile = open(VCDataFile, 'rU')
    virtualClassroomDataReader = csv.reader(virtualClassroomDataFile)
    virtualClassroomData = []
    seenVirtualClassRoomOrgUnitIds = []
    for row in virtualClassroomDataReader:
        if ('youseeu' in row[5] and row[
            1] not in seenVirtualClassRoomOrgUnitIds):  # get the org unit ids of the courses in which there was created at least one virtual classroom meeting
            virtualClassroomData.append(row)
            seenVirtualClassRoomOrgUnitIds.append(row[1])
    # read in the instructor usage data file that was obtained from desire 2 learn data hub
    instructorUsageDataFile = open(usage, 'rU')
    instructorUsageDataReader = csv.reader(instructorUsageDataFile)
    instructorUsageData = []
    seenRIds = []
    numberOfFacultyMembersUsingVirtualClassroom = 0
    fullDataOnInstructors = []
    # print('Instructors That Have Created at Least 1 Virtual Classroom Meeting:')
    resultList.append('Instructors That Have Created at Least 1 Virtual Classroom Meeting:')
    for row in instructorUsageDataReader:
        if (row[
            10] in seenVirtualClassRoomOrgUnitIds):  # filter the rows to just be the rows for faculty members that have created at least one virtual classroom meeting
            if (row[3] not in seenRIds):  # make sure that each instructor is onlt accounted for once
                seenRIds.append(row[3])
                fullDataOnInstructors.append([row[3], row[1], row[2]])
                # print(row[3] + ': ' + row[1] + ', ' + row[2])
                resultList.append(row[3] + ': ' + row[1] + ', ' + row[2])
    numberOfFacultyMembersUsingVirtualClassroom = len(seenRIds)

    # print('Number of Instructors That Have Created at Least 1 Virtual Classroom Meeting: ' + str(
    #     numberOfFacultyMembersUsingVirtualClassroom))
    resultList.append('Number of Instructors That Have Created at Least 1 Virtual Classroom Meeting: ' + str(
        numberOfFacultyMembersUsingVirtualClassroom))
    seenFullAndPartTimeRIds = []  # this is needed to keep track of the Rids that belong to either full or part time faculty members in order to determine which rids are left over, the left over rids are the rids of staff members teaching part time
    # Full time faculty members that have created at least one virtual classroom meeting
    fullTimeFacultyDataFile = open(fullTime, 'rU')
    fullTimeFacultyDataReader = csv.reader(fullTimeFacultyDataFile)
    fullTimeFacultyUsingVirtualClassroomRids = []
    for row in fullTimeFacultyDataReader:
        if (row[0] in seenRIds):
            fullTimeFacultyUsingVirtualClassroomRids.append(row[0])
            seenFullAndPartTimeRIds.append(row[0])
    # Part time faculty members that have created at least one virtual classroom meeting
    partTimeFacultyDataFile = open(partTime, 'rU')
    partTimeFacultyDataReader = csv.reader(partTimeFacultyDataFile)
    partTimeFacultyUsingVirtualClassroomRids = []
    for row in partTimeFacultyDataReader:
        if (row[0] in seenRIds):
            partTimeFacultyUsingVirtualClassroomRids.append(row[0])
            seenFullAndPartTimeRIds.append(row[0])
    staffTeachingPartTimeRids = []
    for rid in seenRIds:
        if (rid in seenFullAndPartTimeRIds):
            staffTeachingPartTimeRids.append(rid)
    # sort full instructor data based upon the rids in each category
    fullTimeFacultyUsingVC = []
    partTimeFacultyUsingVC = []
    staffUsingVC = []
    # sort out full time faculty
    for rid in fullTimeFacultyUsingVirtualClassroomRids:
        for row in fullDataOnInstructors:
            if (row[0] == rid):
                fullTimeFacultyUsingVC.append(row)
    # sort out part time faculty
    for rid in partTimeFacultyUsingVirtualClassroomRids:
        for row in fullDataOnInstructors:
            if (row[0] == rid):
                partTimeFacultyUsingVC.append(row)
    # sort out staff
    for row in fullDataOnInstructors:
        if (row[0] not in seenFullAndPartTimeRIds):
            staffUsingVC.append(row)
    resultList.append("Full Time Faculty Using Virtual Classroom:")
    # print("Full Time Faculty Using Virtual Classroom:")
    for row in fullTimeFacultyUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])

    # print("The Number of Full Time Faculty Using Virtual Classroom: " + str(len(fullTimeFacultyUsingVC)))
    resultList.append("The Number of Full Time Faculty Using Virtual Classroom: " + str(len(fullTimeFacultyUsingVC)))
    # print("Part Time Faculty Using Virtual Classroom:")
    resultList.append("Part Time Faculty Using Virtual Classroom:")
    for row in partTimeFacultyUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])
    # print("The Number of Part Time Faculty Using Virtual Classroom: " + str(len(partTimeFacultyUsingVC)))
    resultList.append("The Number of Part Time Faculty Using Virtual Classroom: " + str(len(partTimeFacultyUsingVC)))
    # print("Staff Teaching Part Time Using Virtual Classroom:")
    resultList.append("Staff Teaching Part Time Using Virtual Classroom:")
    for row in staffUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])
    print("The Number of Staff Teaching Part Time Using Virtual Classroom: " + str(len(staffUsingVC)))
    resultList.append("The Number of Staff Teaching Part Time Using Virtual Classroom: " + str(len(staffUsingVC)))
    return resultList

def facultyNotUsingD2LCalculation(usage, fullTime, partTime, semester):
    resultList = []
    usageFile = open(usage, 'rU')
    fullTimeFile = open(fullTime, 'rU')
    partTimeFile = open(partTime, 'rU')
    usageFileReader = csv.reader(usageFile)
    fullTimeFileReader = csv.reader(fullTimeFile)
    partTimeFileReader = csv.reader(partTimeFile)
    usageDataRaw = []
    usageData = []
    fullTimeDataRaw = []
    partTimeDataRaw = []
    fullTimeNotUsing = []
    partTimeNotUsing = []
    staffTeachingPartTimeNotUsing = []
    fullTimeRIds = []
    partTimeRIds = []
    ridsOfInstructorsUsing = []
    seenRidsOfInstructorsUsing = []
    ridsOfInstructorsUsingDuplicatesRemoved = []
    # read in the usage data
    for row in usageFileReader:
        usageDataRaw.append(row)
    # Get the rids of the instructors using D2L
    for row in usageDataRaw:
        # print(row[13] + ',' +row[15] + ',' +row[16] + ',' +row[18])
        try:
            if((int(row[13])>0 or int(row[15])>2 or int(row[16])>0 or int(row[18])>0)):
                if(semester in row[9]):
                    ridsOfInstructorsUsing.append(row[3])
        except Exception:
            print(Exception)
    seenRIds = []
    # Filter the rids of instuctors using for duplicates
    for row in ridsOfInstructorsUsing:
        if(row not in seenRidsOfInstructorsUsing):
            ridsOfInstructorsUsingDuplicatesRemoved.append(row)
            seenRidsOfInstructorsUsing.append(row)
    #get the Rids of instructors not using
    for row in usageDataRaw:
        if(row[3] not in ridsOfInstructorsUsingDuplicatesRemoved and semester in row[9]):
            usageData.append(row)
    for row in fullTimeFileReader:
        fullTimeDataRaw.append(row)
    for row in partTimeFileReader:
        partTimeDataRaw.append(row)
    for row in fullTimeDataRaw:
        fullTimeRIds.append(row[0])
    for row in partTimeDataRaw:
        partTimeRIds.append(row[0])
    for row in usageData:
        if(row[3] in fullTimeRIds and row[3] not in seenRIds):
            fullTimeDepartment = ''
            for fullTimeRow in fullTimeDataRaw:
                if(row[3]==fullTimeRow[0]):
                    fullTimeDepartment = fullTimeRow[5]
            fullTimeNotUsing.append([row[3], row[1], row[2], fullTimeDepartment])
            seenRIds.append(row[3])
        elif(row[3] in partTimeRIds and row[3] not in seenRIds):
            if(row[3] in partTimeRIds):
                partTimeDepartment = ''
                for partTimeRow in partTimeDataRaw:
                    if(row[3]==partTimeRow[0]):
                        partTimeDepartment = partTimeRow[5]
                partTimeNotUsing.append([row[3], row[1], row[2], partTimeDepartment])
                seenRIds.append(row[3])
        else:
            if(row[3] not in seenRIds):
                staffTeachingPartTimeNotUsing.append([row[3], row[1], row[2]])
                seenRIds.append(row[3])
    resultList.append('The Total Number of Faculty Not Using Desire 2 Learn: ' + str(len(fullTimeNotUsing)+len(partTimeNotUsing) + len(staffTeachingPartTimeNotUsing)))
    resultList.append('The Number of Full Time Faculty Not Using D2L: ' + str(len(fullTimeNotUsing)))
    resultList.append('Full Time Faculty Not Using Desire 2 Learn:')
    for row in fullTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2] + ', ' + row[3])
    resultList.append('')
    resultList.append('')
    resultList.append('The Number of Part Time Faculty Not Using D2L: ' + str(len(partTimeNotUsing)))
    resultList.append('Part Time Faculty Not Using Desire 2 Learn:')
    for row in partTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2] + ', ' + row[3])
    resultList.append('')
    resultList.append('')
    resultList.append(
        'The Number of Staff Teaching Part Time Faculty Not Using D2L: ' + str(len(staffTeachingPartTimeNotUsing)))
    resultList.append('Staff Teaching Part Time Not Using Desire 2 Learn:')
    for row in staffTeachingPartTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2])
    return resultList


from __future__ import annotations

from typing import NewType

from htmltools import Tag, TagAttrs, TagAttrValue, TagChild

from ._htmldeps import page_dep
from ._utils import assign_to_slot

SidebarTag = NewType("SidebarTag", Tag)

def sidebar(
    *args: IconSectionTag | TagChild | TagAttrs,
    open_width_px: int = 320,
    **kwargs: TagAttrValue,
) -> SidebarTag:
    """
    Collapsible sidebar for use in dashboards and cards.

    Parameters
    ----------
    *args : Union[TagChild, TagAttrs]
        Child elements to this tag.
    open_width_px : int, optional
        The width of the sidebar when it is open, in pixels, by default 320.
    **kwargs : Dict[str, TagAttrValue]
        Attributes to this tag.

    Returns
    -------
    Tag representing a collapsible sidebar.

    See Also
    --------
    ~shinycomponent.dashboard
    ~shinycomponent.card
    ~htmltools.Tag
    """
    return SidebarTag(
        Tag(
            "shiny-sidebar",
            page_dep(),
            *args,
            _add_ws=False,
            openWidthPx=open_width_px,
            **kwargs,
        )
    )

FooterTag = NewType("DashboardFooterTag", Tag)

def footer(
    *args: TagChild | TagAttrs, _add_ws: bool = True, **kwargs: TagAttrValue
) -> FooterTag:
    """
    A footer for a dashboard. Sticks to bottom of dashboard layouts defined with
    `shinycomponent.page_dashboard()` or `shinycomponent.dashboard()`.

    Parameters
    ----------
    *args
        Child elements to this tag.
    **kwargs
        Attributes passed along to html element.

    Returns
    -------
    Tag element

    See Also
    --------
    ~shinycomponent.dashboard
    ~shinycomponent.page_dashboard
    ~htmltools.Tag
    """
    return FooterTag(Tag("sc-footer", page_dep(), *args, _add_ws=_add_ws, **kwargs))

HeaderTag = NewType("DashboardHeaderTag", Tag)

def header(
    *args: TagChild | TagAttrs, _add_ws: bool = True, **kwargs: TagAttrValue
) -> HeaderTag:
    """
    A header for a dashboard. Sticks to top of dashboard layouts defined with
    `shinycomponent.page_dashboard()` or `shinycomponent.dashboard()`.

    Parameters
    ----------
    *args
        Child elements to this tag.
    **kwargs
        Attributes passed along to html element.

    Returns
    -------
    Tag element

    See Also
    --------
    ~shinycomponent.dashboard
    ~shinycomponent.page_dashboard
    ~htmltools.Tag
    """
    return HeaderTag(Tag("sc-header", page_dep(), *args, _add_ws=_add_ws, **kwargs))

IconSectionTag = NewType("IconSectionTag", Tag)

def icon_section(
    *args: TagChild | TagAttrs,
    icon: str | Tag,
    **kwargs: TagAttrValue,
) -> IconSectionTag:
    """
    A section with a left-side icon. Can be paired with sidebar to create a sidebar that
    will collapse to icons

    Parameters
    ----------
    *args
        The contents of the section. E.g. inputs and text.
    icon
        The icon to display on the left side of the section. Can be a representing an
        icon available from the [iconify icons list](https://icon-sets.iconify.design/)
        or an abritrary tag. (Try and make sure the tag is simple and narrow or else
        overflow will happen)
    **kwargs
        Additional attributes passed to the element

    Returns
    -------
    Section with left-side icon

    See Also
    --------
    ~shinycomponent.sidebar
    ~shinycomponent.icon
    ~shinycomponent.dashboard
    ~htmltools.Tag
    """

    # If icon is a string, we just pass it as the property icon and let the component do
    # the work. If it's a tag, we need to place it as a child with the slot attribute of
    # "icon"
    if isinstance(icon, str):
        kwargs["icon"] = icon
    else:
        args = (assign_to_slot(icon, "icon"), *args)

    return IconSectionTag(
        Tag("shiny-section", page_dep(), *args, _add_ws=True, **kwargs)
    )


from django.shortcuts import redirect, render
from django.urls import reverse_lazy
from django.views.generic.edit import DeleteView
from django.views.generic.list import ListView
from django.views.generic.base import TemplateView
from .models import Escala

from django.contrib import messages
from .forms import EscalaModelForm
from medico.models import Medico
from posto.models import Posto
from folga.models import Folga
from datetime import datetime, date

# Create your views here.
class HomeView(TemplateView):
    template_name = 'escala/home_page.html'

class ListEscalaView(ListView):
    model = Escala
    template_name = 'escala/index.html'

def create_escala(request):
    formulario = EscalaModelForm(request.POST)

    if request.method == 'POST':

        if formulario.is_valid():
            medico = request.POST.get('medico')
            posto = request.POST.get('posto_de_trabalho')
            data = request.POST.get('data')

            # de string pra date
            data_escala = datetime.strptime(data, '%d/%m/%Y').date()
            # data de hoje
            hoje = date.today()

            # Verificando dia da escala.
            if data_escala >= hoje:
                try:
                    # analisando se o médico está ativo
                    medico_qs = Medico.objects.get(id=medico)
                    medico_status = medico_qs.ativo

                    # analisando se não está em escala
                    medico_escala_qs = Escala.objects.filter(medico_id=medico)

                    # analisando se não está de folga
                    folga_qs = Folga.objects.filter(medico_id=medico, dia_de_folga=data_escala)
                    medico_disponivel = False

                    if len(folga_qs) > 0:
                        if folga_qs[0].dia_de_folga != data_escala:
                            medico_disponivel = True
                        else:
                            messages.error(request, 'Não é possível, pois a data da escala do médico é igual da folga.')
                    else:
                        medico_disponivel = True

                    # analisando se o posto está ativo
                    posto_qs = Posto.objects.get(id=posto)
                    posto_status = posto_qs.ativo

                    if medico_status and len(medico_escala_qs) == 0 and medico_disponivel and posto_status:
                        # salvando dados
                        formulario.save()

                        # redirecionando
                        return redirect('escala:escala')

                    else:
                        messages.error(request, 'Verifique se posto de trabalho e médico estão ativos / aptos.')

                except Exception as e:
                    messages.error(request, f'Opa, houve um erro: {e}...')

            else:
                messages.error(request, 'Esta data já passou.')

        else:
            messages.error(request, 'Houve algum erro, tente novamente.')

    else:
        formulario = EscalaModelForm()

    context = {
        'form': formulario
    }

    return render(request, 'escala/escala_form.html', context)

def update_escala(request, pk):
    escala = Escala.objects.get(pk=pk)
    formulario = EscalaModelForm(instance=escala)

    if request.method == 'POST':
        formulario = EscalaModelForm(request.POST, instance=escala)

        if formulario.is_valid():
            medico = request.POST.get('medico')
            posto = request.POST.get('posto_de_trabalho')
            data = request.POST.get('data')

            # de string pra date
            data_escala = datetime.strptime(data, '%d/%m/%Y').date()
            # data de hoje
            hoje = date.today()

            # Verificando dia da escala.
            if data_escala >= hoje:
                try:
                    # analisando se o médico está ativo
                    medico_qs = Medico.objects.get(id=medico)
                    medico_status = medico_qs.ativo
                    medico_disponivel = False

                    # analisando escala do médico
                    medico_escala_qs = Escala.objects.filter(medico_id=medico)

                    if len(medico_escala_qs) > 0:
                        if medico_escala_qs[0].data != data_escala:
                            medico_disponivel = True
                        else:
                            messages.error(request, 'Coloque uma data diferente da atual.')
                    else:
                        medico_disponivel = True

                    # analisando se não está de folga
                    folga_qs = Folga.objects.filter(medico_id=medico, dia_de_folga=data_escala)

                    if len(folga_qs) > 0:
                        if folga_qs[0].dia_de_folga != data_escala:
                            medico_disponivel = True
                        else:
                            messages.error(request, 'Não é possível, pois a data da escala do médico é igual da folga.')
                    else:
                        medico_disponivel = True

                    # analisando se o posto está ativo
                    posto_qs = Posto.objects.get(id=posto)
                    posto_status = posto_qs.ativo

                    if medico_status and medico_disponivel and posto_status:
                        # salvando dados
                        formulario.save()

                        # redirecionando
                        return redirect('escala:escala')

                    else:
                        messages.error(request, 'Verifique se posto de trabalho e médico estão ativos / aptos.')

                except Exception:
                    messages.error(request, 'Opa, houve um erro...')

            else:
                messages.error(request, 'Esta data já passou.')

        else:
            messages.error(request, 'Houve algum erro, tente novamente.')

    context = {
        'form': formulario
    }

    return render(request, 'escala/escala_form.html', context)

class DeleteEscalaView(DeleteView):
    model = Escala
    template_name = 'escala/escala_delete.html'
    success_url = reverse_lazy('escala:escala')


# -*- coding: utf-8 -*-
# @Time    : 2019/1/3 15:43
# @Author  : zgh
# @Email   : 849080458@qq.com
# @File    : auto_oracle_insert.py
# @Software: PyCharm

import cx_Oracle

# 注：设置环境编码方式，可解决读取数据库乱码问题
import os
os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8'

# scott是数据用户名，tiger是登录密码（默认用户名和密码）
connection = cx_Oracle.connect("hg", "123456", '122.112.248.182:1521/CITYDO')

# 操作游标
cursor = connection.cursor()

def JGHG_insert_data(table):
    sql = auto_insert(table)
    #print(sql)

    #批量生成数据进行插入
    for i in range(50000,60000):
        ii=1000+i

        #插入数据格式（修改内容）
        table_data =  [('88T646' + str(ii), '987465004' + str(ii), '648665004' + str(i), 'DKFH0992' + str(ii),'DKeeeFH0992' + str(ii), '抵押','313340' + str(ii), 20180506, 20180506, '解除' ,  '科目1' + str(i), 20180504)]

        cursor.prepare(sql)#sql语句，需要与数据库字段相对应，value值长度与字段对应
        cursor.execute(None,table_data[0])#插入数据集
        print('执行sql:', sql)
        print('插入内容：', table_data[0])

        # 切记一定要执行
        cursor.execute('commit')

    # 关闭连接，释放资源
    cursor.close()
    connection.close()

def auto_insert(table):
    #执行SQL语句
    curs = cursor.execute("select column_name from all_tab_columns where  TABLE_NAME = '" + table + "'order by column_id")
    #查看数据库数据
    data = curs.fetchall()
    data_1=[]
    test=[]
    for i in range(len(data)):
        data_1.append(data[i][0])
        print(data[i][0])
        test.append(':'+str(i+1))#生成value数列

    table_sql="INSERT INTO "+ table + "("+ ','.join(data_1) + ")" + " VALUES " + "("+ ','.join(test) + ")"

    # data_2=[i for i in reversed(data_1)]#反序排列
    # print(','.join(data_1))  # 转为元组
    # print(','.join(test))#转为元组
    # print(data)
    # print(len(data))
    return table_sql

JGHG_insert_data('YJPT_DBGX')


import torch
import torch.utils.data
from torch import nn, optim
from torch.nn import functional as F
import numpy as np

class SparseAttnBottleneck(nn.Module):
    def __init__(self, voc_size, dim, num_topk):
        super().__init__()
        self.voc_size = voc_size
        self.codebook = nn.Parameter(torch.randn(voc_size, dim))
        import math
        # initialize the codebook just like linear layer
        nn.init.kaiming_uniform_(self.codebook, a=math.sqrt(5))
        self.codebook.cuda()  # just a hack
        self.to_q = nn.Linear(dim, dim)
        self.to_k = nn.Linear(dim, dim)
        self.to_v = nn.Linear(dim, dim)
        self.num_topk = num_topk

    def max_neg_value(self, tensor):
        return -torch.finfo(tensor.dtype).max

    def forward(self, x):
        # x: (batch, *, dim)
        q = self.to_q(x)
        # here I first assume k and v are both comes from the codebook
        k = self.to_k(self.codebook)
        v = self.to_v(self.codebook)
        # attention
        dots = torch.einsum('bd,kd->bk', q, k)

        # top k of attention
        top, _ = dots.topk(self.num_topk, dim=-1)
        vk = top[..., -1].unsqueeze(-1).expand_as(dots)
        mask = dots < vk
        mask_value = self.max_neg_value(dots)
        dots.masked_fill_(mask, mask_value)

        # softmax
        attn = F.softmax(dots, dim=-1)

        # get output by applying attention weight to v
        out = torch.einsum('bk,kd->bd', attn, v)
        return out

from models.wta_utils import ResBlock, weights_init_wta, weights_init_vqvae

class SparseAttnAutoencoderFeatureMap(nn.Module):
    # https://github.com/iwyoo/tf_ConvWTA/blob/master/model.py
    def __init__(self, latent_dim=128, hid_channels=64, voc_size=8, num_topk=6, proj_hidden_dim=512,
                 ch=1, image_size=28, sample_num=1024,
                 kernel_size=5, net_type='vqvae',
                 out_act='sigmoid',
                 **kwargs):
        super(SparseAttnAutoencoderFeatureMap, self).__init__()
        sz = hid_channels
        self.sz = sz
        latent_dim = hid_channels
        code_sz = latent_dim
        self.hid_channels = hid_channels
        self.latent_dim = latent_dim
        self.proj_output_dim = latent_dim
        self.code_sz = code_sz
        self.ch = ch
        self.image_size = image_size
        self.sample_z = torch.rand(sample_num, ch, image_size, image_size)

        # use 1x1 conv to reduce the dimension of the feature map to 6 channels
        self.bottleneck_conv = nn.Conv2d(self.code_sz, 6, 1)
        self.bottleneck_fc = nn.Linear((image_size // 4) * (image_size // 4), latent_dim)

        if net_type == 'wta':
            self.enc = nn.Sequential(
                # input is Z, going into a convolution
                nn.Conv2d(ch, sz, kernel_size, 1, 0),
                nn.ReLU(True),
                nn.Conv2d(sz, sz, kernel_size, 1, 0),
                nn.ReLU(True),
                nn.Conv2d(sz, self.code_sz, kernel_size, 1, 0),
                nn.ReLU(True),
            )
            self.dec = nn.Sequential(
                # input is Z, going into a convolution
                nn.ConvTranspose2d(self.code_sz, sz, kernel_size, 1, 0),
                nn.ReLU(True),
                nn.ConvTranspose2d(sz, sz, kernel_size, 1, 0),
                nn.ReLU(True),
                nn.ConvTranspose2d(sz, ch, kernel_size, 1, 0),
            )
        elif net_type == 'vqvae':
            dim = sz
            self.code_sz = sz
            self.enc = nn.Sequential(
                nn.Conv2d(ch, dim, 4, 2, 1),
                nn.BatchNorm2d(dim),
                nn.ReLU(True),
                nn.Conv2d(dim, dim, 4, 2, 1),
                ResBlock(dim),
                ResBlock(dim),
            )

            self.dec = nn.Sequential(
                ResBlock(dim),
                ResBlock(dim),
                nn.ReLU(True),
                nn.ConvTranspose2d(dim, dim, 4, 2, 1),
                nn.BatchNorm2d(dim),
                nn.ReLU(True),
                nn.ConvTranspose2d(dim, ch, 4, 2, 1),
            )
        if out_act == 'sigmoid':
            self.dec = nn.Sequential(self.dec, nn.Sigmoid())
        elif out_act == 'tanh':
            self.dec = nn.Sequential(self.dec, nn.Tanh())
        elif out_act == 'none':
            pass
        else:
            raise ValueError('Unknown output activation function: {}'.format(out_act))
        self.out_act = out_act
        self.embedder = nn.Sequential(
            nn.Linear(self.latent_dim, self.latent_dim, bias=False),
            nn.BatchNorm1d(self.latent_dim)
        )
        self.sparse_attention = SparseAttnBottleneck(voc_size, self.latent_dim, num_topk=num_topk)

        self.projector = nn.Sequential(
            nn.Linear(self.latent_dim, proj_hidden_dim),
            nn.BatchNorm1d(proj_hidden_dim),
            nn.ReLU(),
            nn.Linear(proj_hidden_dim, self.proj_output_dim),
        )
        if net_type == 'wta':
            self.apply(weights_init_wta)
        elif net_type == 'vqvae':
            self.apply(weights_init_vqvae)
        self.net_type = net_type

    def bottleneck(self, latent):
        # latent: (batch, dim， 7，7)
        position_feature = self.bottleneck_conv(latent)  # (batch, num_components, 7, 7)
        b, c, h, w = position_feature.shape
        position_feature_max_pos = torch.argmax(position_feature.reshape(b, c, h * w),
                                                dim=1)  # (batch, num_components)

        feature_vector = self.bottleneck_fc(position_feature.reshape(b*c, -1))  # (batch * num_components, hidden_dim)
        emb = self.embedder(feature_vector)
        emb_after_attn = self.sparse_attention(emb)
        emb_proj = self.projector(emb_after_attn)  # (batch * num_components, hidden_dim)

        feature_map_out = torch.zeros_like(latent).reshape(b, -1, h * w).transpose(1, 2)  # (batch, 7*7, hidden_dim)

        # fill the feature map with the projected embedding according to position_feature_max_pos
        for i in range(c):
            feature_map_out[torch.arange(b), position_feature_max_pos[:, i], :] += emb_proj[i * b: (i + 1) * b, :]

        return feature_map_out.reshape(b, -1, h, w)

    def encode(self, x):
        h = self.enc(x.view(-1, self.ch, self.image_size, self.image_size))
        return h

    def decode(self, z):
        return self.dec(z)

    def forward(self, x, **kwargs):
        z = self.encode(x)
        latent = self.bottleneck(z)
        out = self.decode(latent)
        return out

from torchvision import datasets, transforms
import utils.data_utils

def get_data_config(dataset_name):
    if dataset_name in ['mnist', 'fashion_mnist', 'kuzushiji', 'google_draw']:
        transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Resize(28),
        ])
        config = {'image_size': 28, 'ch': 1, 'transform': transform, 'out_act': 'sigmoid'}
        return config
    elif dataset_name == 'omniglot':
        transform = transforms.Compose([
            transforms.ToTensor(),
            utils.data_utils.flip_image_value,
            transforms.Resize(28),
        ])
        config = {'image_size': 28, 'ch': 1, 'transform': transform, 'out_act': 'sigmoid'}
        return config
    elif dataset_name in ['cifar10', 'cifar100']:
        transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
        ])
        config = {'image_size': 32, 'ch': 3, 'transform': transform, 'out_act': 'tanh'}
        return config
    elif dataset_name in ['wikiart', 'celeba']:
        transform = transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
            transforms.Resize((64, 64)),  # TODO: decide image size
        ])
        config = {'image_size': 64, 'ch': 3, 'transform': transform, 'out_act': 'tanh'}
        return config
    elif dataset_name in ['mpi3d', 'dsprite']:
        transform = None
        config = {'image_size': 64, 'ch': 1, 'transform': transform, 'out_act': 'sigmoid'}
        return config
    elif dataset_name == 'bach':
        transform = None
        config = {'image_size': 48, 'ch': 1, 'transform': transform, 'out_act': 'sigmoid'}
        return config
    else:
        raise ValueError('Unknown dataset name: {}'.format(dataset_name))

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

def get_new_model(**kwargs):
    model = SparseAttnAutoencoderFeatureMap(**kwargs).to(device)
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    return model, optimizer


# -*- coding: utf-8 -*-

from DateTime import DateTime
from plone import api
from plone.namedfile import NamedBlobFile
from persistent.mapping import PersistentMapping
from Products.PloneMeeting.migrations.migrate_to_4200 import Migrate_To_4200 as PMMigrate_To_4200
from Products.PloneMeeting.migrations.migrate_to_4201 import Migrate_To_4201
from Products.PloneMeeting.migrations.migrate_to_4202 import Migrate_To_4202
from Products.PloneMeeting.migrations.migrate_to_4203 import Migrate_To_4203
from Products.PloneMeeting.migrations.migrate_to_4204 import Migrate_To_4204
from Products.PloneMeeting.migrations.migrate_to_4205 import Migrate_To_4205
from Products.PloneMeeting.migrations.migrate_to_4206 import Migrate_To_4206
from Products.PloneMeeting.migrations.migrate_to_4207 import Migrate_To_4207
from Products.PloneMeeting.migrations.migrate_to_4208 import Migrate_To_4208
from Products.PloneMeeting.migrations.migrate_to_4209 import Migrate_To_4209
from Products.PloneMeeting.migrations.migrate_to_4210 import Migrate_To_4210

import logging

logger = logging.getLogger('MeetingCommunes')

class Migrate_To_4200(PMMigrate_To_4200):

    def _fixUsedMeetingWFs(self):
        """meetingcommunes_workflow/meetingitemcommunes_workflows do not exist anymore,
           we use meeting_workflow/meetingitem_workflow."""
        logger.info("Adapting 'meetingWorkflow/meetingItemWorkflow' for every MeetingConfigs...")
        for cfg in self.tool.objectValues('MeetingConfig'):
            if cfg.getMeetingWorkflow() == 'meetingcommunes_workflow':
                cfg.setMeetingWorkflow('meeting_workflow')
            if cfg.getItemWorkflow() == 'meetingitemcommunes_workflow':
                cfg.setItemWorkflow('meetingitem_workflow')
        # delete old unused workflows, aka every workflows containing 'communes_workflow'
        wfTool = api.portal.get_tool('portal_workflow')
        wfs_to_delete = [wfId for wfId in wfTool.listWorkflows()
                         if 'communes_workflow' in wfId]
        if wfs_to_delete:
            wfTool.manage_delObjects(wfs_to_delete)
        logger.info('Done.')

    def _get_wh_key(self, itemOrMeeting):
        """Get workflow_history key to use, in case there are several keys, we take the one
           having the last event."""
        keys = itemOrMeeting.workflow_history.keys()
        if len(keys) == 1:
            return keys[0]
        else:
            lastEventDate = DateTime('1950/01/01')
            keyToUse = None
            for key in keys:
                if itemOrMeeting.workflow_history[key][-1]['time'] > lastEventDate:
                    lastEventDate = itemOrMeeting.workflow_history[key][-1]['time']
                    keyToUse = key
            return keyToUse

    def _adaptWFDataForItemsAndMeetings(self):
        """We use PM default WFs, no more meetingcommunes(item)_workflow...
           Adapt:
           - workflow_history for items and meetings;
           - takenOverByInfos for items."""
        logger.info('Updating WF history items and meetings to use new WF id...')
        wfTool = api.portal.get_tool('portal_workflow')
        catalog = api.portal.get_tool('portal_catalog')
        for cfg in self.tool.objectValues('MeetingConfig'):
            # this will call especially part where we duplicate WF and apply WFAdaptations
            cfg.registerPortalTypes()
            itemWFId = cfg.getItemWorkflow()
            for brain in catalog(portal_type=(cfg.getItemTypeName(), cfg.getMeetingTypeName())):
                itemOrMeeting = brain.getObject()
                itemOrMeetingWFId = wfTool.getWorkflowsFor(itemOrMeeting)[0].getId()
                if itemOrMeetingWFId not in itemOrMeeting.workflow_history:
                    wf_history_key = self._get_wh_key(itemOrMeeting)
                    itemOrMeeting.workflow_history[itemOrMeetingWFId] = \
                        tuple(itemOrMeeting.workflow_history[wf_history_key])
                    del itemOrMeeting.workflow_history[wf_history_key]
                    # do this so change is persisted
                    itemOrMeeting.workflow_history = itemOrMeeting.workflow_history
                else:
                    # already migrated
                    break
                if itemOrMeeting.__class__.__name__ == 'MeetingItem':
                    takenOverByInfos = itemOrMeeting.takenOverByInfos.copy()
                    newTakenOverByInfos = PersistentMapping()
                    for k, v in takenOverByInfos.items():
                        wf_name, state = k.split('__wfstate__')
                        newTakenOverByInfos['{0}__wfstate__{1}'.format(itemWFId, state)] = v
                    if sorted(newTakenOverByInfos.keys()) != sorted(takenOverByInfos.keys()):
                        itemOrMeeting.takenOverByInfos = newTakenOverByInfos
        logger.info('Done.')

    def _hook_before_meeting_to_dx(self):
        """Adapt WF related stored data if items and meetings before migrating to DX."""
        self._adaptWFDataForItemsAndMeetings()

    def _add_dashboard_pod_template_export_users_groups(self):
        """Add the export users and groups DashboardPODTemplate in the contacts directory."""
        logger.info("Adding 'Export users and groups' to 'contacts' directory...")
        pod_template_id = 'export-users-groups'
        contacts = self.portal.contacts
        if pod_template_id in contacts.objectIds():
            self._already_migrated()
            return

        profile_path = self.ps._getImportContext(self.profile_name)._profile_path
        odt_path = profile_path + '/../examples_fr/templates/users-groups-export.ods'
        odt_file = open(odt_path, 'rb')
        odt_binary = odt_file.read()
        odt_file.close()
        data = {'title': 'Export utilisateurs et groupes',
                'pod_formats': ['ods', 'xls'],
                'dashboard_collections': contacts.get('orgs-searches').all_orgs.UID(),
                'odt_file': NamedBlobFile(
                    data=odt_binary,
                    contentType='application/vnd.oasis.opendocument.text',
                    filename=u'users-groups-export.ods'),
                'use_objects': False,
                }
        pod_template = api.content.create(
            id=pod_template_id,
            type='DashboardPODTemplate',
            container=contacts,
            **data)
        pod_template.reindexObject()
        logger.info('Done.')

    def _mc_fixPODTemplatesInstructions(self):
        '''Make some replace in POD templates to fit changes in code...'''
        # for every POD templates
        replacements = {}
        # specific for Meeting POD Templates
        meeting_replacements = {}
        # specific for MeetingItem POD Templates
        item_replacements = {}

        self.updatePODTemplatesCode(replacements, meeting_replacements, item_replacements)

    def run(self,
            profile_name=u'profile-Products.MeetingCommunes:default',
            extra_omitted=[]):

        if self.is_in_part('a'):  # main step

            # change self.profile_name that is reinstalled at the beginning of the PM migration
            self.profile_name = profile_name

            # fix used WFs before reinstalling
            self._fixUsedMeetingWFs()

            # add a new DashboardPodTemplate in contacts directory
            self._add_dashboard_pod_template_export_users_groups()

            # fix some instructions in POD templates
            self._mc_fixPODTemplatesInstructions()

        # call steps from Products.PloneMeeting
        super(Migrate_To_4200, self).run(extra_omitted=extra_omitted)

        if self.is_in_part('c'):  # last step

            # execute upgrade steps in PM that were added after main upgrade to 4200
            Migrate_To_4201(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4202(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4203(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4204(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4205(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4206(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4207(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4208(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4209(self.portal).run(from_migration_to_4200=True)
            Migrate_To_4210(self.portal).run(from_migration_to_4200=True)

            # now MeetingCommunes specific steps
            logger.info('Migrating to MeetingCommunes 4200...')

            # add new searches (searchitemswithnofinanceadvice)
            self.addNewSearches()

# The migration function -------------------------------------------------------
def migrate(context):
    '''This migration function:

       1) Change MeetingConfig.meetingWorkflow to use meeting_workflow/meetingitem_workflow;
       2) Call PloneMeeting migration to 4200;
       3) Call every PloneMeeting 420x upgrade steps;
       4) In _after_reinstall hook, adapt items and meetings workflow_history
          to reflect new defined workflow done in 1);
       5) Add new searches.
    '''
    migrator = Migrate_To_4200(context)
    migrator.run()
    migrator.finish()


from sqlalchemy.orm import Session
from . import models,schemas
from datetime import  datetime as _dt

def add_in_stock(db:Session, stock_in: schemas.StockIn):

    new_stock = models.Stock(
        currency = stock_in.currency,
        date = _dt.strptime(stock_in.date,'%Y.%m.%d_%H:%M'),
        frame = stock_in.frame,
        open = stock_in.open,
        high = stock_in.high,
        low = stock_in.low,
        close = stock_in.close,
    )
    db.add(new_stock)
    db.commit()
    db.refresh(new_stock)
    return new_stock


# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url, include
from Project.SecureWitness import views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index'),
    url(r'^about/$', views.about, name='about'),
    url(r'^reports/([0-9]*)\/*$', views.reports, name='reports'),
    #url(r'^list/$', views.list, name='list'),                               #need to get rid of later
    url(r'^add_report/$', views.add_report, name='add_report'),
    url(r'^add_category/$', views.add_category, name='add_category'),
    url(r'^register/$', views.register, name='register'),
    url(r'^category/(?P<category_name_url>\w+)/$', views.category, name='category'),
    url(r'^login/$', views.user_login, name='login'),
    url(r'^restricted/$', views.restricted, name='restricted'),
    url(r'^logout/$', views.user_logout, name='logout'),
    url(r'^auth/$',views.auth, name='auth'),
    url(r'^messages/', include('postman.urls', namespace='postman', app_name='postman')),
    url(r'^search/', views.search, name='search'),
    url(r'^addFolder/$', views.add_folder, name='addFolder'),
    url(r'^disp_report/([0-9]+)/$', views.disp_report, name="disp_report"),
    url(r'^edit_report/([0-9]+)/$', views.edit_report, name="edit_report"),
    url(r'^groups/$', views.groups, name='groups'),
    url(r'^viewFolder/([a-zA-Z0-9" "]*)/$', views.view_folder, name="view_folder"),
    #url(r'^selectable/', include('selectable.urls')),
)


import pygame
from Data.settings import HEIGHT
ground = pygame.image.load("Assets/ground.png")

class platforms(pygame.sprite.Sprite):
    def __init__(self, pos, width):
        super().__init__()
        self.image = pygame.transform.scale(ground, (width, 10)).convert()
        self.rect = self.image.get_rect(center = pos)

    def update(self, scroll):
        self.rect.y += scroll
        if self.rect.top > HEIGHT:
            self.kill()


import torch
import torch.nn as nn
from torch.nn import init
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim

import torchvision
import torchvision.models as models
import torchvision.utils as vutils

import kornia
from collections import namedtuple

###########################################
##   Tools
#------------------------------------------

mean_std = namedtuple("mean_std", ['mean','std'])
vgg_outputs = namedtuple("VggOutputs", ['relu1_1', 'relu2_1', 'relu3_1', 'relu4_1'])
vgg_outputs_super = namedtuple("VggOutputs", ['map', 'relu1_1', 'relu2_1', 'relu3_1', 'relu4_1'])

###########################################
##   Layers and Blocks
#------------------------------------------

class InstanceNorm(nn.Module):
    def __init__(self, epsilon=1e-8):

        """ avoid in-place ops.
            https://discuss.pytorch.org/t/encounter-the-runtimeerror-one-of-the-variables-needed-for-gradient-computation-has-been-modified-by-an-inplace-operation/836/3 """

        super(InstanceNorm, self).__init__()
        self.epsilon = epsilon

    def forward(self, x):
        x   = x - torch.mean(x, (2, 3), True)
        tmp = torch.mul(x, x)
        tmp = torch.rsqrt(torch.mean(tmp, (2, 3), True) + self.epsilon)
        return x * tmp

class FilterPredictor(nn.Module):
    def __init__(self, vgg_channel=512, inner_channel=32):
        super(FilterPredictor, self).__init__()
        self.down_sample = nn.Sequential(nn.Conv2d(vgg_channel, inner_channel, kernel_size=3, padding=1))
        self.inner_channel = inner_channel
        self.FC = nn.Linear(inner_channel*2,inner_channel*inner_channel)

    def forward(self, content, style):
        content = self.down_sample(content)
        style = self.down_sample(style)

        content = torch.mean(content.view(content.size(0), content.size(1), -1), dim=2)
        style = torch.mean(style.view(style.size(0), style.size(1), -1), dim=2)

        filter = self.FC(torch.cat([content, style],1))
        filter = filter.view(-1, self.inner_channel, self.inner_channel).unsqueeze(3)
        return filter

class KernelFilter(nn.Module):
    def __init__(self, vgg_channel=512, inner_channel=32):
        super(KernelFilter, self).__init__()
        self.down_sample = nn.Sequential(
                        nn.Conv2d(vgg_channel, inner_channel, kernel_size=3, padding=1),
                    )

        self.upsample = nn.Sequential(
                        nn.Conv2d(inner_channel, vgg_channel, kernel_size=3, padding=1),
                    )

        self.F1 = FilterPredictor(vgg_channel, inner_channel)
        self.F2 = FilterPredictor(vgg_channel, inner_channel)

        self.relu = nn.LeakyReLU(0.2)

    def apply_filter(self, input_, filter_):
        ''' input_:  [B, inC, H, W]
            filter_: [B, inC, outC, 1] '''

        B = input_.shape[0]
        input_chunk = torch.chunk(input_, B, dim=0)
        filter_chunt = torch.chunk(filter_, B, dim=0)

        results = []

        for input, filter_ in zip(input_chunk, filter_chunt):
            input = F.conv2d(input, filter_.permute(1,2,0,3), groups=1)
            results.append(input)

        return torch.cat(results,0)

    def forward(self, content, style):
        content_ = self.down_sample(content)

        content_ = self.apply_filter(content_, self.F1(content, style))
        content_ = self.relu(content_)

        content_ = self.apply_filter(content_, self.F2(content, style))

        return content + self.upsample(content_)

class FilterPredictor_S(nn.Module):
    def __init__(self, vgg_channel=512, inner_channel=32):
        super(FilterPredictor_S, self).__init__()
        self.down_sample = nn.Sequential(
                        nn.Conv2d(vgg_channel, inner_channel, kernel_size=3, padding=1),
                    )
        self.inner_channel = inner_channel
        self.FC = nn.Linear(inner_channel,9*inner_channel*inner_channel)

    def forward(self, style):
        style = self.down_sample(style)
        style = torch.mean(style.view(style.size(0), style.size(1), -1), dim=2)

        filter = self.FC(style)
        filter = filter.view(-1, self.inner_channel, self.inner_channel, 3,3)
        return filter

class KernelFilter_S(nn.Module):
    def __init__(self, vgg_channel=512, inner_channel=32):
        super(KernelFilter_S, self).__init__()
        self.down_sample = nn.Sequential(
                        nn.Conv2d(vgg_channel, inner_channel, kernel_size=3, padding=1),
                    )

        self.upsample = nn.Sequential(
                        nn.Conv2d(inner_channel, vgg_channel, kernel_size=3, padding=1),
                    )

        self.F1 = FilterPredictor_S(vgg_channel, inner_channel)
        self.F2 = FilterPredictor_S(vgg_channel, inner_channel)

        self.relu = nn.LeakyReLU(0.2)

    def apply_filter(self, input_, filter_):
        ''' input_:  [B, inC, H, W]
            filter_: [B, inC, outC, 1] '''

        B = input_.shape[0]
        input_chunk = torch.chunk(input_, B, dim=0)
        filter_chunt = torch.chunk(filter_, B, dim=0)

        results = []

        for input, filter_ in zip(input_chunk, filter_chunt):
            input = F.conv2d(input, filter_.squeeze(0), groups=1, padding=1)
            results.append(input)

        return torch.cat(results,0)

    def forward(self, content, style):
        content_ = self.down_sample(content)

        content_ = self.apply_filter(content_, self.F1(style))
        content_ = self.relu(content_)

        content_ = self.apply_filter(content_, self.F2(style))

        return content + self.upsample(content_)

class ResidualBlock(nn.Module):
    def __init__(self, input_channel, output_channel, upsample=True):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(input_channel, output_channel, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(output_channel, output_channel, kernel_size=3, padding=1)
        self.conv_shortcut = nn.Conv2d(input_channel, output_channel, kernel_size=1, bias=False)
        self.relu = nn.LeakyReLU(0.2)
        self.norm = InstanceNorm()
        self.upsample = upsample

    def forward(self, x):
        if self.upsample:
            x = F.interpolate(x, mode='nearest', scale_factor=2)
        x_s = self.conv_shortcut(x)

        x = self.conv1(x)
        x = self.relu(x)
        x = self.norm(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.norm(x)

        return x_s + x

###########################################
##   Networks
#------------------------------------------

class Vgg19(nn.Module):
    def __init__(self, requires_grad=False):
        super(Vgg19, self).__init__()
        vgg_pretrained_features = models.vgg19(pretrained=True).features
        self.slice1 = nn.Sequential()
        self.slice2 = nn.Sequential()
        self.slice3 = nn.Sequential()
        self.slice4 = nn.Sequential()
        for x in range(2):
            self.slice1.add_module(str(x), vgg_pretrained_features[x])
        for x in range(2, 7):
            self.slice2.add_module(str(x), vgg_pretrained_features[x])
        for x in range(7, 12):
            self.slice3.add_module(str(x), vgg_pretrained_features[x])
        for x in range(12, 21):
            self.slice4.add_module(str(x), vgg_pretrained_features[x])
        if not requires_grad:
            for param in self.parameters():
                param.requires_grad = False

    def forward(self, X):
        h = self.slice1(X)
        h_relu1_1 = h
        h = self.slice2(h)
        h_relu2_1 = h
        h = self.slice3(h)
        h_relu3_1 = h
        h = self.slice4(h)
        h_relu4_1 = h
        out = vgg_outputs(h_relu1_1, h_relu2_1, h_relu3_1, h_relu4_1)
        return out

class Encoder(nn.Module):
    def __init__(self):
        super(Encoder, self).__init__()
        # VGG
        vgg_pretrained_features = models.vgg19(pretrained=True).features
        self.slice = nn.Sequential()
        for x in range(21):
            self.slice.add_module(str(x), vgg_pretrained_features[x])

    def forward(self, input_frame):
        return self.slice(input_frame)

class EncoderStyle(nn.Module):
    def __init__(self):
        super(EncoderStyle, self).__init__()
        # VGG
        vgg_pretrained_features = models.vgg19(pretrained=True).features

        self.slice1 = nn.Sequential()
        self.slice2 = nn.Sequential()
        self.slice3 = nn.Sequential()
        self.slice4 = nn.Sequential()

        for x in range(2):
            self.slice1.add_module(str(x), vgg_pretrained_features[x])
        for x in range(2, 7):
            self.slice2.add_module(str(x), vgg_pretrained_features[x])
        for x in range(7, 12):
            self.slice3.add_module(str(x), vgg_pretrained_features[x])
        for x in range(12, 21):
            self.slice4.add_module(str(x), vgg_pretrained_features[x])

    def cal_mean_std(self, feat, eps=1e-5):
        # eps is a small value added to the variance to avoid divide-by-zero.
        size = feat.size()
        N, C = size[:2]
        feat_var = feat.view(N, C, -1).var(dim=2) + eps
        feat_std = feat_var.sqrt().view(N, C, 1, 1)
        feat_mean = feat.view(N, C, -1).mean(dim=2).view(N, C, 1, 1)

        out = mean_std(feat_mean, feat_std)
        return out

    def forward(self, style):
        h = self.slice1(style)
        h_relu1_1 = self.cal_mean_std(h)

        h = self.slice2(h)
        h_relu2_1 = self.cal_mean_std(h)

        h = self.slice3(h)
        h_relu3_1 = self.cal_mean_std(h)

        h = self.slice4(h)
        h_relu4_1 = self.cal_mean_std(h)

        out = vgg_outputs_super(h, h_relu1_1, h_relu2_1, h_relu3_1, h_relu4_1)
        return out

class Decoder(nn.Module):
    def __init__(self, dynamic_filter=True, both_sty_con=True):
        super(Decoder, self).__init__()

        self.slice4 = ResidualBlock(512, 256)
        self.slice3 = ResidualBlock(256, 128)
        self.slice2 = ResidualBlock(128, 64)
        self.slice1 = nn.Conv2d(64, 3, kernel_size=3, padding=1)
        self.norm = InstanceNorm()

        self.dynamic_filter = dynamic_filter
        if dynamic_filter:
            if both_sty_con:
                self.Filter1 = KernelFilter()
                self.Filter2 = KernelFilter()
                self.Filter3 = KernelFilter()
            else:
                self.Filter1 = KernelFilter_S()
                self.Filter2 = KernelFilter_S()
                self.Filter3 = KernelFilter_S()

    def AdaIN(self, content_feat, style_feat):
        size = content_feat.size()

        style_mean = style_feat.mean
        style_std = style_feat.std

        normalized_feat = self.norm(content_feat)

        return normalized_feat * style_std.expand(size) + style_mean.expand(size)

    def AdaIN_filter(self, content_feat, style_feat, style_map):
        size = content_feat.size()

        style_mean = style_feat.mean
        style_std = style_feat.std

        normalized_content = self.norm(content_feat)
        normalized_style = (style_map - style_mean)/style_std

        results = self.Filter1(normalized_content, normalized_style)
        results = self.Filter2(results, normalized_style)
        results = self.Filter3(results, normalized_style)

        return results * style_std.expand(size) + style_mean.expand(size)

    def forward(self, x, style_features=None):
        if self.dynamic_filter:
            h = self.AdaIN_filter(x, style_features.relu4_1, style_features.map)
        else:
            h = self.AdaIN(x, style_features.relu4_1)

        h = self.slice4(h)

        h = self.AdaIN(h, style_features.relu3_1)
        h = self.slice3(h)

        h = self.AdaIN(h, style_features.relu2_1)
        h = self.slice2(h)

        h = self.AdaIN(h, style_features.relu1_1)
        h = self.slice1(h)

        return h

class TransformerNet(nn.Module):
    def __init__(self, dynamic_filter=True, both_sty_con=True):
        super(TransformerNet, self).__init__()

        # Sub-models
        self.Decoder = Decoder(dynamic_filter=dynamic_filter, both_sty_con=both_sty_con)
        self.Encoder = Encoder()
        self.EncoderStyle = EncoderStyle()
        self.Vgg19 = Vgg19()

        self.have_delete_vgg = False

    def generate_style_features(self, style):
        self.F_style = self.EncoderStyle(style)
        if not self.have_delete_vgg:
            del self.Vgg19
            self.have_delete_vgg = True

    def RGB2Gray(self, image):
        mean = image.new_tensor([0.485, 0.456, 0.406]).view(-1, 1, 1)
        std = image.new_tensor([0.229, 0.224, 0.225]).view(-1, 1, 1)

        image = (image*std+mean)

        gray = image[:,2:3,:,:]*0.299 + image[:,1:2,:,:]*0.587 + image[:,0:1,:,:]*0.114
        gray = gray.expand(image.size())

        gray = (gray-mean)/std

        return gray

    def forward(self, input_frame):
        F_content = self.Encoder(self.RGB2Gray(input_frame))
        return self.Decoder(F_content, self.F_style)


from __future__ import annotations

from typing import List

import numpy as np

from core.predictor import Predictor, PredictionResult
from utilities.interpolate import LinearlyInterpolatedFunction

class LinearRegressionPredictor(Predictor):

    def type(self) -> str:
        return "Constant Predictor"

    def predict(self, times: List[float], old_queues: List[np.ndarray]) -> PredictionResult:
        raise NotImplementedError()

    def predict_from_fcts(self, old_queues: List[LinearlyInterpolatedFunction], phi: float) -> PredictionResult:
        queues_minus_0 = np.array([max(0., queue(phi)) for queue in old_queues])
        queues_minus_1 = np.array([max(0., queue(phi - 1)) for queue in old_queues])
        queues_minus_2 = np.array([max(0., queue(phi - 2)) for queue in old_queues])
        queues_minus_3 = np.array([max(0., queue(phi - 3)) for queue in old_queues])
        queues_minus_4 = np.array([max(0., queue(phi - 4)) for queue in old_queues])

        m = len(old_queues)
        zeros = np.zeros(m)
        mask = (queues_minus_0 == 0) & (queues_minus_1 == 0) & (queues_minus_2 == 0) & (queues_minus_3 == 0) \
               & (queues_minus_4 == 0)
        result = PredictionResult(
            [phi, phi + 1, phi + 2, phi + 3, phi + 4, phi + 5, phi + 6, phi + 7, phi + 8, phi + 9, phi + 10],
            [
                np.where(mask, zeros, queues_minus_0),
                np.where(mask, zeros, queues_minus_0 + 2.68),
                np.where(mask, zeros, 1.01 * queues_minus_0 + 5.36),
                np.where(mask, zeros, 1.01 * queues_minus_0 + 8.47),
                np.where(mask, zeros, 1.02 * queues_minus_0 + 11.67),
                np.where(mask, zeros, 1.02 * queues_minus_4 + 14.67),
                np.where(mask, zeros, 1.02 * queues_minus_3 + 14.67),
                np.where(mask, zeros, 1.02 * queues_minus_2 + 14.67),
                np.where(mask, zeros, 1.02 * queues_minus_1 + 14.67),
                np.where(mask, zeros, 1.02 * queues_minus_0 + 14.67),
                np.where(mask, zeros, 1.02 * queues_minus_0 + 14.67)
            ]
        )

        return result


import numpy as np
import torch
from torch import nn
from torch.utils.data import DataLoader

from torch.autograd import Variable
from tqdm import tqdm

import matplotlib.pyplot as plt
# mpl.use('Agg')
import matplotlib

def showData(x, y, batchN, sen):
    plt.close()
    plt.plot(x.detach().cpu()[:, batchN, sen])
    plt.plot(y.detach().cpu()[:, batchN, sen])
    plt.show()
    # plt.savefig("mygraph.png")

def showData2(x, y, batchN, sen):
    plt.close()
    plt.plot(x.detach().cpu()[batchN, sen, :])
    plt.plot(y.detach().cpu()[batchN, sen, :])
    plt.show()

class SpeedNETUtilsClass:

    @staticmethod
    def showData(x, y, batchN, sen):
        plt.close()
        plt.plot(x.detach().cpu()[batchN, sen, :])
        plt.plot(y.detach().cpu()[batchN, sen, :])
        plt.show()

    @staticmethod
    def showDataTest(xi, y, batchN, sen):
        a=matplotlib.get_backend()

        x = np.linspace(0, 6.28, 100)

        plt.plot(x, x ** xi, label='square root')
        plt.plot(x, np.sin(x), label='sinc')

        plt.xlabel('x label')
        plt.ylabel('y label')

        plt.title("test plot")

        plt.legend()

        plt.show()

    def filter_step(self, SpeedNetTeacher, SpeedNetStudent, samples, labels, trainW, trainH, optimizer, loss1, train=True):

        samples = samples.transpose(0, 1)
        # labelsV = Variable(labels.type(SpeedNetStudent.dtype), requires_grad=True)
        weightV = Variable(trainW.type(SpeedNetStudent.dtype), requires_grad=True)
        heightV = Variable(trainH.type(SpeedNetStudent.dtype), requires_grad=True)

        if train:
            optimizer.zero_grad()
        # Forward + Backward + Optimize
        samplesV = Variable(samples.type(SpeedNetStudent.dtype), requires_grad=True)

        y, x_decod, latentT, kl_loss, aten = SpeedNetTeacher(samplesV, weightV, heightV)

        y, x_decod, latentS, kl_loss, aten = SpeedNetStudent(samplesV, weightV, heightV)

        loss = loss1(latentS, latentT)

        recon_loss = loss1(samplesV, x_decod)

        loss1res = self.lossMultip * loss

        if train:
            loss1res.backward()
            optimizer.step()

        return loss, recon_loss

    def studenTeacherReduction(self, SpeedNetTeacher, SpeedNetStudent, train_dataset, learning_rate, num_epochs):

        self.lossMultip=1
        # trainable_params = list(filter(
        #     lambda p: p.requires_grad, SpeedNetStudent.parameters()))
        # trainable_params = list(self.fc1.parameters())+list(self.fc2.parameters())+list(self.fc3.parameters())\
        #                       +list(self.fc3_2.parameters())+list(self.fc4.parameters())+list(self.ensamble2.parameters())\
        #                       +list(self.ensamble1_weight.parameters())+list(self.ensamble1_height.parameters())
        trainable_params = list(SpeedNetStudent.encoder.parameters()) + list(SpeedNetStudent.lmbd.parameters())

        optimizer = torch.optim.Adam(trainable_params, lr=learning_rate)

        train_data_loader = DataLoader(dataset=train_dataset, batch_size=SpeedNetStudent.batch_size, shuffle=True,
                                       drop_last=True)

        SpeedNetTeacher.to(SpeedNetTeacher.device)
        SpeedNetStudent.to(SpeedNetStudent.device)

        MAE = nn.L1Loss().to(SpeedNetStudent.device)
        MSE = nn.MSELoss().to(SpeedNetStudent.device)

        self.epoch_stop = 0

        for epoch in range(num_epochs):
            print('Epoch:', epoch)

            SpeedNetStudent.train()
            SpeedNetTeacher.train()

            steps = 0
            loss_x = 0
            loss_rec=0

            torch.cuda.empty_cache()
            # training
            for (samples, labels, weight, height) in tqdm(train_data_loader):
                steps += 1

                loss, loss_r = self.filter_step(SpeedNetTeacher, SpeedNetStudent, samples, labels, weight, height, optimizer, MSE)
                loss_x += loss.item()
                loss_rec += loss_r.item()

            print("Training loss:", loss_x / steps)
            print("Training loss rec:", loss_rec / steps)
            SpeedNetStudent.logger.log_value('{} MSE'.format("train"), loss_x / steps)
            SpeedNetStudent.logger.log_value('{} VRAE'.format("train"), loss_rec / steps)

    def analyzeErrorDistribution(self, SpeedNET, test_dataset, divide):
        test_data_loader = DataLoader(dataset=test_dataset, batch_size=SpeedNET.batch_size, shuffle=True, drop_last=True)

        maxSp = max(test_dataset.tensors[1].numpy().squeeze())
        stepSp = maxSp / (divide - 1)

        SpeedNET.to(SpeedNET.device)
        steps = 0

        data = np.empty(divide, dtype=np.object)
        for i in range(data.shape[0]):
            data[i] = []

        mae=[]

        for i, (samples, labels, weight, height) in enumerate(test_data_loader):
            with torch.no_grad():
                samples = samples.transpose(1, 2)
                labelsV = Variable(labels.type(SpeedNET.dtype), requires_grad=False)
                weightV = Variable(weight.type(SpeedNET.dtype), requires_grad=False)
                heightV = Variable(height.type(SpeedNET.dtype), requires_grad=False)
                samplesV = Variable(samples.type(SpeedNET.dtype), requires_grad=False)

                # heightV = Variable(torch.tensor(np.arange(16)*2 + 70).type(self.dtype), requires_grad=False).view(16,1)
                #weightV = Variable(torch.tensor(np.arange(16) * 2 + 60).type(SpeedNET.dtype), requires_grad=False).view(16, 1)

                y = SpeedNET(samplesV, weightV, heightV)

                err = labelsV - y

                indexes = (labels.numpy().squeeze() / stepSp)
                err = err.detach().cpu().numpy()
                mae.append(np.absolute(err))

                for i in range(SpeedNET.batch_size):
                    ind = int(indexes[i])
                    if ind < 0:
                        ind = 0
                    if ind > divide:
                        ind = divide
                    data[ind].append(err[i][0])
                steps += 1

        x = np.linspace(0, maxSp, divide)

        return data, x, np.mean(mae)


from django.contrib import admin

# Register your models here.
from . import models

@admin.register(models.ListModel)
class ListModel(admin.ModelAdmin):

    list_display =('ip','method','t_code','create_time')
    # 默认不配置的话，第一个字段会存在链接到记录编辑页面
    # list_display_links = None
    list_display_links = ('ip','method','t_code','create_time')
    # 每页显示条目数 缺省值100
    list_per_page = 10
    readonly_fields = ('id',)
    # 设置过滤选项
    list_filter = ('id', 'ip', 'method', 't_code','create_time')
    # 按发布日期降序排序
    ordering = ('-create_time',)
    # 搜索条件设置
    search_fields = ('ip','method','create_time')


from ast import arg
from asyncio.log import logger
import imp
from traceback import print_tb
import torch
import torch.nn as nn
import torch.nn.functional as F
from parse import parse_args
from pprint import pformat
from thop import profile

import matplotlib.pyplot as plt
import random
from data_loader.build import build_loader
from utils import *
from models import MLP, GNN
import warnings
import numpy as np
import torch.optim as optim
from time import time
warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning)
warnings.filterwarnings("ignore", category=UserWarning)
from sklearn.metrics import recall_score,precision_score

args_config = parse_args()
# logger = getLogger(*args_config)
csvwriter = CSVWriter(args_config.csv_path)

yy_train_loss = []
yy_val_loss = []
yy_train_acc = []
yy_val_acc = []
yy_test_loss=[]
yy_test_acc = []

xx = []

def train(epoch, gnn, gnn_optimizer, train_loader):
    total, correct, train_acc = 0, 0, 0
    losses = AverageMeter()

    gnn.train()

    progressbar = tqdm(enumerate(train_loader), desc=f'epoch:[{epoch}]', total=len(train_loader))
    for batch_idx, batch in progressbar:
            gnn_optimizer.zero_grad()

            adj, mask, emb, labels = batch

            outputs = gnn(emb.cuda(), adj.cuda(), mask.cuda())
            #Calculate
            # flops, params = profile(gnn, inputs=(emb.cuda(), adj.cuda(), mask.cuda() ))
            # print('FLOPs = ' + str(flops/1000**3) + 'G')
            # print('Params = ' + str(params/1000**2) + 'M')
            bce_loss_batch = gnn.bce_loss(outputs, labels.float().cuda())
            l2_loss_batch = gnn._l2_loss()
            loss_batch = (l2_loss_batch + bce_loss_batch)
            losses.update(loss_batch.item())

            gnn_optimizer.zero_grad()
            loss_batch.backward()
            gnn_optimizer.step()

            preds = gnn.predict(outputs)
            preds = torch.argmax(preds, 1)

            correct += preds.eq(torch.argmax(labels.cuda(), 1)).sum().item()
            total += len(labels)
            train_acc = correct*1.0 / total

            progressbar.set_postfix({
            'batch_loss': "{:.4f}".format(loss_batch.item()),
            'avg_loss': "{:.4f}".format(losses.avg),
            'avg_acc': "{:.4f}".format(train_acc),
        })
    return losses.avg, train_acc

def test(epoch, gnn, test_loader):
    total, correct, acc = 0, 0, 0
    losses = AverageMeter()
    progressbar = tqdm(enumerate(test_loader), desc='test', total=len(test_loader))
    with torch.no_grad():
        for _,batch in progressbar:
            adj, mask, emb, labels = batch
            outputs = gnn(emb.cuda(), adj.cuda(), mask.cuda())
            bce_loss_batch = gnn.bce_loss(outputs, labels.float().cuda())
            l2_loss_batch = gnn._l2_loss()
            loss_batch = (l2_loss_batch + bce_loss_batch)
            losses.update(loss_batch.item())

            preds = gnn.predict(outputs)
            preds = torch.argmax(preds, 1)

            correct += preds.eq(torch.argmax(labels.cuda(), 1)).sum().item()
            total += len(labels)
            test_acc = correct*1.0 / total
            # print(labels,preds)
            recall=recall_score(torch.argmax(labels.cuda(), 1).cpu().numpy(),preds.detach().cpu().numpy(),zero_division=1)
            precision=precision_score(torch.argmax(labels.cuda(), 1).cpu().numpy(),preds.detach().cpu().numpy(),zero_division=1)

            progressbar.set_postfix({
            'batch_loss': "{:.4f}".format(loss_batch.item()),
            'avg_loss': "{:.4f}".format(losses.avg),
            'avg_acc': "{:.4f}".format(test_acc),
            'recall': "{:.4f}".format(recall),
            'precision': "{:.4f}".format(precision)
        })
    return losses.avg, test_acc ,recall,precision

def main():
    SEED = 77

    random.seed(SEED)
    torch.manual_seed(SEED)
    allrecall = []
    allprecision=[]

    """initialize args and dataset"""

    """set gpu id"""
    args_config.gpu = args_config.gpu and torch.cuda.is_available()
    device = torch.device("cuda" if args_config.gpu else "cpu")

    if args_config.gpu:
        print('Using GPU')
        torch.cuda.manual_seed(SEED)
        torch.cuda.set_device(args_config.gpu_id)

    train_adj, train_embed, train_y, val_adj, val_embed, val_y, test_adj, test_embed, test_y = load_data(args_config.dataset)

    # preprocessing
    train_adj, train_mask = preprocess_adj(train_adj)
    train_embed = preprocess_features(train_embed)
    val_adj, val_mask = preprocess_adj(val_adj)
    val_embed = preprocess_features(val_embed)
    test_adj, test_mask = preprocess_adj(test_adj)
    test_embed = preprocess_features(test_embed)
    print("Data Loaded")

    print("Build Data Loader")
    train_loader, test_loader, val_loader = build_loader(args_config=args_config, train_adj=train_adj, train_mask=train_mask, train_emb=train_embed, train_y=train_y,
                                             test_adj=test_adj, test_mask=test_mask, test_emb=test_embed, test_y=test_y,
                                             val_adj=val_adj, val_mask=val_mask, val_emb=val_embed, val_y=val_y)

    gnn = GNN(args_config.input_dim, train_y.shape[1]).cuda()
    gnn_optimizer = torch.optim.Adam(gnn.parameters(), args_config.learning_rate)
    lr_scheduler = optim.lr_scheduler.StepLR(gnn_optimizer, step_size=40, gamma=0.2)

    total_params = sum(p.numel() for p in gnn.parameters() if p.requires_grad)
    logger.info(f"model total trainable parameters: {total_params:,}")
    logger.info(f"current experiment parameters are:")
    logger.info(pformat(args_config)+"\n")
    best_acc = 0.0
    best_acc1 = 0.0
    best_epoch = 0
    brep=0
    logger.info('==>Start training...')

    for epoch in range(0, args_config.epochs):
        xx.append(epoch)
        lr_list = [param_groups['lr'] for param_groups in gnn_optimizer.param_groups]
        logger.info(f"current LR: {lr_list}")

        train_loss, train_acc = train(epoch, gnn, gnn_optimizer, train_loader)
        val_loss, val_acc ,_,_= test(epoch, gnn, val_loader)
        test_loss, test_acc,test_rec,test_pre = test(epoch, gnn, test_loader)

        lr_scheduler.step()
        allrecall.append(test_rec)
        allprecision.append(test_pre)
        kwargs = {'epoch':epoch,'train_loss': train_loss, 'train_acc': train_acc, 'test_loss':test_loss, 'test_acc':test_acc}

        is_best = test_acc > best_acc
        if is_best:
            best_acc = test_acc
            best_epoch = epoch

        logger.info(f"Epoch: {epoch} - train_loss: {train_loss:.4f} - train_acc: {train_acc:.4f} - test_loss:{test_loss:.4f} - test_acc: {val_acc:.4f}, Best acc is {best_acc:.4f} achieved at epoch {best_epoch}\n")
        csvwriter.write(**kwargs)
        if val_acc > best_acc1:
            best_acc1 = val_acc
            es = 0
            torch.save(gnn.state_dict(), 'model_weight.pt')
        else:
            es += 1
            print("Counter {} of 20".format(es))

            if es > args_config.early_stopping:
                print("Early stopping with best_acc: ", best_acc, "and val_acc for this epoch: ", val_acc, "...")
                brep=epoch
                break
        yy_train_loss.append(train_loss)
        yy_train_acc.append(train_acc)

        yy_val_loss.append(val_loss)
        yy_val_acc.append(val_acc)

        yy_test_loss.append(test_loss)
        yy_test_acc.append(test_acc)

    logger.info("==> Training finished!!!")
    a=0
    b=0
    for i in range(len(allrecall)):
        a = a + allrecall[i]
        b = b + allprecision[i]
    a = a / len(allrecall)
    b = b / len(allrecall)
    # print(allrecall)
    print('precision is {:.4f}'.format(b))
    print('recall is {:.4f}'.format(a))
    print('F-SCORE is {:.4f}'.format((2*a*b)/(a+b)))
    if len(allrecall)!=args_config.epochs:
        xx.pop()
    plt.plot(xx, yy_train_loss, "r", label="train_loss")

    plt.legend()
    plt.savefig("loss.png")
    plt.close()
    plt.plot(xx, yy_train_acc, "y", label="train_acc")
    plt.plot(xx, yy_val_acc, "g", label="valid_acc")
    plt.plot(xx, yy_test_acc, "b", label="test_acc")
    plt.legend()
    plt.savefig("acc.png")
    plt.close()

if __name__ == '__main__':
    main()


import random
from itertools import combinations
import math
import matplotlib.pyplot as plt
from tkinter import *
from functools import partial

import os
from shutil import rmtree

class Individuo():
    def __init__(self, X,Y,bino,dec):
        self.X = X
        self.Y = Y
        self.bino = bino
        self.dec = dec

    def __repr__(self):
        rep = 'Individuo(' + str(self.X) +' | '+ str(self.Y) + ' | ' + str(self.bino) +' | ' + str(self.dec) +')'
        return rep

class AlgoritmoGenetico():

    def __init__(self, Xmin,Xmax, intervalo, rango, puntos, poblacionMaxima, poblacionInicial, generaciones,noBits, Pmi,Pmg, Pc):
        self.Xmin = Xmin
        self.intervalo = intervalo
        self.Xmax = Xmax
        self.rango = rango
        self.puntos = puntos
        self.poblacionInicial = poblacionInicial
        self.poblacionMaxima = poblacionMaxima
        self.generaciones = generaciones
        self.noBits = noBits
        self.individuos = []
        self.probabilidadDeCruce = Pc
        self.Pmi = Pmi
        self.Pmg = Pmg

    def generarPoblacionInicial(self):
        individuos = []
        for i in range(0,self.poblacionInicial):
            binary = 0
            decimal = random.uniform(0,self.rango)
            binary = bin(int(decimal))
            binary = binary.removeprefix("0b")
            if(len(binary) < self.noBits):
                binary = binary.zfill(self.noBits)
            x = self.Xmin + (decimal * self.intervalo)
            self.individuos.append(Individuo(x,0,binary,decimal))
        individuos = self.individuos
        return individuos

    def posiblesParejas(self):
        parejas = []
        temp = combinations(self.individuos,2)
        for i in list(temp):
            parejas.append(i)
        return parejas

    def posiblesParejasX(self):
        listCombinaciones = []
        padres = []
        for i in range(len(self.individuos)):
            individuosSeleccionados = random.randint(2, len(self.individuos))
            candidatos = random.sample(self.individuos, individuosSeleccionados)
            ganador = max(candidatos, key = lambda individuo: individuo.Y)
            padres.append(ganador)
        temp = combinations(padres,2)
        for i in list(temp):
            listCombinaciones.append(i)
        return listCombinaciones

    def cruzaX(self, listParejas):
            listNewgen = []
            if(len(listParejas) > 1):
                for i in range(len(listParejas)):
                    for k in range(0,1):
                        MascaraDeCruce = ''
                        child1 = ''
                        for y in range(self.noBits):
                            MascaraDeCruce = MascaraDeCruce + "" + str(random.randint(0,1))
                        var = listParejas[i]
                        if(k == 0):
                            gen1 = var[0].bino
                            gen2 = var[1].bino
                        else:
                            gen1 = var[1].bino
                            gen2 = var[0].bino
                        for j in range(len(gen1)):
                            if MascaraDeCruce[j] == '1':
                                child1 = child1 + gen1[j]
                            else:
                                child1 = child1 + gen2[j]
                        listNewgen.append(child1)
            return listNewgen

    def mutacion(self, listCruza):

        listNewInd = []
        listNewBit = []
        listNewBit2 = []
        listAnalizador= []
        listNoCruza= []
        listNew = []
        listGenMutado= []
        for i in range(len(listCruza)):
            var = listCruza[i]
            pmir=float("{:.4f}".format(random.uniform(0.0,1.0)))
            if pmir < self.Pmi:
                listNewInd.append(listCruza[i])
            if pmir > self.Pmi:
                listGenMutado.append(listCruza[i])
        for i in range(len(listNewInd)):
                var =listNewInd[i]
                for y in range(len(var)):
                    listNew.append(var[y])
                for y in range(len(var)):
                    listAnalizador.append(var[y])
                    pmgr= random.randint(0,len(var)-1)
                    listNew[pmgr]
                    lis2=listNew[pmgr]
                    listNewBit.append(lis2)
                # print(listNewBit)
                bit2 = ''.join(listNewBit)
                listGenMutado.append(bit2)
                listAnalizador.clear()
                listNewBit.clear()
                listNew.clear()
        return listGenMutado

    def ConvertirAObjetos(self,mutados):
        for i in range(0,len(mutados)):
            mutadoBinario = mutados[i]
            posicion = 0
            decimal = 0
            binario = mutadoBinario[::-1]
            for digito in binario:
                # Elevar 2 a la posición actual
                multiplicador = 2**posicion
                decimal += float(digito) * multiplicador
                posicion += 1
            x = self.Xmin + (decimal * self.intervalo)
            self.individuos.append(Individuo(x,0,mutados[i],decimal))

    def limpiar(self):
        auxIndividuos = []
        # i = 0
        for i in range(len(self.individuos)):
            if(self.individuos[i].X <= self.Xmax):
                auxIndividuos.append(self.individuos[i])
        self.individuos.clear()
        self.individuos = auxIndividuos
        # for elemento in self.individuos:
        #     if(elemento.X > self.Xmax):
        #         self.individuos.pop(i)
            # i += 1

    def fx(self):
        dataAux =  []
        for i in range(len(self.individuos)):
            x = self.individuos[i].X
            op = float("{:.4f}".format(math.sin(math.radians(x))))
            op2 = float("{:.4f}".format(math.sqrt(2*(pow(x,2))-x-2)))
            if(op2 > 0):
                resultado = float("{:.4f}".format(op * op2))
                self.individuos[i].Y = resultado
                dataAux.append(self.individuos[i])
        self.individuos.clear()
        self.individuos = dataAux
        # for elemento in self.individuos:

        # print(len(self.individuos))

    def poda(self):
        uniqueData = []
        if(len(self.individuos) > self.poblacionMaxima):
            while(len(self.individuos) > self.poblacionMaxima):
                posicion = random.randint(0, self.poblacionMaxima)
                self.individuos.pop(posicion)
        for individuo in self.individuos:
            if not any(I.bino == individuo.bino for I in uniqueData):
                uniqueData.append(individuo)
        self.individuos.clear()
        self.individuos = uniqueData

def graficasXiteracion(self,x, fx):
    try:
        rmtree("assets\GraficaHistorial\img")
    except:
        pass
    plt.plot(x,fx, label="Mejores individuo", color="green", linestyle="-",marker ='.')
    plt.legend()
    os.makedirs("assets\GraficaHistorial\img", exist_ok=True)
    plt.savefig("assets\GraficaHistorial\img\GraficaGeneral.png")
    plt.close()

def graficasXgeneracion(self, listNuevaPAptitud,listNuevaFx,index):
    print('Entro a graficas x generacion')
    plt.title("Generacion: "+str(index))
    plt.plot(listNuevaPAptitud,listNuevaFx, label="Generacion "+str(index), color="blue", linestyle="none",marker ='.')
    plt.legend()
    os.makedirs("assets\GraficasIndividuales\img", exist_ok=True)
    plt.savefig("assets\GraficasIndividuales\img\GraficaIndividual "+str(index)+".png")
    plt.close()

if __name__ == "__main__":
    def envioDatos(generaciones, poblacionM, rangoMax, rangoMin, intervalo, mutacionInd, mutacionGen, probCruza):
        print("generaciones :", generaciones.get())
        print("poblacionM :", poblacionM.get())
        print("rangoMaximo :", rangoMax.get())
        print("rangoMinimo :", rangoMin.get())
        print("Intervalo :", intervalo.get())
        print("Mutacion individuo :", mutacionInd.get())
        print("Mutacion gen :", mutacionGen.get())
        print("Probabilida de cruza: ", probCruza.get())

        return
    #window
    tkWindow = Tk()
    tkWindow.geometry('400x250')
    tkWindow.title('Obtencion de datos')
    #----
    usernameLabel = Label(tkWindow, text="Generaciones").grid(row=0, column=0)
    generaciones = IntVar()
    generacionesEntry = Entry(tkWindow, textvariable=generaciones).grid(row=0, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Poblacion Maxima").grid(row=1, column=0)
    poblacionM = DoubleVar()
    poblacionMEntry = Entry(tkWindow, textvariable=poblacionM).grid(row=1, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Rango maximo").grid(row=2, column=0)
    rangoMaximo = DoubleVar()
    rangoMaximoEntry = Entry(tkWindow, textvariable=rangoMaximo).grid(row=2, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Rango minimo").grid(row=3, column=0)
    rangoMinimo = DoubleVar()
    rangoMinimoEntry = Entry(tkWindow, textvariable=rangoMinimo).grid(row=3, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Intervalo").grid(row=4, column=0)
    intervalo = DoubleVar()
    intervaloEntry = Entry(tkWindow, textvariable=intervalo).grid(row=4, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Mutacion del individuo").grid(row=5, column=0)
    mutacionIndividuo = DoubleVar()
    mutacionIndividuoEntry = Entry(tkWindow, textvariable=mutacionIndividuo).grid(row=5, column=1)
    #-----
    poblacionMLabel = Label(tkWindow,text="Mutacion del gen").grid(row=6, column=0)
    mutacionGen = DoubleVar()
    mutacionGenEntry = Entry(tkWindow, textvariable=mutacionGen).grid(row=6, column=1)

    probabilidadCruzaLabel = Label(tkWindow,text="Probabilidad de cruza").grid(row=7, column=0)
    probCruza = DoubleVar()
    probCruzaEntry = Entry(tkWindow, textvariable=probCruza).grid(row=7, column=1)

    envioDatos = partial(envioDatos, generaciones, poblacionM, rangoMaximo, rangoMinimo, intervalo, mutacionIndividuo, mutacionGen, probCruza)
    #login button
    btnEnviar = Button(tkWindow, text="Enviar", command=envioDatos).grid(row=8, column=0)

    tkWindow.mainloop()

    print("Generaciones: ")
    generaciones = generaciones.get()
    print("Población maxima")
    poblacionMaxima = poblacionM.get()
    poblacionInicial = random.randint(2,poblacionMaxima)
    print("Rango Maximo")
    xMaximo = rangoMaximo.get()
    print("Rango Minimo")
    xMinimo = rangoMinimo.get()
    print("Intervalo")
    intervalo = intervalo.get()
    rango = xMaximo-xMinimo
    puntos = (rango/intervalo)+1
    bit= bin(int(puntos))
    bit=bit.removeprefix("0b")
    noBits= len(bit)
    print(noBits)
    print("Ingrese la probabilidad de mutación del inidividuo")
    Pmi= mutacionIndividuo.get()
    print("Ingrese la probabilidad de mutacion de gen")
    Pmg= mutacionGen.get()
    print("Ingrese la probabilidad de cruza")
    Pc= probCruza.get()

    ag = AlgoritmoGenetico(xMinimo, xMaximo, intervalo, rango, puntos, poblacionMaxima, poblacionInicial, generaciones, noBits, Pmi,Pmg, Pc)

    ag.generarPoblacionInicial()

    MejoresIndividuos = []
    MediaIndividuos = []
    PeoresIndividuos = []

    for i in range(0,generaciones):
        parejas = ag.posiblesParejasX()
        hijos = ag.cruzaX(parejas)
        mutados = ag.mutacion(hijos)
        ag.ConvertirAObjetos(mutados)
        ag.fx()
        # print("_______________________________ya2_________________")
        ag.limpiar()
        ag.poda()
        MejoresAptitudes = [individuo.Y for individuo in ag.individuos]
        MejoresIndividuos.append(max(MejoresAptitudes))
        aptitudes = [individuo.Y for individuo in ag.individuos]
        promedio = sum(aptitudes)/len(aptitudes)
        MediaIndividuos.append(promedio)
        peoresAptitudes = [individuo.Y for individuo in ag.individuos]
        PeoresIndividuos.append(min(peoresAptitudes))
        print(i)
    # print("_____MEJORES_____\n" , MejoresIndividuos)
    # print("______PROMEDIO_____\n", MediaIndividuos)
    # print("_____PEOR_____\n", PeoresIndividuos)

        try:
            os.rmdir("assets")
        except:
            pass
        os.makedirs("assets\Video", exist_ok=True)

        plt.plot(MejoresIndividuos, label="Mejor individuo", color="red", linestyle="-",)
        plt.plot(MediaIndividuos, label="Promedio", color="blue", linestyle="-",)
        plt.plot(PeoresIndividuos, label="Peor individuo", color="green", linestyle="-")
        plt.legend()
        os.makedirs("assets\Grafica", exist_ok=True)
        plt.savefig("assets\Grafica\GraficaHistorial.png")
        plt.close()

        TodaslasX = [individuo.X for individuo in ag.individuos]
        print(TodaslasX)
        TodaslasY = [individuo.Y for individuo in ag.individuos]
        print(TodaslasY)
        fig, ax = plt.subplots()
        ax.scatter(x = TodaslasX, y = TodaslasY)
        os.makedirs("assets\GraficaxGeneracion", exist_ok=True)
        plt.savefig('assets\GraficaxGeneracion\GraficaHistorial'+str(i)+'.png')
        plt.close()


import numpy as np
from skimage.feature import greycomatrix, greycoprops, hog
from sklearn.decomposition import PCA

def glcm_F(image, angles, distances, prop, d, features, symmetric=True, normed=True):
    n_features = len(angles) * len(distances)

    glcm = greycomatrix(image, distances=distances, angles=angles, levels=int(256/d), symmetric=symmetric, normed=normed)

    for idx, p in enumerate(prop):  # obtenim features de la matriu GLCM
        f = greycoprops(glcm, prop=p)[0, 0]
        features[(idx * n_features): (idx + 1) * n_features] = f.flatten()

def HOG(image, px, orientations=9, block_norm="L2"):
    features, img = hog(image, orientations=orientations, pixels_per_cell=(px, px),
                        cells_per_block=(1, 1), transform_sqrt=False,
                        block_norm=block_norm, feature_vector=True, visualise=True)

    return features, img

def pca_F(train_set, test_set, percent=0.95):
    pca = PCA(n_components=train_set.shape[1])

    pca.fit(train_set)

    cumsum = np.cumsum(pca.explained_variance_ratio_)
    r = np.where(cumsum > percent)
    pca = PCA(n_components=r[0][0])

    x_train = pca.fit_transform(train_set)
    x_test = pca.transform(test_set)

    print("Train", x_train.shape[0], "Test", x_test.shape[0])

    return x_train, x_test


import argparse
import csv
import matplotlib.pyplot as plt

THRESHOLD = 50

def next_display_threshold(elevation):
    fifties = int(elevation) // -THRESHOLD
    return -THRESHOLD * (fifties + 1)

def main(inputfile):
    with open(inputfile) as csvfile:
        reader = csv.DictReader(csvfile)
        data = [(float(row["distance"]), -float(row["depth"])) for row in reader]
    x, y = zip(*data)
    bottom = next_display_threshold(min(y))

    _, ax = plt.subplots()
    ax.plot(x, y, color="black")
    ax.fill_between(x, y, bottom, color="grey")

    # bottom is negative, steps must be positive
    # +1 so that the last value is included
    steps = -bottom // THRESHOLD + 1
    ax.set_yticks(
        # labels must go negative
        [n * THRESHOLD * -1 for n in range(steps)],
        [n * THRESHOLD for n in range(steps)]
    )

    ax.set_xlabel("Distance (km)")
    ax.set_xlim([x[0], x[-1]])
    ax.set_ylabel("Depth (m)")
    ax.set_ylim([bottom, 0])

    plt.savefig(inputfile.replace(".csv", ".png"))

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Produce section chart")
    parser.add_argument("file", type=str, help="the file contianing section information")
    args = parser.parse_args()
    main(args.file)