import os
import sys
import re
import glob
import time
import logging
import subprocess
import numpy as np
from itertools import groupby
from collections import namedtuple
from nest.parsers.fastq import Fastq

class Identifier:
    """ The Identifier class contains a set of modules that recognize the
    type of fastq file from the fastq the fastq header. The class is initialzed
    by passing a record to the constructor"""

    def __init__(self, record):
        """Initialize the Identifier class from a fastq record"""
        self.rec = record

    def isIlluminaOld(self):
        """Identify fastq headers in the following format
         @HWUSI-EAS100R:6:73:941:1973#0/1"""
        header_regex = re.compile('@\w+-?\w+:\d+:\d+:\d+:\d+#\d*')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isIlluminaNew(self):
        """Idetify fastq headers in the following format
        #@D00468:24:H8ELMADXX:1:1101:1470:2237 1:N:0:2"""
        header_regex = re.compile('@\w+-?\w+:\d+:\w+-?\w+:\d+:\d+:\d+:\d+\s\d:\w+:\w+:\w*')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isSraOld(self):
        """Identify fastq headers in the following format
        #@SRR037455.1 HWI-E4_6_30ACL:4:1:0:29 length=35
        #@SRR902931.1 HWI-ST1384:61:D1DJ4ACXX:8:1101:1240:2015 length=50"""
        header_regex = re.compile('@\w+\.?\w+ \w+-?\w+:\d+:\d+:\d+:\d+ length=\d+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isSraNew(self):
        """Identify fastq headers in the following format
        #@SRR1873770.5 DH1DQQN1:437:HACT2ADXX:1:2204:8270:58140 length=150"""
        header_regex = re.compile('@\w+\.?\w+ \w+-?\w+:\d+:\w+:\d+:\d+:\d+:\d+ length=\d+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isENA(self):
	#@ERR2509673.1 WTCHG_35574_207:2:1204:15440:129645#CTATATAC length=100
        """Identify fastq headers in the following format
        # ERR161234.14 14 length=100"""
        header_regex = re.compile('@[\w\.]+ \d+ length=\d+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isENANew(self):
	#@ERR2509673.1 WTCHG_35574_207:2:1204:15440:129645#CTATATAC length=100
        """Identify fastq headers in the following format
        # ERR161234.14 14 length=100"""
        header_regex = re.compile('@[\w\.]+ .+ length=\d+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isPacbio(self):
        """Identify fastq headers in the following format
        #@m160113_152755_42135_c100906712550000001823199104291667_s1_p0/15/7044_26271"""
        header_regex = re.compile('@\w+/\d+/\d+_\d+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

    def isFastq(self):
        """Identify fastq with any other header format
        @\w+"""
        header_regex = re.compile('@.+')
        match = re.fullmatch(header_regex, self.rec.header)
        if match != None:
            return(True)
        else:
            return(False)

class Metrics:
    """The Metrics class calculates read length and quality metrics for a given
    fastq file. The class is initialized with the following inputs:
      1. fastq (str): Path to the fastq file"""

    def __init__(self, fastq):
        self.fastq = fastq

    def avgReadLen(self):
        """Given a fastq file, calculate average read length"""
        fastq_reader = Fastq(self.fastq, './', 'phred33')
        total_length = 0
        total_reads = 0
        for lines in fastq_reader.read():
            total_length += len(lines.seq)
            total_reads += 1
            if total_reads >= 100:
                break

        avg_length = total_length/float(total_reads)
        return(avg_length)

class Prepper:
    """Prepper class create the config dictionary for a given study. The class
    constructor takes the following input parameters:
      1. input_path (str) : Path to input directory or sra accession list
      2. sra_path (str) : Path to fastq-dump executable """

    def __init__(self, input_path, out_path, sra_path): #sra_number, sra_path):
        self.input_path = os.path.abspath(input_path)
        self.out_path = os.path.abspath(out_path)
        self.sra_path = sra_path
        self.logger = logging.getLogger('NeST.prepInputs')

    def sra(self, sample, sra, files):
        sample = sample
        if type(sra) is list or sra is None:
            return(self.input_path, files[0], files[1])
        #accession = sra.split(',')
        accession = re.split(',| ', sra)
        outpath = os.path.dirname(files[0])
        if os.path.exists(files[0]) and os.path.exists(files[1]):
            return(self.input_path)
        if 'SAMN' in accession[0] or 'SRS' in accession[0]:
            sralist = []
            while not sralist:
                time.sleep(20)
                ecmd = 'esearch -db sra -query {0} | efetch -format docsum | xtract -pattern Runs -element Run@acc'.format(accession[0])
                samtosra = subprocess.Popen(ecmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                sraout = samtosra.communicate()[0]
                sralist = sraout.decode('utf-8').strip().split()
        else:
            sralist = accession
        for acc in sralist:
            sra_cmd  = ['fastq-dump', '--split-3', '-O', outpath, acc]
            sra_run = subprocess.Popen(sra_cmd, shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            sra_run.wait()
            if sra_run.returncode != 0:
                self.logger.error('Could not download {0}'.format(acc))
                print(' '.join(sra_cmd))
            else:
                self.logger.debug('Downladed complete: {0}'.format(acc))
        if sra == sample:
           return(self.input_path)
        #Get R1 and R2 list
        r1 = ['{1}/{0}_1.fastq'.format(acc, outpath) for acc in sralist]
        r2 = ['{1}/{0}_2.fastq'.format(acc, outpath) for acc in sralist]
        #print(sample, accession, r1, r2)
        #Cat R1's together and rename to sample name
        cat_cmd = 'cat '
        for files in r1:
            cat_cmd += '{0} '.format(files)
        cat_cmd += '> {1}/{0}_1.fastq'.format(sample, outpath)
        #cat_cmd = 'cat {0} > /nv/hp10/sravishankar9/scratch/NeST/NEJM/{1}_1.fq'.format(' '.join(r1), sample)
        cat_run = subprocess.Popen(cat_cmd, shell=True)
        cat_run.wait()
        #Cat R2's together and rename to sample name
        cat_cmd = 'cat '
        for files in r2:
            cat_cmd += '{0} '.format(files)
        cat_cmd += '> {1}/{0}_2.fastq'.format(sample, outpath)
        #cat_cmd = 'cat {0} > /nv/hp10/sravishankar9/scratch/NeST/NEJM/{1}_2.fq'.format(' '.join(r2), sample)
        cat_run = subprocess.Popen(cat_cmd, shell=True)
        cat_run.wait()
        #Delete orginal files
        del_dat = ['{1}/{0}_*.fastq'.format(acc, outpath) for acc in sralist]
        del_cmd = 'rm {0}'.format(' '.join(del_dat))
        del_run = subprocess.Popen(del_cmd, shell=True)
        del_run.wait()
        return(self.input_path, r1, r2)

    def downloadSRA(self, sra_number, files):
        """Give a SRA accession list, download all the associated fastq files"""
        if os.path.exists(files[0]) and os.path.exists(files[1]):
            return(self.input_path)
        else:
            out_dir = os.path.dirname(files[0])
            self.logger.debug('Downloading : {0}'.format(sra_number))
            fqd_cmd = [self.sra_path, '--gzip', '--split-3', '-O', out_dir,
                    '-A', sra_number]
            fqd_run = subprocess.Popen(fqd_cmd, shell=False,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
            fqd_run.wait()
            if fqd_run.returncode != 0:
                self.logger.error('Could not download {0}'.format(sra_number))
                print(' '.join(fqd_cmd))
            else:
                self.logger.debug('Downladed complete: {0}'.format(sra_number))
            return(self.input_path)

    def getFastqPaths(self):
        """Given a directory path, extract all the fastq file names"""
        filenames = list()
        for subdir, dirname, files in os.walk(self.input_path):
            for filename in files:
                if ('.fastq' in filename or '.fastq.gz' in filename or
                    'fq' in filename or 'fq.gz' in filename):
                    filepath = subdir + os.sep + filename
                    filenames.append(filepath)
        return(filenames)

    def getReadPresence(self, file_name, minimum=1):
        """Given a fastq file, check for the presence of a minimum number of
        reads"""
        reader = Fastq(file_name, None, 'phred33')
        read_number = 0
        for rec in reader.read():
            read_number += 1
            if read_number >= minimum:
                return(True)
            else:
                return(False)

    def parseMaRS(self, file_name):
        """Given a fastq file, check is the file name follows the MaRS regex:
        <Year>{2}<Country>{2}<Site>{2}<Day of treatment>{2}<Treatment>{1}
        <SampleID>{4}<Genus and species>{2}<Strain type>{1}<Markers>{3}<Rep>{1}
        If the regex is found, the module decodes the sample information and
        returns a namedtuple for the sample"""

        mars_regex = ('(?P<Year>[0-9x]{2})(?P<Country>[A-Zx]{2})'
                      '(?P<Site>[A-Zx]{2})(?P<DT>[0-9]{2})'
                      '(?P<Treatment>[A-Kx]{1})(?P<SID>[0-9]{4})'
                      '(?P<GS>[a-zA-Z]{2})(?P<ST>[BFPTSx]{1})'
                      '(?P<Markers>[0-9]{3})(?P<Rep>[0-9]{1})')
        mars_groups = re.match(mars_regex, file_name)
        sample_info  = namedtuple('Sample', ['Year', 'Country', 'Site',
            'TreatmentDay', 'Treatment', 'ID', 'Genus', 'Type', 'Markers',
            'Replicate'])

        if not mars_groups:
            marker_list = np.array(['PfK13', 'PfCRT', 'PfMDR', 'MT', 'CytB',
                'PfDHPS', 'PfDHFR'])
            sample = sample_info(np.nan, 'xx', 'xx', 'xx', 'xx', 'xxxx', 'xx',
                'x', marker_list, 1)
            return(sample)
        if mars_groups.group('Year') == 'xx':
            year = np.nan
        else:
            year = int('20' + mars_groups.group('Year'))
        country = mars_groups.group('Country')
        site = mars_groups.group('Site')
        treatDate = mars_groups.group('DT')
        treatment = mars_groups.group('Treatment')
        sampleID = mars_groups.group('SID')
        genusSpecies = mars_groups.group('GS')
        type_dict = {'B': 'Blood', 'F': 'Filter blood spots', 'P': 'Plasma',
            'T': 'Tissue', 'S': 'Stool', 'x': 'Unknown'}
        type = type_dict[mars_groups.group('ST')]
        marker_list = np.array(['PfK13', 'PfCRT', 'PfMDR', 'MT', 'CytB',
            'PfDHPS', 'PfDHFR'])
        marker_found = list()
        markers = int(mars_groups.group('Markers'))
        while len(marker_found) < 8:
            markers, rem = divmod(markers, 2)
            marker_found.append(rem)

        marker_found = np.array(marker_found[::-1][1:])
        marker_list = np.extract(marker_found, marker_list)
        rep = int(mars_groups.group('Rep'))
        sample = sample_info(year, country, site, treatDate, treatment,
            sampleID, genusSpecies, type, marker_list, rep)
        return(sample)

    def prepInputs(self):
        """Given a sra accession number create a sample record of reach file. Each sample record is
        added to a dictionary with the sample name as the key and sample record
        as the value"""
        if  os.path.isfile(self.input_path):
            files = open(self.input_path)
            isfastq = False
            if os.path.splitext(self.input_path)[1] == '.tsv':
                isacclist = False
            else:
                isacclist = True
        else:
            files = self.getFastqPaths()
            isfastq = True
        experiment = dict()
        for fastq in files:
            #reader = Fastq(fastq, './', 'phred33')
            Sample = namedtuple('Sample', ['sample',
            'files', 'paired', 'year', 'country', 'site',
            'treatmentDay', 'treatment', 'iD', 'genus', 'type', 'markers',
            'replicate', 'sra'])
            #readPresence = self.getReadPresence(fastq)
            #if not readPresence:
            #    self.logger.warning('Sample doesn\'t contain minimum number of required reads; skipping sample : {0}'.format(fastq))
            #    continue
            #rec = next(reader.read())
            #identifier = Identifier(rec)
            #metric = Metrics(fastq)
            #isIllOld =  identifier.isIlluminaOld()
            #isIllNew =  identifier.isIlluminaNew()
            #isSraOld = identifier.isSraOld()
            #isSraNew = identifier.isSraNew()
            #isPac = identifier.isPacbio()
            #isENA = identifier.isENA()
            #isFastq = identifier.isFastq()
            #isENANew = identifier.isENANew()
            if isfastq:
                sample_regex = re.compile('_r1|_r2|_?l001|_?l002|_?l003|_?l004|_R1|_R2|_L001|_?L002|_L003|_L004|_1|_2') #|L001|L002|L003|L004')
                sample = sample_regex.split(os.path.basename(fastq))[0]
                sra = None
            else:
                if isacclist:
                    sample = fastq.strip()
                    sra = sample
                else:
                    study = fastq.strip().split('\t')
                    sample = study[0]
                    sra = study[1]

            sample_info = self.parseMaRS(sample)
            year = sample_info.Year
            country = sample_info.Country
            site = sample_info.Site
            td = sample_info.TreatmentDay
            treatment = sample_info.Treatment
            sid = sample_info.ID
            gs = sample_info.Genus
            stype = sample_info.Type
            markers = sample_info.Markers
            replicate = sample_info.Replicate
            paired = False
            #if isIllOld:
            #    paired_regex = re.compile('@\w+-?\w+:\d+:\d+:\d+:\d+#\d')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
            #    #if metric.avgReadLen(): removing unnecessary call for the time being ##01/24/19
            #    libType = 'Short'
            #elif isIllNew:
            #    paired_regex = re.compile('@\w+-?\w+:\d+:\w+-?\w+:\d+:\d+:\d+:\d+\s')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
                #if metric.avgReadLen():
            #    libType = 'Short'
            #elif isSraOld:
            #    paired_regex = re.compile('@\w+\.?\w+ \w+-?\w+:\d+:\d+:\d+:\d+ length=\d+')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
            #    #if metric.avgReadLen():
            #    libType = 'Short'
            #elif isSraNew:
            #    paired_regex = re.compile('@\w+\.?\w+ \w+-?\w+:\d+:\w+:\d+:\d+:\d+:\d+ length=\d+')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
                #if metric.avgReadLen():
            #    libType = 'Short'
            #elif isENA:
            #    paired_regex = re.compile('@[\w\.]+ \d+ length=\d+')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
            #    if metric.avgReadLen():
            #        libType = 'Short'
            #elif isENANew:
            #    paired_regex = re.compile('@[\w\.]+ .+ length=\d+')
            #    lib = re.findall(paired_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Illumina'
            #    if metric.avgReadLen():
            #        libType = 'Short'
            #elif isPac:
            #    lib_regex = re.compile('@\w+_\d+_\d+_\w+')
            #    lib = re.findall(lib_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Pacbio'
                #if metric.avgReadLen():
            #    libType = 'Long'
            #elif isFastq:
            #    lib_regex = re.compile('@.+')
            #    lib = re.findall(lib_regex, rec.header)[0]
            #    paired = False
            #    seqType = 'Unknown'
                #if metric.avgReadLen():
            #    libType = 'Short'
            #else:
             #   self.logger.warning('Read from {0} with header : {1} does not follow any defined fastq header format.Please correct it'.format(fastq, rec.header))
            if isfastq:
                try:
                    paired = True
                    experiment[sample] = Sample(sample,
                        [experiment[sample].files[0],fastq], paired,
                        year, country, site, td, treatment, sid, gs, stype, markers,
                        replicate, sra)
                except (KeyError, AttributeError):
                    experiment[sample] = Sample(sample,  [fastq],
                    paired, year, country, site, td, treatment, sid, gs,
                    stype, markers, replicate, sra)
            else:
                paired =True
                file_list = ['{0}/{1}/RawFastq/{1}_1.fastq'.format(self.out_path, sample),
                             '{0}/{1}/RawFastq/{1}_2.fastq'.format(self.out_path, sample)]
                experiment[sample] = Sample(sample, file_list, paired, year, country, site,
                                            td, treatment, sid, gs, stype, markers, replicate, sra)
        self.logger.debug('A total of {0} libraries were identified from the given folder'.format(len(experiment)))
        #self.logger.debug('The following libraries were detected in the given folder : {0}'.format(self.input_path))
        for sample, values in experiment.items():
            self.logger.debug('Sample : {0}; Files: {1}; Paired: {2}'.format(
                    values.sample, ''.join(values.files), values.paired))
        for samples, info in experiment.items():
            if not info.paired:
                self.logger.warning('NeST does not currently support single end runs; skipping sample : {0}'.format(samples))
                experiment.pop(samples)
        return(experiment)

if __name__ == '__main__':
    path = os.path.abspath(sys.argv[1])
    prepper = Prepper(path)
    experiment = prepper.prepInputs()
    rone = list()
    rtwo = list()
    for study in experiment:
        for files in experiment[study].files:
            if re.findall('.*_R1.*', files):
                rone.append(files)
            else:
                rtwo.append(files)
    print(rone)
    print(rtwo)


# Boat wake simulation (06.11.2022)
# Author: Dominik Kuczynski

# Code for generating 3d plots of boat wake patterns.
# A point source of surface waves of different wavelengths ('lambdas')
# travels at speed 'v', ending up in a set place.
# To speed up computation time, some values are first stored in large arrays
# during preprocessing. Other factors that affect time are t_nsteps
# and n_lbdas.

from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
from scipy.ndimage.filters import gaussian_filter

# initiate the axes
ymax = 40
xmax = ymax
pixelsize = 0.4
proportion = 8.0 / 9.0
x = np.arange(-xmax, xmax, pixelsize)
y = np.arange(0, 2 * ymax, pixelsize)
X, Y = np.meshgrid(x, y)
Z = np.ones(np.shape(X))

############################### set system properties #########################
v = 4.13  # speed of boat
A = 5e-5  # amplitude of displacement

################################ set time variables ###########################
tmax = (2 * ymax * proportion + 5.0) / v
t_nsteps = 400
tstep = 2 * tmax / t_nsteps

################################ set lambda variables #########################
lbdamin = 0.423
lbdamax = 4.5
n_lbdas = 100
lbda_step = (lbdamax - lbdamin) / n_lbdas
lbdas = np.arange(lbdamin, lbdamax, lbda_step)

# shift Y axis to place boat in the right place
shift = v * tmax - 2 * proportion * ymax
Y += shift

# dispersion relation for deep water waves
def gen_omega(k):
    return np.sqrt(9.81 * k)  # cp approx. 3.13

def preprocess():
    # do preprocessing
    xsize = int(np.ceil(2 * xmax / pixelsize))
    ysize = int(np.ceil(2 * ymax / pixelsize))

    r = np.zeros((t_nsteps, ysize, xsize))
    # r - 3d array of distances from the points source at given time

    checks = np.full((n_lbdas, t_nsteps, ysize, xsize), False)
    # checks - 4d array that stores boolean values indicating which
    # points should be affected by a wave starting at a given time and
    # propagating at speed cp.

    for t in range(0, t_nsteps):
        r[t] = np.sqrt(X ** 2 + (Y - v * t * tstep) ** 2)
        # print("\r"+"preprocessing: %.0f" %(100.0*float(t+1)/t_nsteps) + "%", end='')

        # Changed the way of printing to be more pythonic, according to PEP* standard.
        print(f"\rpreprocessing: {100.0*float(t+1)/t_nsteps:.0f}%", end="")
        for lbda in range(len(lbdas)):
            k = 2 * np.pi / lbdas[lbda]
            cp = gen_omega(k) / k
            checks[lbda][t] = r[t] <= cp * (tmax - t * tstep)
    print("")
    return checks, r

checks, r = preprocess()

# run simulation
for lbda in range(len(lbdas)):
    k = 2 * np.pi / lbdas[lbda]
    omega = gen_omega(k)
    cp = omega / k

    for t in range(t_nsteps):
        Z += (
            A
            * checks[lbda][t]
            * np.exp(-0.02 * r[t] * (tmax - t * tstep))
            * np.cos((k * r[t] - omega * (tmax - t * tstep)))
        )
        # add to Z a damped cosine surface wave originating at (0,vt) and
        # propagating for tmax-t time

    print(
        "\rlambda = %.3f  - %d/%d" % (lbdas[lbda], lbda + 1, len(lbdas)),
        end="",
        flush=True,
    )
print("")

# create the figure
fig = plt.figure(figsize=(15, 15))
ax = fig.add_subplot(111, projection="3d")
ax.view_init(elev=35, azim=110)
ax.set(zlim=[0, 2])
ax.set_title(
    "%d lambdas in (%.2f, %.2f), " % (n_lbdas, lbdamin, lbdamax)
    + "ymax=%d, pixelsize=%.2f, t_nsteps=%d" % (ymax, pixelsize, t_nsteps)
    + ", v=%.2f" % (v)
)

# plot the surface
ax.plot_surface(X, Y, Z, cmap=plt.cm.bone, rcount=400, ccount=400, antialiased=False)


#!/usr/bin/env python

'''
USAGE: python star_scripts/summarise_mapping_quality.py ./STAR_OUTPUT

DESCRIPTION: summarise mapping/feature count results (mapping rates etc.)
Directory should be like
/working_dir/
	L____ STAR_OUTPUT (made by running star.sh. May be named differently)
	L____ star_scripts
Make sure to call this script from working directory as shown above

OUTPUT: ./STAR_OUTPUT/df_mappeing_featureCounts_quality_summary.csv
'''

import subprocess
import pandas as pd
import numpy as np
import sys
import argparse

def validate_dir(d):
	if d[-1] == '/':
		return d[:-1]
	else:
		return d

def extract_info(directory,df,ID):
	with open(directory+'/'+ID+'/Log.final.out','r') as f:
		'''
		Input looks like:
		Started job on |       Jun 28 04:55:40
		Started mapping on |       Jun 28 04:57:11
		'''
		for line in f:
			if '|' not in line:
				continue
			l = line.split('|')
			label = l[0].strip()
			value = l[1].strip()
			df.loc[label,ID] = value

	with open(directory+'/'+ID+'/counts.txt.summary','r') as f:
		'''
		Input looks like:
		Status  Aligned.sortedByCoord.out.bam
		Assigned        4788206
		'''
		for line in f:
			l = line.strip().split('\t')
			df.loc[l[0]] = l[1]
	return df

def main():
	parser = argparse.ArgumentParser()
	parser.add_argument('input_dir')
	parser.add_argument('--all',help='optional',action='store_true')
	args = parser.parse_args()
	input_dir = validate_dir(args.input_dir)
	subprocess.call('ls '+input_dir+' | ls -d */ > '+input_dir+'/rm_run_folder_lst.txt',shell=True)
	df = pd.DataFrame()
	with open (input_dir+'/rm_run_folder_lst.txt','r') as f:
		for line in f:
			ID = line.strip().rstrip('/')
			if ID == 'counts':
				continue
			df = extract_info(input_dir,df,ID)
	if not args.all:
		df = df[~np.all(df == 0, axis=1)]
		df = df[~np.all(df == '0', axis=1)]
		df = df[~np.all(df == '0%', axis=1)]
		df = df[~np.all(df == '0.00%', axis=1)]
		df = df.drop(['Started job on','Started mapping on','Finished on','Average input read length','Status'])
	df.to_csv(input_dir+'/df_mapping_featureCounts_quality_summary.csv')
	subprocess.call('rm '+input_dir+'/rm_run_folder_lst.txt',shell=True)

if __name__ == '__main__':
	main()


# python3
# coding: utf-8 -*-
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import matplotlib.pyplot as plt
import numpy as np
from functools import reduce
import sys

BATCH_SIZE = 512        # 批的大小
EPOCHS = 10             # 遍历训练集的次数
DEVICE = torch.device('cuda')   # 分配设备
lr = 0.001              # 学习率
momentum = 0.9

#  定义网络
class LeNet5(nn.Module):

    def __init__(self):
        super(LeNet5, self).__init__()      # 继承父类nn.Model的属性并初始化
        self.conv1 = nn.Conv2d(1, 6, 5)     # 卷积层1——LeNet5第一层
        self.conv2 = nn.Conv2d(6, 16, 5)    # 卷积层2——LeNet5第三层
        self.fc1 = nn.Linear(16 * 5 * 5, 120)   # 全连接层1——LeNet5第五层
        self.fc2 = nn.Linear(120, 84)       # 全连接层2——LeNet5第六层
        self.fc3 = nn.Linear(84, 10)        # 全连接层output——LeNet第七层

    def forward(self, x):
        """正向传导过程

        :param x: 输入样本
        """
        x = F.max_pool2d(F.relu(self.conv1(x)), (2,2)) # 池化层1——LeNet5第二层
        x = F.max_pool2d(F.relu(self.conv2(x)), (2,2)) # 池化层2——LeNet5第四层
        # x = x.view(-1, self.num_flat_features(x))   # 数据重组为256*(16*5*5)
        x = x.view(-1, reduce(lambda x,y:x*y, x.size()[1:]))   # 数据重组为256*(16*5*5)
        x = F.relu(self.fc1(x))             # 激活函数，产生新的输出
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 用torch的图像处理库预处理
transforms = transforms.Compose([
    transforms.Resize(32),
    transforms.ToTensor(),
    # # this line for a glimpse of the data
    # transforms.Lambda(lambda x: x.repeat(3, 1, 1)),
    transforms.Normalize((0.1307,), (0.3081,)),
])

# 下载MNIST数据库，设定数据加载器
train_set = datasets.MNIST(root='data', train=True, download=True, transform=transforms)
test_set = datasets.MNIST(root='data', train=False, download=True, transform=transforms)

train_loader = torch.utils.data.DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)

def train(model, device, train_loader, optimizer, criterion, epoch):
    """训练过程

    :param model: 输入网络模型
    :param device: 分配设备
    :param train_loader: 训练数据集加载
    :param optimizer: 优化函数
    :param criterion: 损失函数
    :param epoch: 训练总次数
    """
    model.train()   # 启用 BatchNormalization 和 Dropout，让model变成训练模式，起到防止网络过拟合的问题
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        if (batch_idx + 1) % 30 == 0:
            print('Epoch: {} [{}/{} ({:.0f}%]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

def test(model, device, test_loader, criterion):
    """测试过程

    :param model: 输入网络模型
    :param device: 分配设备
    :param test_loader: 测试数据集加载
    :param criterion: 损失函数
    """
    model.eval()    # 不启用 BatchNormalization 和 Dropout，不会取平均，而是用训练好的值
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target)
            pred = output.max(1, keepdim=True)[1]
            correct += pred.eq(target.view_as(pred)).sum().item()
    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

if __name__ == '__main__':
    # # 显示一个batch的数据
    # images, labels = next(iter(trainloader))
    # img = torchvision.utils.make_grid(images)
    # img = img.numpy().transpose(1, 2, 0)
    # std = [0.1304,]
    # mean = [0.3081,]
    # img = img * std + mean
    # plt.imshow(img)
    # plt.show()

    if len(sys.argv) < 3 or len(sys.argv) > 4:
        print('usage: python LeNet5.py <train/test> <model_path>')
        sys.exit(1)

    if sys.argv[1] == 'train':
        # 训练过程
        model = LeNet5().to(DEVICE)                         # 构建训练网络
        optimizer = optim.Adam(model.parameters(), lr=lr)   # 优化算法：Adam算法
        criterion = nn.CrossEntropyLoss(reduction='sum')    # 损失算法：交叉熵函数
        for epoch in range(1, EPOCHS + 1):
            train(model, DEVICE, train_loader, optimizer, criterion, epoch)
            test(model, DEVICE, test_loader, criterion)
        torch.save(model, sys.argv[2])         # 保存模型
        print('trianing done.')

    elif sys.argv[1] == 'test':
        # 加载模型并测试
        model = torch.load(sys.argv[2])
        criterion = nn.CrossEntropyLoss(reduction='sum')
        test(model, DEVICE, test_loader, criterion)


from datetime import datetime
import threading
import socket

HOST = 'localhost'
PORT = 61001

def new_conn():
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        conn.sendall(b'Enter your nickname, please!')
        nick = conn.recv(1024)
        with open('logs', 'a') as f:
            now = datetime.now()
            tmp = '[' + str(nick) + ']:(' + now.strftime("%d/%m/%Y %H:%M:%S") + ') - ' + 'Connected by (IP address, port) - ' + str(addr) + ';\n'
            f.write(tmp)
        while True:
            data = conn.recv(1024)
            if data == b'':
                now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
                tmp = '[CLIENT]:(' + now + ')[' + str(nick) + '] - disconnected;\n'
                with open('logs', 'a') as f:
                    f.write(tmp)
                print(tmp)
                break
            print(nick, 'message:', data)
            if not data: break
            conn.sendall(data)
            with open('logs', 'a') as f:
                now = datetime.now()
                tmp = '[' + str(nick) + ']:(' + now.strftime("%d/%m/%Y %H:%M:%S") + ') - ' + str(data) + ';\n'
                f.write(tmp)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()

    t1 = threading.Thread(group=None, target=new_conn, name=None, args=(), kwargs={}, daemon=None)
    t1.start()
    t2 = threading.Thread(group=None, target=new_conn, name=None, args=(), kwargs={}, daemon=None)
    t2.start()

    try:
        t1.run()
    except:
        t1.join()

    try:
        t2.run()
    except:
        t2.join()


import subprocess
from datetime import datetime, timedelta, timezone
import re
import os
import matplotlib.pyplot as plt
from datetime import datetime
import time
import csv
from threading import Lock
import yaml
from prometheus_data_collector import *

"""
Manifest files
kubectl apply -f ./../5gc-manifests/free5gc-v3.2.0/ --recursive -n paul
kubectl apply -f ./../5gc-manifests/ueransim-v3.2.6-gnb/ --recursive -n paul
kubectl apply -f ./../5g-manifests/ueransim-v3.2.6-ue/ue-attacker/ --recursive -n paul
"""

coreSelector = 'free5gc-v3.2.0-duplicate'
gnbSelector = 'ueransim-v3.2.6-gnb-duplicate'
ueSelector = 'ueransim-v3.2.6-ue-duplicate'
ueManifest = f'../5g-manifests/{ueSelector}/' # '../5gc-manifests/ueransim-v3.2.6-ue-slice-x3/ue-attacker/'

"""
shell on container udm
ifconfig eth0
busypolling
linux tc
"""

# Markers needed to read the logs
REGISTRATION_REQUEST = 'Sending Initial Registration'
REGISTRATION_ACCEPT = 'Registration accept received'
PDU_SESSION_EST_REQUEST = 'Sending PDU Session Establishment Request'
PDU_SESSION_EST_ACCEPT = 'PDU Session Establishment Accept received'
DEREGISTRATION_REQUEST = 'Starting de-registration procedure'
DEREGISTRATION_ACCEPT = 'De-registration accept received'

command_get_ue = "kubectl get pods -n paul | awk '/ueransim-ue1/ {print $1;exit}'"
command_delete = f'kubectl delete -f ./{ueManifest} --recursive -n paul'
command_create = f'kubectl apply -f ./{ueManifest} --recursive -n paul'

def exec_command(command):
    result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = result.stdout.decode().strip()  # Decode and remove leading/trailing whitespace

    return output

def delete_files_in_folder(folder_path, key=''):
    # Get a list of all files in the folder
    files = os.listdir(folder_path)
    if key:
        files = [filename for filename in files if key in filename]

    # Iterate over the files and delete each one
    for file_name in files:
        file_path = os.path.join(folder_path, file_name)
        if os.path.isfile(file_path):
            os.remove(file_path)
            #print(f"Deleted file: {file_path}")

def init_pod(deployments):
    # Try to get existing ue pod
    existing_pod = exec_command(command_get_ue)

    if existing_pod:
        # There is a running ue pod
        print("Existing pod to be terminated:", existing_pod)

        # Delete existing pod
        exec_command(command_delete)

        # Wait for the pod to terminate
        print('Waiting for the existing pod to terminate...')
        while(exec_command(command_get_ue)):
            time.sleep(2)

    # Create pod
    print("Creating pod...")
    if deployments > 1:
        exec_command(command_create)
    else:
        exec_command(f'kubectl apply -f ./{ueManifest} --recursive -n paul')

    pod = exec_command(f"kubectl get pods -n paul | awk '/ueransim-ue1" + "/ {print $1;exit}'")
    while exec_command(f"kubectl get pod {pod} -n paul -o json | jq -r '.status.phase'") != 'Running':
        time.sleep(1)
    time.sleep(5)

def kill_pod():
    # Get nunning ue pod
    running_pod = exec_command(command_get_ue)

    if running_pod:
        # There is a running ue pod

        # Delete existing pod
        exec_command(command_delete)

        # Wait for the pod to terminate
        print(f'Pod {running_pod} terminating...')

        while(exec_command(command_get_ue)):
            time.sleep(2)

        print(f'[{datetime.now(timezone.utc).strftime("%H:%M:%S.%f")}]  Pod {running_pod} terminated.')

def get_params_from_yaml_file(yaml_file_path, params):
    # Load the YAML file
    with open(yaml_file_path, 'r') as file:
        data = yaml.safe_load(file)

    output = {}
    fields = data['data']['amfcfg.yaml']
    for param, key_string in params.items():
        value = None
        key_chain = fields.split('\n')
        for i in range (len(key_chain)):
            if key_string in key_chain[i]:
                value = key_chain[i+1].split('- ')[1]
                #print(value)
        output[param] = value

    return output

def complete_params(atk_params):
    params_values_amf = get_params_from_yaml_file(
        '../5g-manifests/free5gc/nf/amf1/amf-configmap.yaml',
        {
            'ciphering_algorithm': 'cipheringOrder',
            'integrity_protection': 'integrityOrder'
         }
    )
    for params in params_values_amf:
        atk_params[params] = params_values_amf[params]

    default_deployments_path = {
        'udm': f'../5g-manifests/{coreSelector}/nf/udm/udm-deployment.yaml',
        'ausf': f'../5g-manifests/{coreSelector}/nf/ausf/ausf-deployment.yaml',
        'udr': f'../5g-manifests/{coreSelector}/nf/udr/udr-deployment.yaml',
        'ue1': f'../5g-manifests/{ueSelector}/ue1/ue-deployment.yaml',
        'gnb1': f'../5g-manifests/{gnbSelector}/gnb1/gnb-deployment.yaml',
        'amf1': f'../5g-manifests/{coreSelector}/nf/amf1/amf-deployment.yaml'
    }

    additional_deployments_path = {
        'ue2': f'../5g-manifests/{ueSelector}/ue2/ue-deployment.yaml',
        'gnb2': f'../5g-manifests/{gnbSelector}/gnb2/gnb-deployment.yaml',
        'amf2': f'../5g-manifests/{coreSelector}/nf/amf2/amf-deployment.yaml'
    }

    for function, path in default_deployments_path.items():
        # Load the YAML file
        with open(path, 'r') as file:
            data = yaml.safe_load(file)
            data = data['spec']['template']['spec']['containers'][0]['resources']
        atk_params[f'{function}_cpu_requests'] = data['requests']['cpu']
        atk_params[f'{function}_cpu_limits'] = data['limits']['cpu']

    if atk_params['deployments'] > 1:
        for function, path in additional_deployments_path.items():
            # Load the YAML file
            with open(path, 'r') as file:
                data = yaml.safe_load(file)
                data = data['spec']['template']['spec']['containers'][0]['resources']
            atk_params[f'{function}_cpu_requests'] = data['requests']['cpu']
            atk_params[f'{function}_cpu_limits'] = data['limits']['cpu']

    return atk_params

def run_flooding_attack(atk_params, log_folder, output_charts_folder):
    end_time = (atk_params['start'] + timedelta(minutes=atk_params['duration'])).timestamp()
    next_wave_time = atk_params['start'].timestamp()

    delete_files_in_folder('logs/')
    delete_files_in_folder('ue-data/')
    delete_files_in_folder('charts/', key=f'ue{2 * (atk_params["deployments"]==1)}')

    atk_params = complete_params(atk_params)

    # Save attack parameters
    with open('logs/atk_params.txt', 'w', newline="") as file:
        writer = csv.writer(file)
        # Write each parameter as a row in the CSV file
        for key, value in atk_params.items():
            writer.writerow([key, value])

    wave_times = [[],[]]
    results = []
    for pod in range(atk_params['deployments']):
        dictionary = {'registration_duration':[[],[]], 'pdu_establishment_duration':[[],[]], 'deregistration_duration':[[],[]]}
        results.append(dictionary)

    # Keep the script running until end time
    while datetime.now(timezone.utc).timestamp() < end_time:

        wave_time = datetime.now(timezone.utc)
        wave_num = len(wave_times[0])
        next_wave_time = (wave_time + timedelta(seconds=atk_params['wave_freq']))

        # Create new nodes and store logs in a separate file
        prc = []
        if wave_num == 0:
            for ue_pod in range(atk_params['deployments']):
                log_file_path = f'{log_folder}ueransim-ue{ue_pod+1}.txt'
                my_pod = f"$(kubectl get pods -n paul | awk '/ueransim-ue{ue_pod+1}" + "/ {print $1;exit}')"

                with Lock():
                    prc.append(subprocess.Popen(f'kubectl exec {my_pod} -n paul -- ./nr-ue -c config/free5gc-ue.yaml -n {str(atk_params["nb_ues"])} > {log_file_path}', shell=True, stdout=subprocess.PIPE))

            time.sleep(1)

        # Save wave time
        with open('logs/atk_params.txt', 'a', newline="") as file:
            writer = csv.writer(file)
            writer.writerow([f'Wave{wave_num}', wave_time])

        # Perform the attack on multiple ueransim-ue pods in parallel
        processes, output_queues = [], []

        for pod in range(atk_params['deployments']):
            log_file_path = f'{log_folder}ueransim-ue{pod+1}.txt'
            output_queues.append(multiprocessing.Queue())

            registration_duration, pdu_establishment_duration, deregistration_duration = results[pod].values()
            process = multiprocessing.Process(target=one_round_atk, args=(atk_params,registration_duration, pdu_establishment_duration, deregistration_duration, wave_times, log_file_path, wave_time, pod+1, output_queues[pod]))
            processes.append(process)

        for process in processes:
            process.start()

        for process in processes:
            process.join()

        time.sleep(atk_params['cooldown'])

        # Get results
        for i in range (atk_params['deployments']):
            registration_duration, pdu_establishment_duration, deregistration_duration, wave_times = output_queues[i].get()
            results[i]['registration_duration'] = registration_duration
            results[i]['pdu_establishment_duration'] = pdu_establishment_duration
            results[i]['deregistration_duration'] = deregistration_duration

            # Save the data to csv files
            save_data_points(registration_duration, f'ue-data/ue{i+1}_registration_time.csv')
            save_data_points(pdu_establishment_duration, f'ue-data/ue{i+1}_pdu_establishment_time.csv')
            save_data_points(wave_times, f'ue-data/ue{i+1}_wave_time.csv')

            log_file_path = f'{log_folder}ueransim-ue{i+1}.txt'

            if atk_params['restart_pod']:
                with Lock():
                    shutil.copy(log_file_path, f'logs/ueransim-ue{i+1}-wave{wave_num}.txt')

            # Plot the chart
            output_charts_path = f'{output_charts_folder}ue{i+1}_processing_times.png'
            plot_time_charts(registration_duration, pdu_establishment_duration, deregistration_duration, wave_times, output_charts_path)

        # Sleep until next wave
        time.sleep((next_wave_time - datetime.now(timezone.utc)).total_seconds())

    time.sleep(2)

def one_round_atk(
        atk_params,
        registration_duration,
        pdu_establishment_duration,
        deregistration_duration,
        wave_times,
        log_file_path,
        wave_time,
        ue_pod,
        output_queue=None,
        timer=30
    ):

    withDeregistration = atk_params['deregistration'] != None
    absolute_time_start = atk_params['start']
    wave_num = len(wave_times[0])
    max_end_time = (wave_time + timedelta(seconds=timer)).timestamp()

    formatted_wavetime = wave_time.strftime("%Y-%m-%d %H:%M:%S.%f")
    relative_wave_time = (wave_time - absolute_time_start).total_seconds()

    print(f'[{formatted_wavetime.split(" ")[1]}]  Proceeding wave {wave_num}')
    wave_times[0].append(relative_wave_time)
    wave_times[1].append(wave_num)
    time_for_procedure = 0

    registration_start_times = {}
    pdu_session_start_times = {}
    deregistration_start_times = {}
    deregistrations_count = 0
    deregistrations_started = False
    regist_and_pdu_done = False
    deregistrations_completed = False
    wave_completed = False
    line_counter = 0
    my_pod = exec_command("kubectl get pods -n paul | awk '/ueransim-ue" + str(ue_pod) + "/ {print $1;exit}'")

    while datetime.now(timezone.utc).timestamp() < max_end_time:
        logs = parse_log(log_file_path)

        for i in range (line_counter, len(logs), 1):#timestamp, line in logs:
            timestamp, line = logs[i]
            match = re.search(r'\[(\d+)\|nas\]', line)
            if match:
                ue_id = match.group(1)
                if REGISTRATION_REQUEST in line:
                    registration_start_times[ue_id] = timestamp
                elif REGISTRATION_ACCEPT in line:
                    #print(REGISTRATION_ACCEPT)
                    if ue_id in registration_start_times:
                        registration_start_time = registration_start_times[ue_id]
                        time_for_procedure = (timestamp - registration_start_time).total_seconds()
                        relative_registration_start_time = (registration_start_time - absolute_time_start).total_seconds()
                        registration_duration[0].append(relative_registration_start_time)
                        registration_duration[1].append(time_for_procedure)
                        del registration_start_times[ue_id]

                elif PDU_SESSION_EST_REQUEST in line:
                    pdu_session_start_times[ue_id] = timestamp
                elif PDU_SESSION_EST_ACCEPT in line:
                    #print(PDU_SESSION_EST_ACCEPT)
                    if ue_id in pdu_session_start_times:
                        pdu_session_start_time = pdu_session_start_times[ue_id]
                        time_for_procedure = (timestamp - pdu_session_start_time).total_seconds()
                        relative_pdu_session_start_time = (pdu_session_start_time - absolute_time_start).total_seconds()
                        pdu_establishment_duration[0].append(relative_pdu_session_start_time)
                        pdu_establishment_duration[1].append(time_for_procedure)
                        del pdu_session_start_times[ue_id]

                elif DEREGISTRATION_REQUEST in line:
                    deregistration_start_times[ue_id] = timestamp
                elif DEREGISTRATION_ACCEPT in line:
                    #print(DEREGISTRATION_ACCEPT)
                    if ue_id in deregistration_start_times:
                        deregistration_start_time = deregistration_start_times[ue_id]
                        time_for_procedure = (timestamp - deregistration_start_time).total_seconds()
                        relative_deregistration_start_time = (deregistration_start_time - absolute_time_start).total_seconds()
                        deregistration_duration[0].append(relative_deregistration_start_time)
                        deregistration_duration[1].append(time_for_procedure)
                        del deregistration_start_times[ue_id]
                        deregistrations_count += 1

                # Debugging negative times bug due to access conflicts (?) to the log file
                if time_for_procedure < 0:
                    print(timestamp, line, time_for_procedure)
                    exit(1)

        line_counter = len(logs)

        registrations_completed = not bool(registration_start_times) # Is True if the dictionary is empty, i.e. there is no registration ongoing
        pdu_session_est_completed = not bool(pdu_session_start_times) # Is True if the dictionary is empty, i.e. there is no PDU session establishment ongoing
        deregistrations_completed = not bool(deregistration_start_times)

        #print(f'line {line_counter}, {len(registration_start_times)}  regstr,  {deregistrations_count} deregistrations, {deregistrations_completed}, {wave_num}')

        # Check if it's time to deregister ()
        if withDeregistration and (not deregistrations_started) and ((wave_num == 0 and registrations_completed and pdu_session_est_completed) or wave_num>0):
            ue_dump = exec_command(f'kubectl exec {my_pod} -n paul -- ./nr-cli --dump')
            ue_nodes = ue_dump.split('\n')
            for ue_id in ue_nodes:
                exec_command(f'kubectl exec {my_pod} -n paul -- ./nr-cli {ue_id} --exec "deregister {atk_params["deregistration"]}"')
            deregistrations_started = True
        #elif atk_params['restart_pod'] and registrations_completed and pdu_session_est_completed:

        if deregistrations_started and deregistrations_completed and registrations_completed and pdu_session_est_completed:
            break

    # In case procedures went wrong
    if not registrations_completed:
        print(f'[ERROR]  {len(registration_start_times)} UEs have not been registered')
        print(f'[ERROR]  {len(pdu_session_start_times)} UEs have not established a PDU session')
    if not deregistrations_completed:
        if len(deregistration_start_times) == 0:
            print(f'[WARNING] Deregistration procedure has encountered errors but has now completed.')
        else:
            print(f'[ERROR]  {len(deregistration_start_times)} UEs have not been deregistered')

    if output_queue != None:
        output_queue.put((registration_duration, pdu_establishment_duration, deregistration_duration, wave_times))

    return registration_duration, pdu_establishment_duration, deregistration_duration, wave_times

def read_subprocess(process):
    logs = []
    line_counter = 0
    line_limit = 200

    # Read the output of the subprocess line by line
    for line_raw in iter(process.stdout.readline, b''):
        line = line_raw.decode('utf-8')
        match = re.search(r'^\[(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})\]', line)
        if match:
            #print(line)
            timestamp = datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S.%f")

            # Ensure the timestamp is in UTC (offset-aware)
            timestamp_utc = timestamp.replace(tzinfo=timezone.utc)

            logs.append((timestamp_utc, line))
            line_counter += 1
        if line_counter > line_limit:
            break
    return logs

def save_data_points(data_points, filename):
    mode = "w"  # "w" for writing, "a" for appending
    x,y = data_points

    # Open the CSV file in the specified mode
    with open(filename, mode, newline="") as file:
        writer = csv.writer(file)

        # Write each point as a row in the CSV file
        for i in range (0,len(x),1):
            writer.writerow([x[i],y[i]])
    print(f'   Data saved as csv ({filename})')

def plot_time_charts(registration_duration, pdu_establishment_duration, deregistration_duration, wave_times, output_chart_path):
    plt.plot(registration_duration[0],registration_duration[1], 'x', label='Initial Registration', color='tab:green')
    plt.plot(pdu_establishment_duration[0],pdu_establishment_duration[1], '+', label='PDU Session Establishment', color='tab:orange')
    plt.plot(deregistration_duration[0],deregistration_duration[1], 'x', label='Deregistration', color='tab:red')

    plt.plot(wave_times[0], [0]*len(wave_times[0]), 'o', label="Waves", color='tab:blue')
    plt.xlabel('Time elapsed (s)',)
    plt.ylabel('Processing time (s)')
    plt.title('UE requests processing time')
    plt.legend()
    plt.grid(True)

    if output_chart_path:
        plt.savefig(output_chart_path)
        plt.close()
    else:
        plt.show()
    print(f'   Chart generated ({output_chart_path})')

def parse_log(log_file_path, start_at=None):
    logs = []
    time_window = start_at == None
    with open(log_file_path, 'r') as file:
        for line in file:
            match = re.search(r'^\[(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})\]', line)
            if match:
                #print(line)
                timestamp = datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S.%f")
                # Ensure the timestamp is in UTC (offset-aware)
                timestamp_utc = timestamp.replace(tzinfo=timezone.utc)

                if not time_window and timestamp_utc >= start_at:
                    time_window = True

                if time_window:
                    logs.append((timestamp_utc, line))
                    #print(line)
    return logs

def find_message(logs, ue_id, MESSAGE):
    for timestamp, line in logs:
        if f'[{ue_id}|nas]' in line and MESSAGE in line:
            return timestamp
    return None

if __name__ == "__main__":

    atk_params = {
        'start': datetime.now(timezone.utc),
        'duration': 5,                  # in minutes
        'collection_extra_time': 2,     # in minutes
        'collection_step': 1,           # in seconds
        'wave_freq': 60,                # in seconds
        'cooldown': 1,                  # in seconds
        'nb_ues': 100,
        'deployments': 1,
        'restart_pod': True,
        'deregistration': 'normal',
        'integrity_protection': '',
        'ciphering_algorithm': '',
        'gnb1_cpu_requests':'',
        'gnb1_cpu_limits':'',
        'ue1_cpu_requests':'',
        'ue1_cpu_limits':'',
        'amf1_cpu_requests':'',
        'amf1_cpu_limits':'',
        'gnb2_cpu_requests':'',
        'gnb2_cpu_limits':'',
        'ue2_cpu_requests':'',
        'ue2_cpu_limits':'',
        'amf2_cpu_requests':'',
        'amf2_cpu_limits':'',
        'ausf_cpu_requests': '',
        'ausf_cpu_limits': '',
        'udr_cpu_requests': '',
        'udr_cpu_limits': '',
        'udm_cpu_requests': '',
        'udm_cpu_limits': '',
        'udm_rate':1,                   # in mbit
        'udm_burst':10,                 # in kb
        'udm_latency':70,               # in ms
    }

    # Initialization
    init_pod(atk_params['deployments'])

    # Time parameters
    start_now = atk_params['start']
    start_time = start_now.timestamp()
    end_time = (start_now + timedelta(minutes=atk_params['duration'])).timestamp()
    end_collection_time = (start_now + timedelta(minutes=atk_params['duration']+atk_params['collection_extra_time'])).timestamp()
    #print(end_time)
    step = atk_params['collection_step']

    # Collect data from the testbed (Prometheus) in the background
    log_collection_process = multiprocessing.Process(target=generate_logs, args=(atk_params['deployments'],))
    log_collection_process.start()
    print(f'[{datetime.now(timezone.utc).strftime("%H:%M:%S.%f")}]  Log collection running in background.')

    # Plot charts every 5 seconds for real-time visualisation
    delete_files_in_folder('data/')
    real_time_charts_process = multiprocessing.Process(target=real_time_charts, args=(metric_lists, labels_dict, start_time, end_collection_time, step))
    real_time_charts_process.start()
    print(f'[{datetime.now(timezone.utc).strftime("%H:%M:%S.%f")}]  Real-time charts running in background.')

    # Perform attack and save results
    print(f'[{datetime.now(timezone.utc).strftime("%H:%M:%S.%f")}]  Performing the flooding attack...')
    run_flooding_attack(atk_params, 'logs/', 'charts/')
    kill_pod()

    # Waiting for the background process to complete
    log_collection_process.terminate()
    log_collection_process.join()
    real_time_charts_process.join()

    # Commit the results to the remote Git repository
    git_commit_results(str(int(start_time)))


from rest_framework import serializers
from .models import Friend, FriendRequest
from user.serializers import UserSeializer
from datetime import timedelta, datetime

class FriendRequestSerializer(serializers.ModelSerializer):
    """ FriendRequest serializer"""

    class Meta:
        model = FriendRequest
        fields = (
            'id',
            'sender',
            'receiver',
            'status',
            'created_at',
        )
        extra_kwargs = {
            'sender' : { 'read_only' : True },
            'receiver' : { 'write_only' : True },
            'status' : { 'read_only' : True },
            'created_at' : { 'read_only' : True },
        }

    def validate(self, attrs):
        sender = self.context.get('sender')
        current_created_at = datetime.now()
        one_minute_earlier = current_created_at - timedelta(minutes=1)
        check_request_count = FriendRequest.objects.filter(
            sender=sender,
            created_at__gte=one_minute_earlier,
            created_at__lt=current_created_at,
        ).count()
        if check_request_count >= 3:
            raise serializers.ValidationError('You\'ve sent too many friend requests in a short time. Please wait a bit before sending more.')
        return attrs

    def validate_receiver(self, attrs):
        sender = self.context.get('sender')
        if sender == attrs:
            raise serializers.ValidationError('Turns out, you can\'t be friends with yourself. Who would\'ve thought?')

        try:
            FriendRequest.objects.get(sender=sender, receiver=attrs)
            raise serializers.ValidationError('Your friend request has already been sent.')
        except FriendRequest.DoesNotExist:
            pass
        try:
            FriendRequest.objects.get(sender=attrs, receiver=sender)
            raise serializers.ValidationError('You\'ve got another friend request from this user. How unexpected!')
        except FriendRequest.DoesNotExist:
            return attrs

    def to_representation(self, instance):
        data =  super().to_representation(instance)
        data['sender'] = UserSeializer(instance.sender).data
        return data

    def create(self, validated_data):
        validated_data['created_at'] = datetime.now()
        return super().create(validated_data)

class FriendListSerializer(serializers.ModelSerializer):
    """ Friend List serializer """
    friend = serializers.SerializerMethodField()

    class Meta:
        model = Friend
        fields = [
            'id',
            'friend',
            'created_at'
        ]

    def get_friend(self, instance):
        user = self.context.get('user')
        # check logged in user with user_one and user_two and return the friend name
        if user == instance.user_one:
            return UserSeializer(instance.user_two).data
        return UserSeializer(instance.user_one).data


from django.shortcuts import render, HttpResponse
from app1 import models
from crm import settings
import os, json
# Create your views here.
def stu_my_classes(req):
    return render(req, "student/stu_my_classes.html")

def studyrecords(request, enroll_obj_id):
    enroll_obj = models.Enrollment.objects.get(id=enroll_obj_id)
    return render(request, "student/studyrecords.html",{"enroll_obj": enroll_obj})

def homework_detail(request, studyrecord_id):
    studyrecord_obj = models.StudyRecord.objects.get(id=studyrecord_id)

    if request.method == "POST":
        print(request.FILES)
        homework_path = "{base_dir}/{course_id}/{course_record_id}/{studyrecord_id}".format(
            base_dir=settings.HOMEWORK_DATA,
            course_id=studyrecord_obj.student.enrolled_class.id,
            course_record_id=studyrecord_obj.course_record_id,
            studyrecord_id=studyrecord_obj.id
        )

        if not os.path.isdir(homework_path):
            os.makedirs(homework_path, exist_ok=True)

        for k, file_obj in request.FILES.items():
            with open('%s/%s' % (homework_path, file_obj.name), "wb") as f:
                for chunk in file_obj.chunks():
                    f.write(chunk)
        return HttpResponse(json.dumps({"status": 0, "msg": "file upload sucess"}))

    return render(request, "student/homework_detail.html", {"studyrecord_obj":studyrecord_obj})


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

from django.db import models, migrations
from django.conf import settings

class Migration(migrations.Migration):

    dependencies = [
        ('class_overviews', '0001_initial'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Department',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
                ('department_code', models.CharField(max_length=4)),
                ('department_name', models.CharField(max_length=100)),
                ('department_info', models.CharField(max_length=500)),
            ],
            options={
                'ordering': ['department_name'],
            },
        ),
        migrations.CreateModel(
            name='Major',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
                ('major', models.CharField(max_length=50)),
                ('required_classes', models.ManyToManyField(to='class_overviews.Course', blank=True)),
            ],
            options={
                'ordering': ['major'],
            },
        ),
        migrations.CreateModel(
            name='Minor',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
                ('minor', models.CharField(max_length=50)),
                ('required_classes', models.ManyToManyField(to='class_overviews.Course', blank=True)),
            ],
            options={
                'ordering': ['minor'],
            },
        ),
        migrations.CreateModel(
            name='Professor',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
                ('first_name', models.CharField(max_length=100)),
                ('last_name', models.CharField(max_length=100)),
                ('full_name', models.CharField(default='albert einstein', max_length=200)),
                ('email_id', models.EmailField(max_length=254)),
                ('bio', models.CharField(max_length=500)),
                ('office_location', models.CharField(max_length=100)),
                ('photo', models.FileField(upload_to='Profiles/static/img')),
                ('classes', models.ManyToManyField(to='class_overviews.Section', blank=True)),
                ('current_department', models.ForeignKey(null=True, blank=True, to='Profiles.Department')),
            ],
            options={
                'ordering': ['last_name', 'first_name'],
            },
        ),
        migrations.CreateModel(
            name='StudentUser',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
                ('first_name', models.CharField(default='First', max_length=100)),
                ('last_name', models.CharField(default='Last', max_length=100)),
                ('full_name', models.CharField(default='First Last', max_length=200)),
                ('email_id', models.EmailField(default='abc123@case.edu', max_length=254)),
                ('bio', models.CharField(default='none', max_length=500)),
                ('photo', models.FileField(upload_to='Profiles/static/img')),
                ('grad_year', models.IntegerField(default=2016)),
                ('majors', models.ManyToManyField(to='Profiles.Major', blank=True)),
                ('minors', models.ManyToManyField(to='Profiles.Minor', blank=True)),
                ('student_classes', models.ManyToManyField(related_name='studentuser_student', to='class_overviews.Section', blank=True)),
                ('ta_classes', models.ManyToManyField(related_name='studentuser_ta', to='class_overviews.Section', blank=True)),
                ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
            ],
            options={
                'ordering': ['last_name', 'first_name'],
            },
        ),
        migrations.AddField(
            model_name='department',
            name='department_head',
            field=models.ForeignKey(null=True, blank=True, to='Profiles.Professor'),
        ),
        migrations.AddField(
            model_name='department',
            name='majors',
            field=models.ManyToManyField(to='Profiles.Major', blank=True),
        ),
        migrations.AddField(
            model_name='department',
            name='minors',
            field=models.ManyToManyField(to='Profiles.Minor', blank=True),
        ),
    ]


from argparse import ArgumentParser as parser
from pandas.core.common import flatten
from tabulate import tabulate
import pandas as pd

def parseCli():
    '''
        Function to parse command line arguments
    '''
    cli = parser()
    cli.add_argument('dataSet', help='Path to Training Dataset')
    cli.add_argument("-c", "--conditions", help="states of B G C F (or Bf Gf Cf Ff in that order)")
    cli_args = cli.parse_args()

    return cli_args

def prettyPrintData(data, header):
    '''
        Function to pretty print calculated probability tables
    '''
    print(tabulate(data, headers=header, tablefmt="pipe"))
    print("\n\n\n")

def getCount(dataFrame, columns):
    '''
        Function to count number of instances of all combinations of the given headers
    '''
    counts = dataFrame.groupby(columns).size()                          # Extract dataset into a pandas dataFrame
    counts = pd.DataFrame(counts)                                       # Cleanup dataFrame
    counts = counts.values                                              # Some more cleanup
    count = list(flatten(counts))                                       # Convert them to a flat list

    return count

def getDataFrame(dataSet):
    '''
        Function read the given dataset and to convert it to a Pandas dataFrame
    '''
    columns = ['B', 'G', 'C', 'F']                                  # Set the expected data headers for the dataset
    dataFrame = pd.read_csv(dataSet, delimiter=' ', names=columns)  # Read dataset

    return dataFrame

def validateStates(states_list):
    BGCF = 0

    if (len(states_list) < 4):
        print("ERROR! Insufficient conditions provided")
        exit()

    else:
        if (states_list[3] == 'F'): BGCF = BGCF + (1<<0)
        elif (states_list[3] == 'Ff'): BGCF = BGCF + (0)
        else: print("Fourth argument does not provide information about F")

        if (states_list[2] == 'C'): BGCF = BGCF + (1<<1)
        elif (states_list[2] == 'Cf'): BGCF = BGCF + (0)
        else: print("Fourth argument does not provide information about C")

        if (states_list[1] == 'G'): BGCF = BGCF + (1<<2)
        elif (states_list[1] == 'Gf'): BGCF = BGCF + (0)
        else: print("Fourth argument does not provide information about G")

        if (states_list[0] == 'B'): BGCF = BGCF + (1<<3)
        elif (states_list[0] == 'Bf'): BGCF = BGCF + (0)
        else: print("Fourth argument does not provide information about B")

        return BGCF


import sys  # Library for INT_MAX
from collections import namedtuple

class Graph():

    def __init__(self, vertices):
        self.V = vertices
        self.graph = [[0 for column in range(vertices)]
                      for row in range(vertices)]

        # A utility function to print the constructed MST stored in parent[]

    def printMST(self, parent):
        print("Edge \tWeight")

        for i in range(1, self.V):
            print(parent[i], "-", i, "\t", self.graph[i][parent[i]])

            # A utility function to find the vertex with

    # minimum distance value, from the set of vertices
    # not yet included in shortest path tree
    def minKey(self, key, mstSet):

        # Initilaize min value
        min = sys.maxsize

        for v in range(self.V):
            if key[v] < min and mstSet[v] == False:
                min = key[v]
                min_index = v

        return min_index

    def primMST(self):
        l = []
        weight_edge = namedtuple("weight_edge", "weight edge")
        for i, row in enumerate(self.graph):
            for j, x in enumerate(row):
                if x > 0:
                    l.append(weight_edge(x,(i,j)))
        l.sort()
        res = []
        seen_nodes = set()
        res.append(l[0].edge)
        print(res)
        seen_nodes.add(l[0].edge[0])
        seen_nodes.add(l[0].edge[1])

        print(seen_nodes)
        for w, e in l[1:]:
            count =(1 if  e[0] in seen_nodes else 0) + (1 if e[1] in seen_nodes else 0)
            if count == 1:
                res.append(e)
                seen_nodes.add(e[0])
                seen_nodes.add(e[1])

        print(res)

g = Graph(5)
g.graph = [[0, 2, 0, 6, 0],
           [2, 0, 3, 8, 5],
           [0, 3, 0, 0, 7],
           [6, 8, 0, 0, 9],
           [0, 5, 7, 9, 0]]
#
# 0 - 1    2
# 1 - 2    3
# 0 - 3    6
# 1 - 4    5
g.primMST();


from flask import Flask, render_template, request, redirect, session
app = Flask(__name__)
app.secret_key = 'mySecretKey'
@app.route('/')
def index():
    return render_template('index1.html')

@app.route('/result', methods=['POST'])
def dojo_info():
    session['name'] = request.form['name']
    print (session['name'])
    session['location'] = request.form['location']
    print (session['location'])
    session['language'] = request.form['language']
    print (session['language'])
    session['comment'] = request.form['comment']
    print (session['comment'])

    return redirect('/submit')

@app.route('/submit')
def results():
    return render_template('result.html')

app.run(debug=True, port=9999)


from __future__ import absolute_import, division, print_function

import sys
from collections import namedtuple
import tokenize

if sys.version_info[0] == 2:
    TokenInfo = namedtuple('TokenInfo', 'type string start end line')

    def _generate_tokens(readline):
        return map(lambda x: TokenInfo(*x), tokenize.generate_tokens(readline))

else:
    TokenInfo = tokenize.TokenInfo
    _generate_tokens = tokenize.tokenize

def read(filename):
    """
    Read a regular Python file with special formatting and performance
    preprocessing on it.  The result is a string that conforms to the IPython
    notebook version 3 python script format.
    """
    with open(filename, 'rb') as fin:
        token_gen = _generate_tokens(fin.readline)
        cvt_docstr_gen = convert_toplevel_docstring(token_gen)
        nl_gen = fix_newlines(cvt_docstr_gen)
        out = list(nl_gen)

    formatted = tokenize.untokenize(out).decode('utf-8')
    return fix_empty_lines(formatted)

# =============================================================================
#                                   Helpers
# =============================================================================

def convert_toplevel_docstring(tokens):
    for token in tokens:
        # For each string
        if token.type == tokenize.STRING:
            text = token.string
            # Must be a docstring
            if text.startswith('"""') or text.startswith("'''"):
                startline, startcol = token.start
                # Starting column MUST be 0
                if startcol == 0:
                    endline, endcol = token.end
                    lines = ['# ' + line
                             for line in text.strip('"\' \n').split('\n')]
                    text = '\n'.join(lines)
                    fmt = '# <markdowncell>\n{0}\n# <codecell>'.format(text)
                    yield TokenInfo(type=tokenize.COMMENT,
                                    start=(startline, startcol),
                                    end=(endline, endcol),
                                    string=fmt,
                                    line='#')
                    # To next token
                    continue
        # Return untouched
        yield token

def fix_newlines(tokens):
    first = True
    curline = 1
    for token in tokens:
        if first:
            first = False
            curline = token.end[0] + 1
        else:
            # Fill NEWLINE token in between
            while curline < token.start[0]:
                yield TokenInfo(type=tokenize.NEWLINE,
                                string='\n',
                                start=(curline, 0),
                                end=(curline, 0),
                                line='\n', )
                curline += 1

            curline = token.end[0] + 1
        yield token

def fix_empty_lines(text):
    def gen():
        for line in text.splitlines():
            if not line.strip():
                # Empty line
                yield ''
            else:
                yield line

    return '\n'.join(gen())


import sys
import os
import io

args = sys.argv
if len(args) == 1:
    full_name = "example/ExampleMod"
else:
    full_name = args[1]
owner, repo = full_name.split('/')
package_name = owner.replace("-", "")
main_class_template = """package %PackageName%;

import mindustry.mod.*;

public class %MainClassName% extends Mod {

}
"""
readme_template = """<div align = center>

# %Repository%

![Mod Icon](icon.png)

</div>

## Modding ToDo List:

- [x] Create a new Mindustry Java mod project.
- [ ] Edit the [mod.hjson](mod.hjson) for your mod.
- [ ] Create your first content: a block or an item.
- [ ] Put sprites and bundles into [assets](assets) directory.
- [ ] Check the Mindustry's sources with your IDE or on its [repository](https://github.com/Anuken/Mindustry).
- [ ] Make a nice [icon](icon.png) to replace the placeholder.
- [ ] Push a commit with any message containing `[release]` to generate a release draft on [GitHub](https://github.com/%Owner%/%Repository%/releases).
"""

def transform_repo_to_class_name():
    with io.StringIO() as b:
        hyphen = False
        for i, c in enumerate(repo):
            if c == '-':
                hyphen = True
            else:
                if hyphen:
                    b.write(c.upper())
                    hyphen = False
                else:
                    if i == 0:
                        b.write(c.upper())
                    else:
                        b.write(c)
        name: str = b.getvalue()
        if name.endswith("Mod"):
            return name[:-len("Mod")]
        return name

main_class_name = transform_repo_to_class_name() + "Mod"
main_class_qualified_name = f"{package_name}.{main_class_name}"

def replace_mod_meta():
    with open("mod.hjson", mode='r') as mod_meta:
        text = mod_meta.read()
    text = text.replace(
        "%Repository%", repo
    ).replace(
        "%Owner%", owner
    ).replace(
        "%MainClassQualifiedName%", main_class_qualified_name
    )
    with open("mod.hjson", mode='w') as mod_meta:
        mod_meta.write(text)

def generate_main_class():
    os.makedirs(f"src/{package_name}", exist_ok=True)
    with open(f"src/{package_name}/{main_class_name}.java", mode='w+') as main_clz:
        content = main_class_template.replace(
            "%PackageName%", package_name
        ).replace(
            "%MainClassName%", main_class_name
        )
        main_clz.write(content)

def replace_readme():
    text = readme_template.replace(
        "%Repository%", repo
    ).replace(
        "%Owner%", owner
    )
    with open("README.md", mode='w') as readme:
        readme.write(text)

def delete_self():
    os.remove(".github/workflows/CleanUpTemplate.yml")
    os.remove("LICENSE")
    os.remove("cleanup.py")

def main():
    replace_mod_meta()
    generate_main_class()
    replace_readme()
    delete_self()

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""Annotators for detecting keypoints.
"""

import numpy as np
import torch
from torchvision import transforms as T

from .utils import _download_file

class AnnoKeypoints:
    """Detect body keypoints in an input image.

    Uses the KeypointRCNN weights trained on MS COCO, as provided by
    the PyTorch model zoo.

    Attributes:
        model_path (str): location of the saved pytorch weights; set to
            None to download from the DVT repository.
    """
    def __init__(self, model_path=None):
        if not model_path:
            model_path = _download_file("dvt_detect_keypoint.pt")
        self.model = torch.jit.load(model_path)
        self.model.eval()

    def run(self, img: np.ndarray, visualize=False) -> dict:
        """Run the annotator.

        Args:
            img (np.ndarray): image file, as returned by dvt.load_image,
                or dvt.yield_video (for frame from a video file).
            visualize (bool): should a visualization of the output be
                returned as well? Default is False.

        Returns:
            A dictionary giving the annotation.
        """
        img_t = np.transpose(img, (2, 0, 1))
        img_t = torch.Tensor(img_t / 255).unsqueeze(0)

        # Detect the keypoints
        kpnt = self.model(img_t)
        if not len(kpnt):
            return None
        kpnt = [x.detach().numpy() for x in kpnt]

        # Clean up the results
        npeople = kpnt[0].shape[0]
        output = {"person_id": np.repeat(np.arange(npeople), 17),
                  "kpnt_id": np.tile(np.arange(17), npeople),
                  "x": np.int32(kpnt[3][:, :, 0].flatten()),
                  "y": np.int32(kpnt[3][:, :, 1].flatten()),
                  "prob": np.repeat(kpnt[2], 17)}

        # visualize?
        if visualize:
            img_out = img.copy()
            yellow = [250, 189, 47]
            for index in range(npeople):
                for idk in range(17):
                    if kpnt[2][index] > 0.5:
                        img_out[
                            int(kpnt[3][index, idk, 1]),
                            int(kpnt[3][index, idk, 0])
                        ] = yellow
        else:
            img_out = None

        return {"kpnt": output, "img": img_out}


from django.urls import reverse_lazy, reverse
from django.http import HttpResponseRedirect
from django.views.generic import TemplateView
from django.db.models import Q
from core.views.mydatatableview import MyDatatable, columns, helpers
from core.views import crud
from core.models import Aide, CombiAide
from fiche_famille.forms.famille_aides import Formulaire, FORMSET_COMBI, Formulaire_selection_activite
from fiche_famille.views.famille import Onglet

class Page(Onglet):
    model = Aide
    url_liste = "famille_aides_liste"
    url_ajouter = "famille_aides_ajouter"
    url_modifier = "famille_aides_modifier"
    url_supprimer = "famille_aides_supprimer"
    description_liste = "Saisissez ici les aides de la famille."
    description_saisie = "Saisissez toutes les informations concernant l'aide et cliquez sur le bouton Enregistrer."
    objet_singulier = "une aide"
    objet_pluriel = "des aides"

    def get_context_data(self, **kwargs):
        """ Context data spécial pour onglet """
        context = super(Page, self).get_context_data(**kwargs)
        if not hasattr(self, "verbe_action"):
            context['box_titre'] = "Aides"
        context['onglet_actif'] = "aides"
        context['boutons_liste'] = [
            {"label": "Ajouter", "classe": "btn btn-success", "href": reverse_lazy(self.url_ajouter, kwargs={'idfamille': self.kwargs.get('idfamille', None)}), "icone": "fa fa-plus"},
        ]
        return context

    def get_form_kwargs(self, **kwargs):
        """ Envoie l'idfmille au formulaire """
        form_kwargs = super(Page, self).get_form_kwargs(**kwargs)
        form_kwargs["idfamille"] = self.Get_idfamille()
        form_kwargs["idactivite"] = self.kwargs.get("idactivite", None)
        form_kwargs["idmodele"] = self.kwargs.get("idmodele", None)
        return form_kwargs

    def get_success_url(self):
        """ Renvoie vers la liste après le formulaire """
        url = self.url_liste
        if "SaveAndNew" in self.request.POST:
            url = self.url_ajouter
        return reverse_lazy(url, kwargs={'idfamille': self.kwargs.get('idfamille', None)})

class Liste(Page, crud.Liste):
    model = Aide
    template_name = "fiche_famille/famille_liste.html"

    def get_queryset(self):
        return Aide.objects.select_related("caisse", "activite", "activite__structure").prefetch_related("individus").filter(Q(famille=self.Get_idfamille()) & self.Get_filtres("Q"))

    def get_context_data(self, **kwargs):
        context = super(Liste, self).get_context_data(**kwargs)
        context['impression_introduction'] = ""
        context['impression_conclusion'] = ""
        return context

    class datatable_class(MyDatatable):
        filtres = ["idaide", "date_debut", "date_fin", "caisse__nom", "activite__nom"]
        beneficiaires = columns.TextColumn("Bénéficiaires", sources=None, processor='Get_beneficiaires')
        caisse = columns.TextColumn("Caisse", sources=["caisse__nom"])
        activite = columns.TextColumn("Activité", sources=["activite__nom"])
        actions = columns.TextColumn("Actions", sources=None, processor='Get_actions_speciales')

        class Meta:
            structure_template = MyDatatable.structure_template
            columns = ["idaide", 'date_debut', 'date_fin', 'beneficiaires', 'caisse', 'activite']
            processors = {
                'date_debut': helpers.format_date('%d/%m/%Y'),
                'date_fin': helpers.format_date('%d/%m/%Y'),
            }
            ordering = ['date_debut']

        def Get_beneficiaires(self, instance, *args, **kwargs):
            return ", ".join([individu.Get_nom() for individu in instance.individus.all()])

        def Get_actions_speciales(self, instance, *args, **kwargs):
            """ Inclut l'idindividu dans les boutons d'actions """
            view = kwargs["view"]
            kwargs = view.kwargs
            kwargs["pk"] = instance.pk
            if instance.activite.structure in view.request.user.structures.all():
                # Affiche les boutons d'action si l'utilisateur est associé à l'aide
                html = [
                    self.Create_bouton_modifier(url=reverse(view.url_modifier, kwargs=kwargs)),
                    self.Create_bouton_supprimer(url=reverse(view.url_supprimer, kwargs=kwargs)),
                ]
            else:
                # Afficher que l'accès est interdit
                html = ["Accès interdit",]
            return self.Create_boutons_actions(html)

class ClasseCommune(Page):
    form_class = Formulaire
    template_name = "parametrage/activite_edit.html"

    def get_context_data(self, *args, **kwargs):
        context_data = super(ClasseCommune, self).get_context_data(**kwargs)
        context_data["modeles"] = Aide.objects.filter(famille__isnull=True)

        # Traitement du Combi aides
        if "idactivite" in self.kwargs:
            activite = self.kwargs["idactivite"]
        else:
            activite = self.object.activite.idactivite

        if self.request.POST:
            context_data['formset_combi'] = FORMSET_COMBI(self.request.POST, instance=self.object, form_kwargs={'activite': activite})
        else:
            context_data['formset_combi'] = FORMSET_COMBI(instance=self.object, form_kwargs={'activite': activite})

            # Importation du modèle d'aide
            idmodele = self.kwargs.get("idmodele", None)
            if idmodele:
                modele_aide = Aide.objects.get(pk=idmodele)
                combinaisons = CombiAide.objects.filter(aide=modele_aide)
                context_data['formset_combi'].initial = [{"montant": combi.montant, "unites": combi.unites.all()} for combi in combinaisons]
                context_data['formset_combi'].extra = max(combinaisons.count() - 1, 0)

        return context_data

    def form_valid(self, form):
        if "idactivite" in self.kwargs:
            activite = self.kwargs["idactivite"]
        else:
            activite = self.object.activite.idactivite
        formset_combi = FORMSET_COMBI(self.request.POST, instance=self.object, form_kwargs={'activite': activite})
        if formset_combi.is_valid() == False:
            return self.render_to_response(self.get_context_data(form=form))

        # Sauvegarde de l'aide
        self.object = form.save()

        # Sauvegarde des combi
        if formset_combi.is_valid():
            for formline in formset_combi.forms:
                if formline.cleaned_data.get('DELETE') and form.instance.pk:
                    formline.instance.delete()
                if formline.cleaned_data.get('DELETE') == False:
                    instance = formline.save(commit=False)
                    instance.aide = self.object
                    instance.save()
                    formline.save_m2m()

        return super().form_valid(form)

class Ajouter(ClasseCommune, crud.Ajouter):
    form_class = Formulaire
    template_name = "fiche_famille/famille_edit.html"

class Modifier(ClasseCommune, crud.Modifier):
    form_class = Formulaire
    template_name = "fiche_famille/famille_edit.html"

class Supprimer(Page, crud.Supprimer):
    template_name = "fiche_famille/famille_delete.html"

class Selection_activite(Onglet, TemplateView):
    template_name = "fiche_famille/famille_edit.html"

    def get_context_data(self, **kwargs):
        context = super(Selection_activite, self).get_context_data(**kwargs)
        if not hasattr(self, "verbe_action"):
            context['box_titre'] = "Aides"
        context['box_introduction'] = "Vous devez commencer par sélectionner l'activité associée à l'aide que vous souhaitez créer. Vous pouvez éventuellement sélectionner un modèle d'aide existant à appliquer."
        context['onglet_actif'] = "aides"
        context['form'] = Formulaire_selection_activite(request=self.request)
        return context

    def post(self, request, **kwargs):
        idactivite = int(request.POST.get("activite"))
        idmodele = int(request.POST.get("modele_aide")) if request.POST.get("modele_aide") else 0
        return HttpResponseRedirect(reverse_lazy("famille_aides_configurer", args=(self.Get_idfamille(), idactivite, idmodele)))

    def Get_annuler_url(self):
        return reverse_lazy("famille_aides_liste", kwargs={'idfamille': self.kwargs.get('idfamille', None)})


from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render

from .forms import PostForm
from .models import Group, Post, User
from .utils import get_page_pages

def index(request):
    """Выводит шаблоны главной страницы."""
    context = get_page_pages(
        Post.objects.select_related('author', 'group'), request)
    return render(request, 'posts/index.html', context)

def group_posts(request, slug):
    """Выводит шаблон с группами постов."""
    group = get_object_or_404(Group, slug=slug)
    context = {
        'group': group,
    }
    context.update(get_page_pages(
        group.posts.select_related('author', 'group'), request))
    return render(request, 'posts/group_list.html', context)

def profile(request, username):
    """Выводит шаблон профайла пользователя."""
    author = get_object_or_404(User, username=username)
    context = {
        'author': author,
    }
    context.update(get_page_pages(
        author.posts.all(), request))
    return render(request, 'posts/profile.html', context)

def post_detail(request, post_id):
    """Выводит информацию о посте."""
    post = get_object_or_404(Post.objects.select_related(
    ), id=post_id)
    context = {
        'post': post,
    }
    return render(request, 'posts/post_detail.html', context)

@ login_required
def post_create(request):
    """Создания новго поста."""
    form = PostForm(request.POST or None)
    if form.is_valid():
        post = form.save(commit=False)
        post.author = request.user
        form.save()
        return redirect('posts:profile', request.user)
    return render(request, 'posts/post_create.html', {'form': form})

@ login_required
def post_edit(request, post_id):
    """Редактирование поста."""
    post = get_object_or_404(Post, id=post_id)
    is_edit = True
    if post.author != request.user:
        return redirect('posts:post_detail', post.pk)
    form = PostForm(request.POST or None, instance=post)
    if form.is_valid():
        form.save()
        return redirect('posts:post_detail', post_id=post_id)
    return render(request, 'posts/post_create.html', {'form': form,
                                                      'is_edit': is_edit})


## https://github.com/python/cpython/blob/main/Lib/http/server.py

from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from os import getcwd
from os.path import isdir, join

working_directory = getcwd()
_data_directory   = join(working_directory, 'data')
if isdir(_data_directory):
  working_directory = _data_directory

def get_md5(file):
  from hashlib import md5
  from base64 import b64encode
  hash = md5()
  try:
    with open(file,'rb') as f:
      while True:
        buffer = f.read1()
        if len(buffer)==0:
          break
        hash.update(buffer)
    return b64encode(hash.digest())
  except BaseException as err:
    return None

class CustomHTTPRequestHandler(SimpleHTTPRequestHandler):

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

## https://www.ietf.org/rfc/rfc2068.txt
##   Messages MUST NOT include both a Content-Length header field and the
##   "chunked" transfer coding. If both are received, the Content-Length
##   MUST be ignored.

  def chunked_handler(self,save_f):
    raise NotImplementedError('"handling for chunked" transfer coding is not implemented')

  def contentlength_handler(self,length,save_f):
    rfile      = self.rfile
    pretimeout = self.connection.gettimeout()
    print('  Content-Length: {}'.format(length))
    try:
      self.connection.settimeout(3)
      remain_length = int(length)
      while remain_length > 0:
        buffer = rfile.read1()
        read_length=len(buffer)
        if read_length==0:
          break
        save_f.write(buffer)
        remain_length -= read_length
      print('    remaining {}'.format(remain_length))
    except BaseException as err:
      self.connection.settimeout(pretimeout)
      raise err

  def do_PUT(self):
    from http import HTTPStatus ## https://docs.python.org/3/library/http.html#http-status-codes
    from socket import timeout
    from urllib.parse import urlparse
    from re import fullmatch
    o = urlparse(self.path)
    if self.directory != _data_directory or not isdir(self.directory):
      self.send_response(HTTPStatus.FORBIDDEN)
      self.end_headers()
      return
    if o.path != '/upload/':
      self.send_response(HTTPStatus.FORBIDDEN)
      self.end_headers()
      return
    if fullmatch(r'[0-9a-zA-Z._-]+',o.query) is None:
      self.send_response(HTTPStatus.BAD_REQUEST)
      self.end_headers()
      return
    filename = o.query
    filename = join(self.directory, filename)
    length   = self.headers.get('content-length')
    tran_enc = self.headers.get('transfer-encoding','').split(r',[ ]*')
    md5_b64  = self.headers.get('content-md5')
    read_type = 'chunked'
    if 'chunked' in tran_enc:
      read_type = 'chunked'
    elif length is not None:
      read_type = 'length'
    else:
      self.send_response(HTTPStatus.BAD_REQUEST)
      self.end_headers()
      return
    print('writing to {}'.format(filename))
    try:
      with open(filename,'wb') as f:
        if read_type == 'chunked':
          self.chunked_handler(f)
        elif read_type == 'length':
          self.contentlength_handler(length,f)
      if md5_b64 is not None:
        print('  Content-MD5: {}'.format(md5_b64))
        md5_check = get_md5(filename).decode()
        print('  File-MD5   : {}'.format(md5_check))
        if md5_b64 != md5_check:
          from os import remove
          print('    failed md5 check, removing {}'.format(filename))
          remove(filename)
          raise OSError()
      self.send_response(HTTPStatus.OK)
    except timeout as err:
      self.send_response(HTTPStatus.BAD_REQUEST)
    except NotImplementedError as err:
      self.send_response(HTTPStatus.NOT_IMPLEMENTED)
    except OSError as err:
      self.send_response(HTTPStatus.BAD_REQUEST)
      ## The Content-MD5 or checksum value that you specified did not match what the server received.
      ## https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
    except BaseException as err:
      self.send_response(HTTPStatus.FORBIDDEN)
    finally:
      self.end_headers()

class DualStackServer(ThreadingHTTPServer):

  def server_bind(self):
    # suppress exception when protocol is IPv4
    from contextlib import suppress
    from socket import IPPROTO_IPV6, IPV6_V6ONLY # For gethostbyaddr()
    with suppress(Exception):
      self.socket.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0)
    return super().server_bind()

    def finish_request(self, request, client_address):
      self.RequestHandlerClass(request, client_address, self, directory=working_directory)

def main():
  from http.server import _get_best_family
  from sys import exit

  HandlerClass = CustomHTTPRequestHandler
  ServerClass  = DualStackServer
  protocol = "HTTP/1.0"
  port     = 8000
  bind     = None

  ServerClass.address_family, addr = _get_best_family(bind, port)
  HandlerClass.protocol_version = protocol
  with ServerClass(addr, HandlerClass) as httpd:
    host, port = httpd.socket.getsockname()[:2]
    url_host = f'[{host}]' if ':' in host else host
    print(
      f"Serving HTTP on {host} port {port} "
      f"(http://{url_host}:{port}/) ..."
    )
    try:
      httpd.serve_forever()
    except KeyboardInterrupt:
      print("\nKeyboard interrupt received, exiting.")
      exit(0)

if __name__ == '__main__':
  main()


from django.urls import path
from .views import *
urlpatterns = [
    path('Cadastro/', Cadastrar_usuarios,name = 'Cadastro'),
    path('Login/',Login,name = 'Login'),
    path('MinhaConta/',Minha_Conta,name = 'Minha_Conta'),
    path('MeusDados/',Meus_Dados,name = 'Meus_Dados'),
    path('ResetarSenha/',Resetar,name = 'Resetar_Senha'),
    path('AlterarSenha/',AlterarSenha,name = 'Alterar_Senha'),
    path('Logout/',Logout,name = 'Logout')
]