#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Run 1st, 2nd or 3rd level analyses using FSL FEAT.

First, fsf files are created based on template fsf files (one per analysis).
For each analysis from these templates one fsf file is created for each
participant and run (1st level only).
Analyses are defined in analysis_definitions_* dicts, including task and output
space definitions.
    analysis_definitions_1stLevel: analysis definitons for first level feat
    analyses (run level)
    analysis_definitions_2ndLevel: analysis definitons for second level feat
    analyses (subject level)
    analysis_definitions_3rdLevel: analysis definitons for third level feat
    analyses (group level)

Second, feat analyses are submitted as separate jobs using the created fsf
files.

Assumed are:
    1. a bids compliant dataset preprocessed with fmriprep, following default
    fmriprep output folder structures and filenames
    2. fsf templates per analysis/task type and output space (e.g.
    sub-PXX_DurXX_analysis-2ndQC_space-T1w.fsf)
        - fsf templates are further assumed to contain place holders for:
        A: subject IDs (sub-PXX),
        B: number of volumes (XXX_NTPS; only 1st level analyses)
        C: task/run place holders that are task specific (e.g. DurXX for
        task-Dur of exp.1)
        D: analysis space place holder (space-SXX) defining the analysis space
        (e.g. T1w, MNI152NLin2009cAsym)
        Analysis labels + suffix must correspond to the labels defined in
        analysis_definitions_* below.

Handles 2nd level feat analyses for 1st level feat dirs which are already
normalized/registered to the desired template (MNI or T1); i.e., bypasses
2nd level normalization usually run by feat. Note: it is essential that
1st level feat dirs are NOT modified by parallel 2nd level analyses! Use
time_wait_after_analysis to set a delay before submitting subsequent 2nd level
analyses.
Also handles 2nd level feat analyses which may involve contrasts that do not
contain trials of that contrast in each 1st level analysis; i.e. removes
these feat dirs from 2nd level analysis, if so specificied (missingCon).
This option is particularly relevant for the task-independent visual processing
analysis as it requires analysis of target trials per stimulus category which
do not contain at least 1 trial in each run. Hence these analyses have to be
defined as separate 2nd level analyses with the missingCon option enabled.

Group level (3rd level) feat analyses can be defined for many copes of the same
2nd level gfeat. Applicable subjects must (currently) be defined in fsf
template.

Additional custom group level (3rd level) feat analysis option added for
task-independent visual processing analysis with special fsf file handling.

Requires FSL functions!
I.e., in the shell run, before executing the python script:
module load FSL

Created on Fri Mar 29 15:11:06 2021

@author: David Richter, Yamil Vidal

"""

import os, time, sys
import pandas as pd

#%% Paths and Parameters

# BIDS path
bids_dir = '/mnt/beegfs/XNAT/COGITATE/fMRI/phase_2/processed/bids'
# FSL output path
fsl_output_dir = bids_dir + '/derivatives/fslFeat'

# Determine which analyses are run:
# Set to 1 to run 1st level analyses; i.e. run level (defined in analysis_definitions_1stLevel)
# Set to 2 to run 2nd level analyses; i.e. across runs (defined in analysis_definitions_2ndLevel)
# Set to 3 to run 3nd level analyses; i.e. across subjects (defined in analysis_definitions_3rdLevel)
run_analysis = 3

# Whether to submit feat jobs using qsub. If False only fsf files are created
submit_jobs = True

# Optional wait time between submitting successive jobs. This can be useful to
# avoid too many parallel jobs due to FEAT's large temporary files, if storage
# space is a concern
wait_between_jobs_in_sec = 600

# Wait time after successive analyses. Useful for 2nd level analyses modifying
# the same 1st level feat dir (i.e. for 2nd level analyses where this applies;
# this should be at least the duration of the 2nd level
# analyses to finish before proceeding!)
time_wait_after_analysis = 600

# Which subject list to use (set to 'all' to process all availabel participants; 'phase2_2.3' to process only those of phase 2 2/3 (list from DMT)))
subject_list_type = 'phase3_V1'
#subject_list_type = 'phase2_V1'
#subject_list_type = 'debug'

# load helper functions / code dir
code_dir_with_helperfunctions = bids_dir + '/code'
sys.path.append(code_dir_with_helperfunctions)
from helper_functions_MRI import get_subject_list

#%% Define analyses to be performed !
"""
Dict per analysis, specifying analysis label, suffix and number of runs.
Analysis labels must match the fsf file template names!

desc: (optional) description of analysis
label: determines the analysis label, including session (ses-xx) and task
    (e.g. task-Dur) key-value pairs to be analyzed
suffix: suffix for the analysis label; should describe the type of analysis
    (e.g. QC = quality control) and space (e.g. T1w = T1 weighted space)
    key-value pair. Space has to correspond to an output space of fmriprep
    2nd level analyses are assumed to be flagged by "analysis-2nd..." key/value
    THIS DETERMINES WHETHER AN ANALYSIS IS CONSIDERED TO BE 1st OR 2nd LEVEL!
fsf_file: path to the fsf file templates (assumed to be located in bids dir).
    The fsf file templates must match the analysis label + suffix
runs: number of runs of this type. Note: 2nd level analyses do not have a run
    key/value pair!
copes: cope labels/numbers used exclusively for 3rd (group) level analyses.
walltime: hours required to process run (submitted as job)
memory: gb memory required to process run (submitted as job)
missingCon: (optional) whether to check 1st level contrasts for runs with
    missing data (e.g. some exp1 runs do not contain target face or target
    letter trials) and if applicable remove these runs from 2nd level analysis
    (required by feat). Only applies for 2nd level analyses.
"""

# First level analyses (run level)
analysis_definitions_1stLevel = {
        # 'ROI_EVCLoc_T1w': {
        # 'desc'      : '1st level ROI definition analysis for EVC localizer exp2 data',
        # 'label'     : 'ses-V2_task-EVCLoc_run-',
        # 'suffix'    : 'analysis-1stROI_space-T1w',
        # 'runs'      : 1,
        # 'fsf_file'  : '/code/glm/fsf_templates/1st_level',
        # 'walltime'  : 3,
        # 'memory'    : 3
        # },
        'GLM_Dur_MNI': {
        'desc'      : '1st level GLM for exp 1',
        'label'     : 'ses-V1_task-Dur_run-',
        'suffix'    : 'analysis-1stGLM_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/1st_level',
        'walltime'  : 8,
        'memory'    : 8
        }
        }

# Second level analyses (subject level)
analysis_definitions_2ndLevel = {
        'GLM_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_TarFace_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (only target face contrast; separate processing necessary since some runs do not contain trials for this contrast, hence must be excluded from 2nd level in feat)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_TarFace_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8,
        'missingCon': 1,
        },
        'GLM_TarObject_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (only target object contrast; separate processing necessary since some runs do not contain trials for this contrast, hence must be excluded from 2nd level in feat)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_TarObject_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8,
        'missingCon': 1,
        },
        'GLM_TarLetter_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (only target letter contrast; separate processing necessary since some runs do not contain trials for this contrast, hence must be excluded from 2nd level in feat)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_TarLetter_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8,
        'missingCon': 1,
        },
        'GLM_TarFalseFont_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (only target false font contrast; separate processing necessary since some runs do not contain trials for this contrast, hence must be excluded from 2nd level in feat)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_TarFalseFont_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8,
        'missingCon': 1,
        },
        'GLM_leave_run1_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 1 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run1_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run2_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 2 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run2_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run3_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 3 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run3_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run4_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 4 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run4_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run5_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 5 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run5_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run6_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 6 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run6_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run7_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 7 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run7_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        },
        'GLM_leave_run8_out_Dur_MNI': {
        'desc'      : '2nd level GLM for exp 1 (all contrasts, leaving run 8 out)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-2ndGLM_leave_run8_out_space-MNI152NLin2009cAsym',
        'runs'      : 8,
        'fsf_file'  : '/code/glm/fsf_templates/2nd_level',
        'walltime'  : 8,
        'memory'    : 8
        }
        }

# Third level analyses (group level)
analysis_definitions_3rdLevel = {
        'GLM_Dur_MNI': {
        'desc'      : '3rd (group) level GLM for exp 1 (all contrasts, except for target stimuli)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-3rdGLM_space-MNI152NLin2009cAsym',
        'copes'     : ['5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24'],
        'fsf_file'  : '/code/glm/fsf_templates/3rd_level',
        'walltime'  : 3,
        'memory'    : 8
        },
        'GLM_TarFace_Dur_MNI': {
        'desc'      : '3rd (group) level GLM for exp 1 (only target face contrast)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-3rdGLM_TarFace_space-MNI152NLin2009cAsym',
        'copes'     : ['1'],
        'fsf_file'  : '/code/glm/fsf_templates/3rd_level',
        'walltime'  : 3,
        'memory'    : 8
        },
        'GLM_TarObject_Dur_MNI': {
        'desc'      : '3rd (group) level GLM for exp 1 (only target object contrast)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-3rdGLM_TarObject_space-MNI152NLin2009cAsym',
        'copes'     : ['2'],
        'fsf_file'  : '/code/glm/fsf_templates/3rd_level',
        'walltime'  : 3,
        'memory'    : 8
        },
        'GLM_TarLetter_Dur_MNI': {
        'desc'      : '3rd (group) level GLM for exp 1 (only target letter contrast)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-3rdGLM_TarLetter_space-MNI152NLin2009cAsym',
        'copes'     : ['3'],
        'fsf_file'  : '/code/glm/fsf_templates/3rd_level',
        'walltime'  : 3,
        'memory'    : 8
        },
        'GLM_TarFalseFont_Dur_MNI': {
        'desc'      : '3rd (group) level GLM for exp 1 (only target false font contrast)',
        'label'     : 'ses-V1_task-Dur',
        'suffix'    : 'analysis-3rdGLM_TarFalseFont_space-MNI152NLin2009cAsym',
        'copes'     : ['4'],
        'fsf_file'  : '/code/glm/fsf_templates/3rd_level',
        'walltime'  : 3,
        'memory'    : 8
        }
        }

# get analysis definitions
if run_analysis == 1:
    analysis_definitions = analysis_definitions_1stLevel
elif run_analysis == 2:
    analysis_definitions = analysis_definitions_2ndLevel
elif run_analysis == 3:
    analysis_definitions = analysis_definitions_3rdLevel

# %% Functions returning analysis names/types and space and paths
def get_fmriprep_processed_nifti_file(bids_dir, sub, session_label, analysis, space):
    """
    get nifti file name, given path + pattern (defined here), and input parameters
    bids_dir = bids directory
    sub: subject ID (e.g. SC101)
    session_label: session label ("key-value" pair; e.g. ses-V1)
    analysis: analysis label (e.g. task-Dur)
    space: analysis space (e.g. T1w)
    Returns nifti file path+filename
    """
    # nifti data path
    nifti_path_pattern = bids_dir + os.sep + 'derivatives' + os.sep + 'fmriprep' + os.sep + '%(sub)s' + os.sep + '%(ses)s' + os.sep + 'func' + os.sep
    # nifti file suffix (appended after subject ID and analysis/task label)
    nifti_file_pattern = '%(sub)s_%(analysis)s_%(space)s_desc-preproc_bold.nii.gz'
    nifti_file = nifti_path_pattern%{'sub':sub, 'ses':session_label} + nifti_file_pattern%{'sub':sub, 'analysis':analysis, 'space':space}
    return nifti_file

def get_analysis_labels(analysis_dict):
    """
    combine analysis label, analysis suffix and run numbers to for analyses
    labels per run (if 1st level). Only return analysis label + suffix for
    higher (2nd level) analyses.
    Returns:
    analyses: analysis labels
    analyses_suffix: analysis suffix
    fsf_templates_dir: path to fsf template
    run_missing_contrast_check: whether or not to run missing contrast check before 2nd level
    """
    analyses = []
    analyses_suffix = analysis_dict['suffix']
    fsf_templates_dir = bids_dir + analysis_dict['fsf_file']
    # process first level (i.e. not 2nd or 3rd level) analyses
    if 'analysis-1st' in analyses_suffix:
        for run in range(analysis_dict['runs']):
            analyses.append(analysis_dict['label'] + str(run+1))
    # process 2nd level analyses
    else:
        analyses.append(analysis_dict['label'])
    # check if missingCon key exists (indicating that additional processing before 2nd level is required)
    run_missing_contrast_check = 'missingCon' in analysis_dict
    return analyses, analyses_suffix, fsf_templates_dir, run_missing_contrast_check

def write_error_log(bids_dir, error_df):
    """
    write log file listing possible errors encountered during job submission
    """
    timestr = time.strftime("date-%Y%m%d_time-%H%M%S")
    file_name = os.sep + 'log_job_submission_' + str(run_analysis) + '-level_' + timestr + '.csv'
    output_dir = bids_dir + os.sep + 'derivatives' + os.sep + 'fslFeat'
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    fname = output_dir + file_name
    error_df.to_csv(fname, sep=',', index=False, na_rep='Null')
    print('Error saved to ' + output_dir)

#%% functions to modify fsf templates
def replace_text(fname, string_to_replace, replacement_string):
    """
    replace text (string_to_replace) with new string (replacement_string) in
    input file (fname)
    """
    import fileinput
    with fileinput.FileInput(fname, inplace=True) as file:
        for line in file:
            print(line.replace(string_to_replace, replacement_string), end='')

def get_n_volumes(nifti_file):
    """
    get number of volumes in input nifti file
    returns number of volumes (v_vols)
    """
    import subprocess
    full_cmd = ['fslhd', nifti_file]
    # run fslhd to get number of volumes
    result = subprocess.run(full_cmd, stdout=subprocess.PIPE)
    fslHd_output = result.stdout.decode('utf-8')
    # extract only n vols (dim4)
    dim4_target_string = '\ndim4\t\t'
    n_vols = fslHd_output[fslHd_output.find(dim4_target_string)+len(dim4_target_string):fslHd_output.find('\ndim5')]
    return n_vols

def make_this_fsf_template(fsf_file, sub, analysis, nifti_file, place_holder, space):
    """
    Take fsf template and replace subject, analysis and number of volumes
    place holders with args below.
    Subject ID place holder is assumed to be PXX number of volumes XXX_NTPS and
    analysis place holders are the task key's value + XX.
    fsf_file: fsf file name
    sub: subject ID (can be empty for 3rd level analyses). Assumed to contain
    sub key-value pair
    analysis: analysis label or cope label (for 3rd level analyses)
    nifti_file: nifti file
    place_holder: string of analysis place holder in template
    space: target analysis space (key-value pair; e.g. space-T1w)
    """
    print(' . Creating fsf files for analysis: ' + analysis)
    # replace PXX in fsf template with subject ID
    replace_text(fsf_file, 'PXX', sub[4::])
    # replace XXX_NTPS in fsf template with number of volumes
    if not nifti_file == 'is_2nd_level':
        n_vols = get_n_volumes(nifti_file)
        replace_text(fsf_file, 'XXX_NTPS', n_vols)
    # replace run/analysis place holder with analysis
    replace_text(fsf_file, place_holder, analysis)
    # replace analysis space place holder with target space
    replace_text(fsf_file, 'space-SXX', space)

# %% function to modify 2nd level fsf template in case of missing contrasts in first level feat dirs, necessary if some contrasts have no trails in some runs (e.g. Dur Targets)
def adjust_fsf_template_for_missing_contrasts(fsf_file):
    """
    Adjust 2nd level feat fsf file such that runs with empty contrasts are
    removed from analysis. First gets list of 1st level dirs and list of
    contrast labels. Then checks for the current contrast of interest (defined
    by the fsf_file namen) whether any runs have an empty contrast of interest.
    If so removes these runs from the 2nd level contrast definitions in the fsf
    file. Note: the 2nd level fsf template must contain the same label for the
    contrast of interest as the 1st level contrast name. Thus, if the contrast
    on the first level is called TarFace (experiment 1, target, face) then the
    2nd level fsf file template must contain TarFace in its filename.
    fsf_file: path and fname of fsf file to be edited
    """
    # get first level feat dirs
    feat_1st_level_dirs = get_1st_level_feat_dirs_from_fsf_file(fsf_file)
    # get contrast labels
    contrast_labels = get_contrast_labels(feat_1st_level_dirs[0])
    # get currently relevant contrast based on contrast labels (from 1st level feat contrast names) and 2nd level fsf file name. Note: fsf template file names must contain the exact contrast name as specified in the first level feat analysis for the contrast of interest
    for con_no, con_label in contrast_labels.items():
        if con_label[0] in fsf_file:
            contrast_idx = con_no-1
    # get which runs for current contrast of interest have empty contrasts
    empty_runs = check_if_contrast_is_empty(feat_1st_level_dirs, contrast_idx)
    # remove empty runs from list of 1nd level fsf files
    valid_1st_level_dirs = get_only_valid_1st_level_feat_dirs(feat_1st_level_dirs, empty_runs)
    # set correct number of valid runs in fsf file
    n_valid_runs = str(len(valid_1st_level_dirs))
    replace_text(fsf_file, 'XXX_NRUNS', n_valid_runs)
    # replace all feat paths in 2nd level fsf file with place holders (to be replaced later)
    run_counter = 0
    for feat_dir in feat_1st_level_dirs:
        run_counter += 1
        replace_text(fsf_file, feat_dir, 'XXX_RUN'+str(run_counter))
    # replace place holders with valid runs
    run_counter = 0
    for valid_feat_dir in valid_1st_level_dirs:
        run_counter += 1
        replace_text(fsf_file, 'XXX_RUN'+str(run_counter), valid_feat_dir)
    # disable other references to invalid runs (technically not necessary as feat will ignore these, but it results in a cleaner design.fsf file)
    cleanup_fsf_file_with_missing_contrasts(fsf_file,feat_1st_level_dirs,valid_1st_level_dirs)

def cleanup_fsf_file_with_missing_contrasts(fsf_file,feat_1st_level_dirs,valid_1st_level_dirs):
    """
    Perform some additional cleanup on 2nd level fsf files with missing
    contrasts by prepending # to unnecessary rows in the fsf file.
    fsf_file: path and fname of fsf file to be edited
    feat_1st_level_dirs: list of 1st level feat dirs
    valid_1st_level_dirs: list of only valid 1st level feat dirs (i.e. without)
    missing contrasts
    """
    runs_to_disable = len(feat_1st_level_dirs) - len(valid_1st_level_dirs)
    for run_idx in range(runs_to_disable):
        run_no = (len(feat_1st_level_dirs)-run_idx)
        # replace 3 target strings that are dependent on the number of runs
        # each string is prepended with a # to denote a comment and the first
        # letter is removed to avoid accidental string finds looking e.g. for
        # the run data
        string_to_replace = 'set fmri(groupmem.' + str(run_no) + ') 1'
        replace_text(fsf_file, string_to_replace, '# '+string_to_replace[1::])
        string_to_replace = 'set fmri(evg' + str(run_no) + '.1) 1'
        replace_text(fsf_file, string_to_replace, '# '+string_to_replace[1::])
        string_to_replace = 'set feat_files(' + str(run_no) + ')'
        replace_text(fsf_file, string_to_replace, '# '+string_to_replace[1::])

def get_only_valid_1st_level_feat_dirs(feat_1st_level_dirs, empty_runs):
    """
    Get a list with 1st level feat dirs that do not contain runs without the
    contrast of interest
    feat_1st_level_dirs: list of 1st level feat dirs
    empty_runs: list with runs that do not contain trials of the contrast of
    interest
    Returns
    valid_1st_level_dirs: list with 1st level feat dirs that all contain runs
    for the contrast of interest (i.e. after removing empty runs from list)
    """
    valid_1st_level_dirs = []
    for feat_dir in feat_1st_level_dirs:
        cur_run = feat_dir[feat_dir.find('run-'):feat_dir.find('run-')+len('run-')+1]
        if not cur_run in empty_runs:
            valid_1st_level_dirs.append(feat_dir)
    return valid_1st_level_dirs

def check_if_contrast_is_empty(feat_1st_level_dirs, contrast_idx):
    """
    Check if all 1st level feat dirs contain the current contrast of interest.
    Specifically, whether their design.con file contains a 1 for the contrast
    of interest. If not, this indicates that the run does not contain trials
    for any regressor forming the contrast of interest. Such empty runs are
    checked and returned in a list of empty_runs.
    feat_1st_level_dirs: list of 1st level feat dirs
    contrast_idx: index of the current contrast of interest in the list of
    contrasts
    Returns
    empty_runs: list with runs that do not contain trials of the contrast of
    interest
    """
    empty_runs=[]
    for feat_dir in feat_1st_level_dirs:
        run_label = feat_dir[feat_dir.find('run-'):feat_dir.find('run-')+len('run-')+1]
        # load design.con file
        con_fname = feat_dir + os.sep + 'design.con'
        with open(con_fname, 'r') as file:
            con_text = file.read()
        # clean up text and get one row per contrast
        con_text_clean = con_text[con_text.find('/Matrix\n')+len('/Matrix\n')::]
        contrasts = con_text_clean.split('\n')
        if not '1' in contrasts[contrast_idx] and len(contrasts[contrast_idx])>0:
            empty_runs.append(run_label)
    return empty_runs

def get_contrast_labels(feat_dir):
    """
    Get labels and numbers of contrasts defined in design.con in feat dir
    feat_dir: path to a feat dir
    Returns
    contrast_labels: dict with constrast numbers and associated contrast labels
    """
    # load design.con file
    con_fname = feat_dir + os.sep + 'design.con'
    with open(con_fname, 'r') as file:
        con_text = file.read()
    con_text_list = con_text.split('\n')
    contrast_labels = {}
    for con_label in con_text_list:
        if 'ContrastName' in con_label:
            con_parts = con_label.split('\t')
            con_no = int(con_parts[0][con_label.find('ContrastName')+len('ContrastName')::])
            con_name = con_parts[1][0:-1]
            contrast_labels[con_no] = []
            contrast_labels[con_no].append(con_name)
    return contrast_labels

def get_1st_level_feat_dirs_from_fsf_file(fsf_file):
    """
    Get list of 1st level feat dir paths from 2nd level fsf file
    fsf_file: fsf file to be submitted as feat job
    Returns
    feat_1st_level_dirs: list of 1st level feat dirs
    """
    with open(fsf_file, 'r') as file:
        fsf_file_text = file.read()
    # check number of runs based on fsf file
    feat_1st_level_dirs = []
    check_run = 1
    while check_run < 20: # check for a max of 20 runs (all tasks have significantly less; even if combining tasks within a session)
        target_string = 'set feat_files(' + str(check_run) + ') '
        if target_string in fsf_file_text:
            feat_dir_1st_level = fsf_file_text[fsf_file_text.find(target_string) + len(target_string): fsf_file_text.find('# Add confound EVs text file')]
            feat_1st_level_dirs.append(feat_dir_1st_level[1:feat_dir_1st_level.find('"\n')])
        check_run += 1
    return feat_1st_level_dirs

def check_if_feat_output_exists(fsf_file):
    """
    Checks if feat output specified in fsf file already exists
    fsf_file: fsf file to be submitted as feat job
    Returns
    feat_dir_exists: bool; true if output already exists
    """
    with open(fsf_file, 'r') as file:
        fsf_file_text = file.read()
    # get output path
    target_string = 'set fmri(outputdir) "'
    feat_dir = fsf_file_text[fsf_file_text.find(target_string) + len(target_string):fsf_file_text.find('# TR(s)') - 3]
    feat_dir_exists = os.path.isdir(feat_dir)
    return feat_dir_exists

# %% functions for submitting feat jobs
def write_tmp_file_with_sbatch_cmd(fsf_file):
    """
    Create file with command to run feat on fsf file to be submitted as job
    Return path + name of tmp file
    """
    if 'analysis-3rd' in fsf_file:
        tmp_file = os.path.split(fsf_file)[0] + os.sep + 'tmpscript_' + fsf_file[fsf_file.find('cope'):fsf_file.find('.fsf')]
    else:
        tmp_file = os.path.split(fsf_file)[0] + os.sep + 'tmpscript_' + fsf_file[fsf_file.find('sub-'):fsf_file.find('.fsf')]
    file = open(tmp_file, 'w')
    # write sbatch command lines to beginning of script, then add feat and path to fsf file
    file.write('#!/bin/bash\n#SBATCH --nodes=1\n#SBATCH --partition=xnat\nfeat ' + fsf_file)
    file.write('\n')
    file.close()
    return tmp_file

def check_1st_level_feat_dirs(fsf_file):
    """
    Checks before 2nd level analyses whether 1st level feat dirs exist and
    whether report.html files of 1st level contain no errors.
    fsf_file: path to fsf file
    Returns
    check_flag_feat_dirs: true if all 1st level feat dirs exist and have no
    errors, false if not all dirs exist or any errors are detected in
    report.html
    error_msg: lists error type and run number for errors detected in 1st level
    feat dirs, if any
    """
    error_msg = []
    check_run = 0
    feat_1st_level_dirs = get_1st_level_feat_dirs_from_fsf_file(fsf_file)
    for feat_dir_1st_level in feat_1st_level_dirs:
        check_run += 1
        # throw warning if feat dir is not found and set flag to false
        if not os.path.isdir(feat_dir_1st_level):
            error_msg.append(['1st level feat dir not found',str(check_run+1)])
            print(' . ! Error for 2nd or 3rd level analysis! FEAT dir NOT FOUND for : ' + feat_dir_1st_level[feat_dir_1st_level.find('ses-')::] + ' ! ')
        #if feat dir exists check report log for errors
        else:
            # read report_log.html file
            report_log = feat_dir_1st_level + os.sep + 'report.html'
            with open(report_log, 'r') as file:
                data_log = file.read()
            # throw warning if error is found and set flag to false
            if ('Error' in data_log) or ('ERROR' in data_log):
                error_msg.append(['error in 1st level feat report',str(check_run+1)])
                print(' . ! Error for 2nd or 3rd level analysis! Error in FEAT log for  : ' + feat_dir_1st_level[feat_dir_1st_level.find('ses-')::] + ' !')
    check_flag_feat_dirs = not any(error_msg)
    return check_flag_feat_dirs, error_msg

def submit_feat_job(fsf_file,walltime,memory):
    """
    Submit feat jobs using fsf files, unless outputs already exist.
    In case of 2nd level analyses, also checks if 1st level feat reports
    contain any errors and prepares 1st level feat dirs to avoid repeating
    registration.
    fsf_file: fsf file to be submitted as feat job
    walltime: walltime in hours for job
    memory: memory in gb for job
    Returns
    error_msg: lists run and error type detected in 1st level feat dirs
    """
    # get expected output feat dir from fsf file
    output_dir_str = 'set fmri(outputdir) "'
    with open(fsf_file, 'r') as file:
        fsf_file_text = file.read()
    # get feat dir
    feat_dir = fsf_file_text[fsf_file_text.find(output_dir_str) + len(output_dir_str):fsf_file_text.find('# TR(s)') - 3]
    # check if output already exists, if so, skip
    if os.path.isdir(feat_dir):
        print(' . ! FEAT dir ALREADY EXISTS: ' + os.path.split(feat_dir)[1] + ' ! Skipping !')
        error_msg = [['_ Analysis output already existed - Skipped _', '_']]
    else:
        # if output doesn exist yet, prepare feat dir and submit jobs
        if 'analysis-2nd' in fsf_file:
            # if its a 2nd level analysis, make sure that all 1 level feat dirs exist and modify reg dir
            check_flag_feat_dirs, error_msg = check_1st_level_feat_dirs(fsf_file)
            if check_flag_feat_dirs:
                process_1st_level_reg_dirs_before_2nd_level(fsf_file)
        elif 'analysis-3rd' in fsf_file:
            # if its a 3rd level also check that all 2nd level dirs exists, but dont modify anything
            check_flag_feat_dirs, error_msg = check_1st_level_feat_dirs(fsf_file)
        else:
            error_msg = []
            check_flag_feat_dirs = True
        # check if no error in feat dirs has been detected or throw error
        if check_flag_feat_dirs:
            # write tmp file to submit as job using qsub
            tmp_file = write_tmp_file_with_sbatch_cmd(fsf_file)
            submit_cmd = ['sbatch -N1 -n1 -t %(walltime)s:00:00 --mem=%(memory)sgb '%{'walltime':walltime,'memory':memory} + tmp_file]
            # submit job
            print(' . . Submitting FEAT job --> ' + tmp_file[tmp_file.find('tmpscript')::])
            os.system(submit_cmd[0])
            # wait after each submission
            time.sleep(wait_between_jobs_in_sec)
        else:
            print(' . ! Analysis NOT submitted ! Unexpected error ! Check inputs ')
    return error_msg

# %% functions for avoiding reregistration in feat before running 2nd level
def process_1st_level_reg_dirs_before_2nd_level(fsf_file):
    """
    Process 1st level feat/reg dirs before running 2nd level feat analysis to
    avoid repeating registration during higher level feat analysis.
    Finds all 1st level feat dir associated with current 2nd level analysis
    (fsf_file) then submits each 1st level dir to be modified.
    Assumes that bold data is already registered to common space (MNI or T1w)
    due to e.g. preprocessing data with fmriprep.
    fsf_file: 2nd level fsf file
    """
    # find 1st level feat dirs
    feat_1st_level_dirs = get_1st_level_feat_dirs_from_fsf_file(fsf_file)
    for feat_dir_1st_level in feat_1st_level_dirs:
        #modify 1st level feat dir
        modify_1st_level_reg_dir(feat_dir_1st_level)

def modify_1st_level_reg_dir(feat_dir_1st_level):
    """
    Modifies 1st level feat /reg dirs before running 2nd level feat analysis to
    avoid repeating registration during higher level feat analysis.
    Removes any files that may result in repeated registration reg_standard dir
    and any .mat file in /reg. Then replaces example_func2standard with
    identity matrix and reference image standard.nii.gz with mean_func.
    feat_dir_1st_level: path to first level feat
    """
    import shutil, glob, os
    print('. modifying /reg dir of 1st level feat: ' + feat_dir_1st_level[feat_dir_1st_level.find('fslFeat')+25::])
    reg_standard_dir = feat_dir_1st_level + os.sep + 'reg_standard'
    reg_dir = feat_dir_1st_level + os.sep + 'reg'
    # remove reg_standard and contents if it already exists
    if os.path.isdir(reg_standard_dir):
        shutil.rmtree(reg_standard_dir)
    # delete mat files in reg dir
    for f in glob.glob(reg_dir + os.sep + '*.mat'):
        os.remove(f)
    # copy idenitty matrix to reg dir
    fsl_dir = os.environ["FSLDIR"]
    ident_mat = fsl_dir + os.sep + 'etc' + os.sep + 'flirtsch' + os.sep + 'ident.mat'
    reg_mat = reg_dir + os.sep + 'example_func2standard.mat'
    shutil.copy(ident_mat,reg_mat)
    # delete existing standard image
    standard_image = reg_dir + os.sep + 'standard.nii.gz'
    if os.path.isfile(standard_image):
        os.remove(standard_image)
    # copy mean_func as new standard reference image
    mean_func = feat_dir_1st_level + os.sep + 'mean_func.nii.gz'
    shutil.copy(mean_func,standard_image)

# %% main function to loop over subjects & analyses
def run_fsf_creation_and_submit_feat_job(bids_dir, subjects, analyses, analyses_suffix, fsf_templates_dir, fsf_output_dir, run_missing_contrast_check, error_df, walltime, memory):
    """
    Run fsf file creation for all subjects and runs for current analysis specification
    bids_dir: path to bids folder
    subjects: subject list
    analyses: list of labels for all runs of current analysis, including task and session keys (e.g. ses-V2_task-Replay_run-1, ...)
    analyses_suffix: suffix specifying analysis further, including analysis type and space key (e.g. analysis-1stROI_space-T1w)
    fsf_templates_dir: path to fsf templates
    fsf_output_dir: path to fsf output dir
    run_missing_contrast_check: whether to run checks for missing contrasts on 1st level before running 2nd level analysis
    error_df: data frame for error logs during job submission
    walltime: walltime for feat jobs in hours
    memory: memory for feat jobs in gb
    """
    from shutil import copy as copyFile
    # loop over subjects
    for sub in subjects:
        print('Subject: ' + sub + ' | processing...')
        # loop over analyses (runs)
        for analysis in analyses:
            error_msg = []
            # get session label from analysis name
            session_label = analysis[analysis.find('ses-'):analysis.find('task-')-1]
            # get analysis space from analysis suffix
            space = analyses_suffix[analyses_suffix.find('space-')::]
            # check if it's first level analysis
            if 'analysis-1st' in analyses_suffix:
                # get nifti file name if it is not 2nd level analysis
                nifti_file = get_fmriprep_processed_nifti_file(bids_dir, sub, session_label, analysis, space)
                # get place holder in template based on analysis name
                place_holder = analysis[analysis.find('task-')+5:analysis.find('_run-')] + 'XX'
            else:
                # if it is 2nd or 3rd level analysis set nifti file to 2nd level flag
                nifti_file = 'is_2nd_level'
                # get place holder in template based on analysis name
                place_holder = analysis[analysis.find('task-')+5::] + 'XX'
            place_holder_analysis_label = analyses_suffix[0:analyses_suffix.find('_space')]
            # check if nifti file exists, if so, create fsf file
            if os.path.isfile(nifti_file) or nifti_file == 'is_2nd_level':
                # create fsf copy of template for current subject & analysis
                input_file = fsf_templates_dir + os.sep + 'sub-PXX_' + place_holder + '_' + place_holder_analysis_label + '_space-SXX.fsf'
                fsf_file = fsf_output_dir + os.sep + sub + '_' + analysis + '_' + analyses_suffix + '.fsf'
                copyFile(input_file,fsf_file)
                # check if output already exists
                if check_if_feat_output_exists(fsf_file):
                    print(' ! Subject: ' + sub + ' | Analysis: ' + analysis + ' in ' + space + ' space. FEAT output already exists ! Skipping ! ')
                    continue
                # make fsf template for current subject & analysis
                make_this_fsf_template(fsf_file, sub, analysis, nifti_file, place_holder, space)
                # modify fsf template in case of runs missing contrasts
                if run_missing_contrast_check:
                    adjust_fsf_template_for_missing_contrasts(fsf_file)
                # submit feat job
                if submit_jobs:
                    error_msg = submit_feat_job(fsf_file, walltime, memory)
                # log error messages
                if any(error_msg):
                    for err_row in error_msg:
                        error_df.loc[len(error_df)] = [sub, analysis, analyses_suffix, 'run-' + err_row[1], err_row[0]]
                else:
                    error_df.loc[len(error_df)] = [sub, analysis, analyses_suffix, '-', '_ Job submitted _']
            else:
                print(' ! Subject: ' + sub + ' | Analysis: ' + analysis + ' in ' + space + ' space. Nifti FILE NOT FOUND !')
                error_msg = ['nifti file not found','x']
                error_df.loc[len(error_df)] = [sub, analysis, analyses_suffix, 'run-' + error_msg[1], error_msg[0]]

def run_fsf_creation_and_submit_feat_job_group_analyses(bids_dir, copes, analysis_prefix, analyses_suffix, fsf_templates_dir, fsf_output_dir, run_missing_contrast_check, error_df, walltime, memory):
    """
    Run fsf file creation for all subjects and runs for current analysis
    specification. Uses modified functions for GLM analyses, detected
    based on GLM string in analyses_suffix.
    bids_dir: path to bids folder
    copes: copes list
    analysis_prefix: analysis_prefix label (prefix; e.g. ses-V1_task-Dur)
    analyses_suffix: suffix specifying analysis further, including analysis type and space key (e.g. analysis-3rdFIR_space-MNI152NLin2009cAsym)
    fsf_templates_dir: path to fsf templates
    fsf_output_dir: path to fsf output dir
    run_missing_contrast_check: whether to run checks for missing contrasts on 1st level before running 2nd level analysis
    error_df: data frame for error logs during job submission
    walltime: walltime for feat jobs in hours
    memory: memory for feat jobs in gb
    """
    from shutil import copy as copyFile
    # loop over copes
    for cope in copes:
        print('Cope: ' + cope + ' | processing...')
        cope = 'cope' + cope
        error_msg = []
        # place holder
        place_holder = 'copeXX'
        # get analysis space from analysis suffix
        space = analyses_suffix[analyses_suffix.find('space-')::]
        # analysis label
        analysis_label = analyses_suffix[0:analyses_suffix.find('_space')]
        # 3rd level analysis uses nifti file to 2nd level flag
        nifti_file = 'is_2nd_level'
        # create fsf copy of template for current subject & analysis
        if 'GLM' in analyses_suffix:
            input_file = fsf_templates_dir + os.sep + analysis_prefix + '_analysis-3rdGLMXX_space-SXX_desc-copeXX.fsf'
            fsf_file = fsf_output_dir + os.sep + analysis_prefix + '_' + analysis_label + '_' + space + '_desc-' + cope + '.fsf'
            copyFile(input_file,fsf_file)
            # make fsf template for current subject & analysis
            make_GLM_3rd_level_fsf_template(fsf_file, analysis_label, subjects, cope, space)
        else:
            input_file = fsf_templates_dir + os.sep + analysis_prefix + '_' + analysis_label + '_space-SXX' + '_desc-' + place_holder + '.fsf'
            fsf_file = fsf_output_dir + os.sep + analysis_prefix + '_' + analyses_suffix + '_desc-' + cope + '.fsf'
            copyFile(input_file,fsf_file)
            # make fsf template for current subject & analysis
            make_this_fsf_template(fsf_file, '', cope, nifti_file, place_holder, space)
        # check if output already exists
        if check_if_feat_output_exists(fsf_file):
            print(' ! Cope: ' + cope + ' | Analysis: ' + analysis_prefix + ' in ' + space + ' space. FEAT output already exists ! Skipping ! ')
            continue
        # submit feat job
        if submit_jobs:
            error_msg = submit_feat_job(fsf_file, walltime, memory)
        # log error messages
        if any(error_msg):
            for err_row in error_msg:
                error_df.loc[len(error_df)] = [cope, analysis_prefix, analyses_suffix, 'run-' + err_row[1], err_row[0]]
        else:
            error_df.loc[len(error_df)] = [cope, analysis_prefix, analyses_suffix, '-', '_ Job submitted _']

#%% functions specific for GLM analysis
def make_GLM_3rd_level_fsf_template(fsf_file, analysis_label, subjects, cope, space):
    """
    Take fsf template and replace analysis, number of subjects, cope and space
    place holders with args below. This specific version handles fsf templates
    specific to the GLM group level (3rd level) analysis.
    Place holders in the fsf template file are assumed to be:
    - GLMXX: set to specific GLM analysis label;
    e.g.   GLMTarFace
    - XXX_NTPS: set to number of subjects; e.g.:   22
    - copeXX: to number of 2nd level cope; e.g.:   cope1
    - feat_filesXX: full string per subject where 2nd feat copes are located
    with additional string; e.g.:   set feat_files(1) ... .feat"
    	note this must also include the correct cope & analysis!
    - evgXX: full string per subject; for # Higher-level EV value for EV 1 and
    input 3 set e.g.:   set fmri(evg3.1) 1
    - groupmemXX: full string per subject; for 3rd subjects
    e.g.:    set fmri(groupmem.3) 1
    - analysis_label: The analysis output/input label; must correspond to 2nd
    level input analysis dir labels.
    fsf_file: fsf file name
    subjects: list of subject IDs to be used in the 3rd level analysis
    analysis_label: string of analysis label. Will be the 3rd level feat
    analysis output label and must be the same name as of the 2nd level
    analysis label; i.e. name of the analysis of the 2nd level feat dir
    (e.g. GLMTarFace)
    cope: cope of interest; must correspond to the cope#.feat dir of the 2nd
    level analyses
    space: target analysis space (key-value pair; e.g. space-T1w)
    """
    print(' . Creating fsf files for analysis: ' + analysis_label)
    # replace analysis label
    replace_text(fsf_file, 'analysis-3rdGLMXX', analysis_label)
    # replace number of volumes (i.e. participants in higher level analyses)
    replace_text(fsf_file, 'XXX_NTPS', str(len(subjects)))
    # replace cope number corresponding to cope#.feat dir
    replace_text(fsf_file, 'copeXX', cope)
    # replace analysis space place holder with target space
    replace_text(fsf_file, 'space-SXX', space)
    # construct & replace feat_filesXX string
    feat_files_string = construct_feat_files_string(subjects, analysis_label, cope, space)
    replace_text(fsf_file, 'feat_filesXX', feat_files_string)
    # construct & replace evgXX string
    evg_string = construct_evg_string(subjects)
    replace_text(fsf_file, 'evgXX', evg_string)
    # construct & replace groupmemXX string
    groupmem_string = construct_groupmem_string(subjects)
    replace_text(fsf_file, 'groupmemXX', groupmem_string)

def construct_feat_files_string(subjects, analysis_label, cope, space):
    """
    Constructs feat files string for replacing placeholder in 3rd level fsf
    template (currently specific to GLM analysis)
    subjects: list of subject IDs to be used in the 3rd level analysis
    analysis_label: string of analysis label. Will be the 3rd level feat
    analysis output label and must be the same name as of the 2nd level
    analysis label; i.e. name of the analysis of the 2nd level feat dir
    (e.g. GLMTarFace)
    cope: cope of interest; must correspond to the cope#.feat dir of the 2nd
    level analyses
    space: name of the analysis space (e.g. MNI152NLin2009cAsym)
    """
    analysis_short_label = analysis_label[analysis_label.find('3rd')+len('3rd')::]
    feat_path_string_template = 'set feat_files(%(sub_idx)s) "%(fsl_output_dir)s/%(sub)s/ses-V1/%(sub)s_ses-V1_task-Dur_analysis-2nd%(analysis_short_label)s_%(space)s.gfeat/%(cope)s.feat"\n\n'
    feat_files_string = ''
    for sub_idx in range(len(subjects)):
        feat_files_string = feat_files_string + feat_path_string_template%{'sub_idx':str(sub_idx+1), 'fsl_output_dir':fsl_output_dir, 'sub':subjects[sub_idx], 'analysis_short_label':analysis_short_label, 'space':space, 'cope':cope}
    return feat_files_string

def construct_evg_string(subjects):
    """
    Constructs evg string for replacing placeholder in 3rd level fsf
    template (currently specific to GLM analysis)
    subjects: list of subject IDs to be used in the 3rd level analysis
    """
    evg_string = ''
    for sub_idx in range(len(subjects)):
        evg_string = evg_string + 'set fmri(evg%(sub_idx)s.1) 1\n\n'%{'sub_idx':str(sub_idx+1)}
    return evg_string

def construct_groupmem_string(subjects):
    """
    Constructs groupmem string for replacing placeholder in 3rd level fsf
    template (currently specific to GLM analysis)
    subjects: list of subject IDs to be used in the 3rd level analysis
    """
    groupmem_string = ''
    for sub_idx in range(len(subjects)):
        groupmem_string = groupmem_string + 'set fmri(groupmem.%(sub_idx)s) 1\n\n'%{'sub_idx':str(sub_idx+1)}
    return groupmem_string

# %% run
if __name__ == '__main__':
    """
    Get analyses from analysis_definitions, then run fsf file creation for all
    analyses.
    bids_dir: path to bids folder
    analysis_definitions: dict per analysis to be performed
    """
    # get subject list
    subjects = get_subject_list(bids_dir,subject_list_type)

    remove_subjects = ['sub-SD122','sub-SD196']
    for r in remove_subjects:
        subjects = subjects[subjects != r]

    print('Removed subjects:',remove_subjects)
    print('Total subjects:',len(subjects))

    # create empty df for error log
    error_df = pd.DataFrame(columns=['sub','analysis','analyses_suffix','error_run_no','error_msg'])

    # loop over analyses in analysis definition dict
    for key in analysis_definitions:
        print('')
        print('PROCESSING ANALYSIS: -> ' + key + ' <- ')

        # get analysis specification
        analysis_dict = analysis_definitions[key]
        analyses, analyses_suffix, fsf_templates_dir, run_missing_contrast_check = get_analysis_labels(analysis_dict)

        # get required walltime and memory from analysis dict
        if submit_jobs:
            walltime = analysis_dict['walltime']
            memory = analysis_dict['memory']
        else:
            walltime = 0
            memory = 0

        # determine fsf output dir
        fsf_output_subdir = analyses_suffix[analyses_suffix.find('analysis-')+len('analysis-'):analyses_suffix.find('analysis-')+len('analysis-')+3]
        fsf_output_dir = bids_dir + os.sep + 'derivatives' + os.sep + 'fslFeat' + os.sep + 'fsf_files' + os.sep + fsf_output_subdir + '_level'
        # create fsf output directory
        if not os.path.isdir(fsf_output_dir):
            os.makedirs(fsf_output_dir)

        # check whether group analysis (3rd level) of individual subject analyses (1st or 2nd level) are to be performed
        if run_analysis == 3:
            # run fsf file creation for group analyses for current analysis
            copes = analysis_dict['copes']
            analysis = analyses[0]
            run_fsf_creation_and_submit_feat_job_group_analyses(bids_dir, copes, analysis, analyses_suffix, fsf_templates_dir, fsf_output_dir, run_missing_contrast_check, error_df, walltime, memory)
        else:
            # run fsf file creation for all subjects and runs of current analysis
            run_fsf_creation_and_submit_feat_job(bids_dir, subjects, analyses, analyses_suffix, fsf_templates_dir, fsf_output_dir, run_missing_contrast_check, error_df, walltime, memory)

        print('')
        print('waiting before continuting with next analyses')
        time.sleep(time_wait_after_analysis)

    # write log file for submission listing potential errors
    write_error_log(bids_dir, error_df)


# Crie um programa que leia dois números e mostre a soma entre eles.

valor1 = input("Digite o primeiro valor")
valor2 = input("digite o segundo valor")
s = int(valor1) + int(valor2)
mensagem = "A soma entre {} e {} é {}"

print(mensagem.format(valor1, valor2, s))

#print("A soma entre", valor1, "e", valor2, "é", int(valor1) + int(valor2))


from typing import Optional, List
from fastapi import Body, Depends, HTTPException, status, Query
from pydantic import EmailStr
from .app_helper import (
    create_student,
    get_student,
    update_student,
    delete_student,
)
from .models import (
    Student,
    StudentDelete,
    StudentUpdate,
    StudentReturn,
    Message,
    TokenData,
)
from fastapi import APIRouter
from ..accessmanager.app_helper import get_current_user

student_manager_app = APIRouter(tags=["Student Manager"])

@student_manager_app.post(
    "/student",
    summary="Create a new Student",
    description="Add a new Student",
    response_model=StudentReturn,
    response_description="Creates Student",
    status_code=status.HTTP_201_CREATED,
    responses={400: {"model": Message}},
    tags=["Student Manager"],
)
async def create_user(user: Student = Body(...)):
    try:
        response_usr = create_student(user.dict())
        if response_usr:
            return response_usr
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))

@student_manager_app.get(
    "/student",
    summary="Get all Students",
    description="Get all Students",
    response_model=List[StudentReturn],
    response_description="Get all Students",
    status_code=status.HTTP_200_OK,
    responses={400: {"model": Message}},
    tags=["Student Manager"],
)
async def get_user(
    current_user: TokenData = Depends(get_current_user),
    instituteId: int = Query(
        None, title="Institute Id", description="Id of The Institute", example=12345
    ),
    u_id: Optional[int] = Query(
        None, title="Student Id", description="Id of The Student", example=12345
    ),
    userName: Optional[str] = Query(
        None,
        title="Student Name",
        description="UserName of The Student",
        example="adam_sit",
    ),
    emailAddress: Optional[EmailStr] = Query(
        None,
        title="Email of Student",
        description="Email of Student",
        example="dd@testemail.com",
    ),
    mobileNumber: Optional[str] = Query(
        None,
        title="Mobile number of Student",
        description="Mobile of The Student",
        example="+91-9028077584",
    ),
):
    try:
        response_usr = get_student(
            institute_id=instituteId,
            u_id=u_id,
            username=userName,
            email=emailAddress,
            mobile=mobileNumber,
        )
        return response_usr
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))

@student_manager_app.put(
    "/student",
    summary="Update a Student",
    description="Update a Student",
    response_model=StudentReturn,
    status_code=status.HTTP_202_ACCEPTED,
    responses={400: {"model": Message}},
    tags=["Student Manager"],
)
async def update_user(
    current_user: TokenData = Depends(get_current_user),
    u_id: int = Query(..., title="User Id", description="Id of The user", example=123),
    user: StudentUpdate = Body(...),
):
    try:
        response_inst = update_student(u_id, user.dict())
        return response_inst
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))

@student_manager_app.delete(
    "/student",
    summary="Delete a Student",
    description="Delete a Student",
    response_description="Delete Student",
    response_model=StudentDelete,
    status_code=status.HTTP_200_OK,
    responses={400: {"model": Message}},
    tags=["Student Manager"],
)
async def delete_user(
    current_user: TokenData = Depends(get_current_user),
    instituteId: int = Query(
        None, title="Institute Id", description="Id of The Institute", example=1234
    ),
    u_id: Optional[int] = Query(
        None, title="Student Id", description="Id of The Student", example=1234
    ),
    userName: Optional[str] = Query(
        None,
        title="Student Name",
        description="UserName of The Student",
        example="adam_sit",
    ),
    emailAddress: Optional[EmailStr] = Query(
        None,
        title="Email of Student",
        description="Email of The Student",
        example="dd@testemail.com",
    ),
    mobileNumber: Optional[str] = Query(
        None,
        title="Mobile number of Student",
        description="Mobile of The Student",
        example="+91-9028077584",
    ),
):
    try:
        response_usr = delete_student(
            institute_id=instituteId,
            u_id=u_id,
            username=userName,
            email=emailAddress,
            mobile=mobileNumber,
        )
        return {"status_delete": response_usr}
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))


import sys
sys.stdin = open("달팽이사각형_input.txt")

def iswall(x, y):
    if x >= 0 and x < 5 and y >= 0 and y < 5 and result_list[x][y] == 0:
        return True
    else:
        return False

def my_min(a):
    mymin = 99
    min_X = 0
    min_Y = 0
    for x in range(len(a)):
        for y in range(len(a[x])):
            if a[x][y] < mymin:
                mymin = a[x][y]
                min_X, min_Y = x, y
    a[min_X][min_Y] = 99
    return mymin

minx = 0
miny = 0
cnt = 0

dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]

n = int(input())
result_list = [[0 for _ in range(n)] for _ in range(n)]
mylist=list(range(1,n+1))

for i in range(n):
    for j in range(n):
        mylist[i].append(j)
result_list[0][0] = my_min(mylist)

for i in range(n+1):
    for j in range(n+1):
        minx += dx[cnt%4]
        miny += dy[cnt%4]

        if iswall(minx, miny) == True:
            result_list[minx][miny] = my_min(mylist)
            freex=minx
            freey=miny
        elif iswall(minx, miny) == False:
            cnt += 1
            minx=freex
            miny=freey
print(result_list)


___author___='Huang'

import numpy as np
import pandas as pd

def loadData(fileName):
    dataSet=pd.read_table(fileName,header=None).values
    dataArray=dataSet[:,:-1]
    labelArray=dataSet[:,-1]
    return dataArray,labelArray

def selectJrand(i,m):
    j=i
    while(j==i):
        j=int(np.random.uniform(0,m))
    return j

#将alpha限定在对角线内，就是这里的L和H
def clipAlgha(aj,H,L):
    if aj>H:
        aj=H
    if aj<L:
        aj=L
    return aj

def smoSimple(dataSet,classLabel,C,toler,maxIter):
    dataMatrix=np.mat(dataSet);labelMatrix=np.mat(classLabel).transpose()
    m,n=dataMatrix.shape
    b=0
    iter=0
    alphas=np.mat(np.zeros((m,1)))
    while(iter<maxIter):
        alphaPairsChanged=0
        for i in range(m):
            fXi=float((np.multiply(alphas,labelMatrix)).T*(dataMatrix*dataMatrix[i,:].T))+b
            Ei=fXi-float(labelMatrix[i])
            if ((labelMatrix[i]*Ei<-toler)and (alphas[i]<C)) or ((labelMatrix[i]*Ei>toler)and (alphas[i]>0)):
                j=selectJrand(i,m)
                fXj=float((np.multiply(alphas,labelMatrix)).T*(dataMatrix*dataMatrix[j,:].T))+b
                Ej=fXj-float(labelMatrix[j])
                alphaIold=alphas[i].copy()
                alphaJold=alphas[j].copy()
                # 当yi!=yj时，alphaJ取值范围
                if labelMatrix[i]!=labelMatrix[j]:
                    L=max(0,alphas[j]-alphas[i])
                    H=min(C,C+alphas[j]-alphas[i])
                else:
                    L=max(0,alphas[j]-alphas[i]-C)
                    H=min(C,alphas[j]+alphas[i])
                if L==H :
                    print("L==H")
                    continue
                eta=2.0*dataMatrix[i,:]*dataMatrix[j,:].T-dataMatrix[i,:]*dataMatrix[i,:].T-dataMatrix[j,:]*dataMatrix[j,:].T
                if eta>=0:
                    print("eta>=0")
                    continue
                # 解二元规划得到迭代后未约束的解
                alphas[j] -= labelMatrix[j] * (Ei - Ej) / eta
                # 将解约束到取值范围里
                alphas[j]=clipAlgha(alphas[j],H,L)
                if(abs(alphas[j]-alphaJold)<0.00001):
                    print("j 几乎没有移动")
                    continue
                # 更新alpha[i]
                alphas[i]+=labelMatrix[i]*labelMatrix[j]*(alphaJold - alphas[j])
                # 在SM0算法中除了更新alpha，还需要更新b
                b1=b-Ei-labelMatrix[i]*dataMatrix[i]*dataMatrix[i].T*(alphas[i]-alphaIold)-labelMatrix[j]*dataMatrix[i]*dataMatrix[j].T*(alphas[j]-alphaJold)
                b2=b-Ej-labelMatrix[i]*dataMatrix[i]*dataMatrix[i].T*(alphas[i]-alphaIold)-labelMatrix[j]*dataMatrix[j]*dataMatrix[j].T*(alphas[j]-alphaJold)
                if alphas[i]>0 and alphas[i]<C:
                    b=b1
                if alphas[j]>0 and alphas[j]<C:
                    b=b2
                else:
                    b=(b1+b2)/2.0
                alphaPairsChanged+=1
                print("iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
        if(alphaPairsChanged ==0): iter+=1
        else:iter=0
        print("iteration number: %d" % iter)
    return b,alphas

'''
#######*******************************  *
带核函数部分
#####*************************************
'''

class optStructK:
    def __init__(self,dataMatIn, classLabels, C, toler, kTup):  # Initialize the structure with the parameters
        self.X = dataMatIn
        self.labelMat = classLabels
        self.C = C
        self.tol = toler
        self.m = np.shape(dataMatIn)[0]
        self.alphas = np.mat(np.zeros((self.m,1)))
        self.b = 0
        self.eCache = np.mat(np.zeros((self.m,2))) #first column is valid flag
        self.K = np.mat(np.zeros((self.m,self.m)))
        for i in range(self.m):
            self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
def calcEkK(oS,k):
    fXk=float(np.multiply(oS.alphas,oS.labelMat).T*oS.K[:,k])+oS.b
    Ek=fXk-float(oS.labelMat[k])
    return Ek

def selectJK(i,oS,Ei):
    maxK=-1 ;maxDeltaE=0;Ej=0
    oS.eCache[i]=[1,Ei]
    # 找出已经计算过Ei的alpha
    validEcacheList=np.nonzero(oS.eCache[:,0].A)[0]
    if (len(validEcacheList))>1:
        for k in validEcacheList:
            if k==i :
                continue
            Ek=calcEkK(oS,k)
            deltaE=np.abs(Ei-Ek)
            if (deltaE>maxDeltaE):
                maxK=k;maxDeltaE=deltaE;Ej=Ek
        return maxK,Ej
    else:
        j=selectJrand(i,oS.m)
        Ej=calcEkK(oS,j)
    return j,Ej

def updateEk(oS,k):
    Ek=calcEk(oS,k)
    oS.eCache[k]=[1,Ek]

def innerLK(i,oS):
    Ei=calcEkK(oS,i)
    if((oS.labelMat[i]*Ei<-oS.tol) and (oS.alphas[i]<oS.C)) or ((oS.labelMat[i]*Ei>oS.tol)and (oS.alphas[i]>0)):
        j,Ej=selectJK(i,oS,Ei)
        alphaIold=oS.alphas[i].copy();alphaJold=oS.alphas[j].copy()
        if (oS.labelMat[i]!=oS.labelMat[j]):
            L = max(0, oS.alphas[j] - oS.alphas[i])
            H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
        else:
            L = max(0, oS.alphas[j] +oS.alphas[i] - oS.C)
            H = min(oS.C, oS.alphas[j] + oS.alphas[i])
        if L == H:
            print("L==H")
            return 0
        eta = 2.0 * oS.K[i, j] - oS.K[i, i] - oS.K[j, j]
        if eta>=0:
            print("eta>=0")
            return 0
        oS.alphas[j]-=oS.labelMat[j]*(Ei-Ej)/eta
        oS.alphas[j]=clipAlgha(oS.alphas[j],H,L)
        updateEk(oS,j)
        if (abs(oS.alphas[j]-alphaJold)<0.00001):
            print("j 几乎没有移动")
            return 0
        oS.alphas[i]+=oS.labelMat[i]*oS.labelMat[j]*(alphaJold-oS.alphas[j])
        updateEk(oS,i)
        b1=oS.b-Ei-oS.labelMat[i]*oS.K[i,i]*(oS.alphas[i]-alphaIold)-oS.labelMat[j]*oS.K[i,j]*(oS.alphas[j]-alphaJold)
        b2=oS.b-Ej-oS.labelMat[i]*oS.K[i,j]*(oS.alphas[i]-alphaIold)-oS.labelMat[j]*oS.K[j,j]*(oS.alphas[j]-alphaJold)
        if (oS.alphas[i]>0) and (oS.alphas[i]<oS.C) :
            oS.b=b1
        elif (oS.alphas[j]>0 )and (oS.alphas[j]<oS.C):
            oS.b=b2
        else:
            oS.b=(b1+b2)/2.0
        #表示有一对alpha被改变
        return 1
    else :return 0

# Smo算法
def smoPK(dataMatIn,classLabels,C,toler,maxIter,kTup):
    oS=optStructK(np.mat(dataMatIn),np.mat(classLabels).transpose(),C,toler,kTup)
    iter=0
    entireSet=True
    #计算每次迭代alphas中的aplha对改变了多少次
    alphaPairsChanged=0
    while(iter<maxIter) and ((alphaPairsChanged>0) or (entireSet)):
        alphaPairsChanged=0
        if entireSet:
            for i in range(oS.m):
                alphaPairsChanged+=innerLK(i,oS)
            iter+=1
        else:
            # 优化在0<aplha<C范围的alpha，即扫描间隔边界上样本点
            nonBoundIs=np.nonzero((oS.alphas.A>0)*(oS.alphas.A<C))[0]
            for i in nonBoundIs:
                alphaPairsChanged+=innerLK(i,oS)
            iter+=1
        if entireSet:
            entireSet=False
        #如果间隔边界上的样本点都满足KKT条件就遍历整个训练集
        elif alphaPairsChanged==0:
            entireSet=True
    return oS.b,oS.alphas

def calcWs(alphas,dataArr,classLabels):
    X=np.mat(dataArr);labelMat=np.mat(classLabels).transpose()
    m,n=np.shape(X)
    #利用smo求解出来的alpha和kkt条件得到超平面法向量
    w=X.T*np.multiply(alphas,labelMat)
    return w

def kernelTrans(X,A,kTup):
    # kTup为包含核函数信息的元组
    m,n=np.shape(X)
    K=np.mat(np.zeros((m,1)))
    #线性，不做核转换，可换为多项式核
    if kTup[0]=='lin':
        K=X*A.T
    elif kTup[0]=='rbf':
        for i in range(m):
            deltaRow=X[i,:]-A
            K[i]=deltaRow*deltaRow.T
        K=np.exp(K/(-1*kTup[1]**2))
    else:
        raise NameError("还不支持这个kernel函数")
    return K

def testRbf(k1=1.3):
    dataArr,labelArr=loadData(r'testSetRBF.txt')
    b,alphas=smoPK(dataArr,labelArr,200,0.0001,10000,('rbf',k1))
    dataMat=np.mat(dataArr);labelMat=np.mat(labelArr).transpose()
    svInd=np.nonzero(alphas.A>0)[0]
    #支持向量
    sVs=dataMat[svInd]
    labelSV=labelMat[svInd]
    print("有%d个支持向量" %(np.shape(sVs)[0]))
    m,n=np.shape(dataMat)
    errorCount=0
    for i in range(m):
        kernelEval=kernelTrans(sVs,dataMat[i,:],('rbf',k1))
        predict=kernelEval.T*np.multiply(labelSV,alphas[svInd])+b
        if np.sign(predict)!= labelMat[i]:
            errorCount+=1
    print("训练误差为 : %f" %(float(errorCount/m)))
    test_dataArr,test_labelArr=loadData(r'testSetRBF2.txt')
    test_errorCount=0
    test_dataMat=np.mat(test_dataArr)
    test_labelMat=np.mat(test_labelArr).transpose()
    test_m,test_n=np.shape(test_dataMat)
    for i in range(test_m):
        kernelEval=kernelTrans(sVs,test_dataMat[i,:],('rbf',k1))
        predict=kernelEval.T*np.multiply(labelSV,alphas[svInd])+b
        if np.sign(predict)!=np.sign(test_labelMat[i]):
            test_errorCount+=1
    print("测试误差 为 : %f"  %(float(test_errorCount/test_m)))

def testRbf1(k1=1.3):
    dataArr,labelArr = loadData('testSetRBF.txt')
    b,alphas = smoPK(dataArr, labelArr, 200, 0.0001, 10000, ('rbf', k1)) #C=200 important
    datMat=np.mat(dataArr); labelMat = np.mat(labelArr).transpose()
    svInd=np.nonzero(alphas.A>0)[0]
    sVs=datMat[svInd] #get matrix of only support vectors
    labelSV = labelMat[svInd];
    print ("there are %d Support Vectors" % np.shape(sVs)[0])
    m,n = np.shape(datMat)
    errorCount = 0
    for i in range(m):
        kernelEval = kernelTrans(sVs,datMat[i,:],('rbf', k1))
        predict=kernelEval.T * np.multiply(labelSV,alphas[svInd]) + b
        if np.sign(predict)!=np.sign(labelArr[i]): errorCount += 1
    print ("the training error rate is: %f" % (float(errorCount)/m))
    dataArr,labelArr = loadData('testSetRBF2.txt')
    errorCount = 0
    datMat=np.mat(dataArr); labelMat = np.mat(labelArr).transpose()
    m,n = np.shape(datMat)
    for i in range(m):
        kernelEval = kernelTrans(sVs,datMat[i,:],('rbf', k1))
        predict=kernelEval.T * np.multiply(labelSV,alphas[svInd]) + b
        if np.sign(predict)!=np.sign(labelArr[i]): errorCount += 1
    print ("the test_import error rate is: %f" % (float(errorCount)/m) )

'''
#######********************************
不应用核函数部分
#######********************************
'''
class optStruct:
    def __init__(self,dataMatIn,classLabels,C,toler):
        self.X=dataMatIn
        self.labelMat=classLabels
        self.C=C
        self.tol=toler
        self.m=dataMatIn.shape[0]
        self.b=0
        self.alphas=np.mat(np.zeros((self.m,1)))
        self.eCache=np.mat(np.zeros((self.m,2)))

def calcEk(oS,k):
    fXk=float(np.multiply(oS.alphas,oS.labelMat).T*(oS.X*oS.X[k,:].T))+oS.b
    Ek=fXk-float(oS.labelMat[k])
    return Ek

def selectJ(i,oS,Ei):
    maxK=-1 ;maxDeltaE=0;Ej=0
    oS.eCache[i]=[1,Ei]
    # 找出已经计算过Ei的alpha
    validEcacheList=np.nonzero(oS.eCache[:,0].A)[0]
    if (len(validEcacheList))>1:
        for k in validEcacheList:
            if k==i :
                continue
            Ek=calcEk(oS,k)
            deltaE=np.abs(Ei-Ek)
            if (deltaE>maxDeltaE):
                maxK=k;maxDeltaE=deltaE;Ej=Ek
        return maxK,Ej
    else:
        j=selectJrand(i,oS.m)
        Ej=calcEk(oS,j)
    return j,Ej

def updateEk(oS,k):
    Ek=calcEk(oS,k)
    oS.eCache[k]=[1,Ek]

def innerL(i,oS):
    Ei=calcEk(oS,i)
    if((oS.labelMat[i]*Ei<-oS.tol) and (oS.alphas[i]<oS.C)) or ((oS.labelMat[i]*Ei>oS.tol)and (oS.alphas[i]>0)):
        j,Ej=selectJ(i,oS,Ei)
        alphaIold=oS.alphas[i].copy();alphaJold=oS.alphas[j].copy()
        if (oS.labelMat[i]!=oS.labelMat[j]):
            L = max(0, oS.alphas[j] - oS.alphas[i])
            H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
        else:
            L = max(0, oS.alphas[j] +oS.alphas[i] - oS.C)
            H = min(oS.C, oS.alphas[j] + oS.alphas[i])
        if L == H:
            print("L==H")
            return 0
        eta = 2.0 * oS.X[i, :] * oS.X[j, :].T - oS.X[i, :] * oS.X[i, :].T - oS.X[j, :] * oS.X[j, :].T
        if eta>=0:
            print("eta>=0")
            return 0
        oS.alphas[j]-=oS.labelMat[j]*(Ei-Ej)/eta
        oS.alphas[j]=clipAlgha(oS.alphas[j],H,L)
        updateEk(oS,j)
        if (abs(oS.alphas[j]-alphaJold)<0.00001):
            print("j is not moving enough")
            return 0
        oS.alphas[i]+=oS.labelMat[i]*oS.labelMat[j]*(alphaJold-oS.alphas[j])
        updateEk(oS,i)
        b1=oS.b-Ei-oS.labelMat[i]*oS.X[i,:]*oS.X[i,:].T*(oS.alphas[i]-alphaIold)-oS.labelMat[j]*oS.X[i,:]*oS.X[j,:].T*(oS.alphas[j]-alphaJold)
        b2=oS.b-Ej-oS.labelMat[i]*oS.X[i,:]*oS.X[j,:].T*(oS.alphas[i]-alphaIold)-oS.labelMat[j]*oS.X[j,:]*oS.X[j,:].T*(oS.alphas[j]-alphaJold)
        if (oS.alphas[i]>0) and (oS.alphas[i]<oS.C) :
            oS.b=b1
        elif (oS.alphas[j]>0 )and (oS.alphas[j]<oS.C):
            oS.b=b2
        else:
            oS.b=(b1+b2)/2.0
        #表示有一对alpha被改变
        return 1
    else :return 0

# Smo算法
def smoP(dataMatIn,classLabels,C,toler,maxIter):
    oS=optStruct(np.mat(dataMatIn),np.mat(classLabels).transpose(),C,toler)
    iter=0
    entireSet=True
    #计算每次迭代alphas中的aplha对改变了多少次
    alphaPairsChanged=0
    while(iter<maxIter) and ((alphaPairsChanged>0) or (entireSet)):
        alphaPairsChanged=0
        if entireSet:
            for i in range(oS.m):
                alphaPairsChanged+=innerL(i,oS)
            iter+=1
        else:
            # 优化在0<aplha<C范围的alpha，即扫描间隔边界上样本点
            nonBoundIs=np.nonzero((oS.alphas.A>0)*(oS.alphas.A<C))[0]
            for i in nonBoundIs:
                alphaPairsChanged+=innerL(i,oS)
            iter+=1
        if entireSet:
            entireSet=False
        #如果间隔边界上的样本点都满足KKT条件就遍历整个训练集
        elif alphaPairsChanged==0:
            entireSet=True
    return oS.b,oS.alphas

def calcWs(alphas,dataArr,classLabels):
    X=np.mat(dataArr);labelMat=np.mat(classLabels).transpose()
    m,n=np.shape(X)
    #利用smo求解出来的alpha和kkt条件得到超平面法向量
    w=X.T*np.multiply(alphas,labelMat)
    return w

'''
#######********************************
用svm对手写识别
#######********************************
'''
def img2vector(filename):
    returnVect = np.zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

def loadImages(dirName):
    from os import listdir
    hwLabels=[]
    trainingFileList=listdir(dirName)
    m=len(trainingFileList)
    trainingMat=np.zeros((m,1024))
    for i in range(m):
        fileNameStr=trainingFileList[i]
        fileStr=fileNameStr.split('.')[0]
        classNumStr=int(fileStr.split('_')[0])
        if classNumStr==9: hwLabels.append(-1)
        else:hwLabels.append(1)
        trainingMat[i,:]=img2vector('%s/%s' %(dirName,fileNameStr))
    return trainingMat,hwLabels

def testDigits(kTup=('rbf',10)):
    dataArr,labelArr=loadImages('trainingDigits')
    b,alphas=smoPK(dataArr,labelArr,200,0.0001,10000,kTup)
    dataMat=np.mat(dataArr)
    labelMat=np.mat(labelArr).transpose()
    #支持向量的下标
    svInd=np.nonzero(alphas.A>0)[0]
    #支持向量
    sVs=dataMat[svInd]
    labelSV=labelMat[svInd]
    print("有%d个支持向量" %len(labelSV))
    m,n=np.shape(dataMat)
    errorCount=0
    for i in range(m):
        kernelEval=kernelTrans(sVs,dataMat[i,:],kTup)
        predict=kernelEval.T*np.multiply(labelSV,alphas[svInd])+b
        if np.sign(predict)!=np.sign(labelMat[i]):
            errorCount+=1
    with open(r'indicate.txt',"a") as f:
            f.writelines('#######********************************\n\
      %s,参数为%s                '  %(kTup[0],kTup[1]))
            f.writelines("支持向量个数 : %d              " %len(labelSV))
            f.writelines("训练误差为 : %f                " %(errorCount/m))
    print("训练误差为 : %f" %(errorCount/m))
    test_dataArr,test_labelArr=loadImages('testDigits')
    test_errorCount=0
    test_dataMat=np.mat(test_dataArr)
    test_labelMat=np.mat(test_labelArr)
    test_m,test_n=np.shape(test_dataMat)
    for i in range(test_m):
        kernelEval=kernelTrans(sVs,test_dataMat[i,:],kTup)
        predict=kernelEval.T*np.multiply(labelSV,alphas[svInd])+b
        if np.sign(predict)!=np.sign(test_labelArr[i]):
            test_errorCount+=1
    with open(r'indicate.txt',"a") as g:
        g.writelines("测试误差为 : %f          \n" %(test_errorCount/test_m))
        g.writelines("#######********************************\n\n")
    print("测试误差为 : %f" %(test_errorCount/test_m))

#dataSet,label=loadData(r'testSet.txt')
#b1,alphas1=smoSimple(dataSet,label,0.6,0.001,40)
#b2,alphas2=smoP(dataSet,label,0.6,0.001,40)

#print(alphas2)
#testRbf()
#testRbf(k1=0.1)
def Digits():
    testDigits(('rbf', 20))
    testDigits(('rbf', 5))
    testDigits(('rbf', 10))
    testDigits(('rbf', 50))
    testDigits(('lin', 0))

def main():
    #手写体识别
    Digits()

if __name__=='__main__':
    main()


# fmt: off
import os
import sys

class OS:
    def __init__(self):
        self.rust_build = None
        self.copy_lib = None
        self.execute_python = None
        self.compile_python = None

    def lib_build(self):
        os.system(self.rust_build)
        os.system(self.copy_lib)

    def dev_build(self):
        os.system(self.execute_python)

    def release_build(self):
        os.system(self.rust_build)
        os.system(self.copy_lib)
        os.system(self.compile_python)

    def install_deps(self):
        print("Try to install dependencies...")
        print("python -m pip install -r requirements.txt")
        try:
            os.system("python -m pip install -r requirements.txt")
        except:
            print("Failed to install dependencies")
            print("Please install dependencies manually")
            sys.exit(1)

class Linux(OS):
    def __init__(self):
        self.rust_build = "cargo build --release --manifest-path=./modules/libkayn/Cargo.toml"
        self.copy_lib = "cp ./modules/libkayn/target/release/liblibkayn.so ./libkayn.so"
        self.execute_python = "python3 kayn.pyw"
        self.compile_python = "nuitka3 kayn.pyw --follow-imports"

class Windows(OS):
    def __init__(self):
        self.rust_build = "cargo build --release --manifest-path=./modules/libkayn/Cargo.toml"
        self.copy_lib = "copy modules\\libkayn\\target\\release\\libkayn.dll libkayn.pyd"
        self.execute_python = "python .\kayn.pyw"
        self.compile_python = "nuitka .\kayn.pyw --follow-imports"

class Mac(OS):
    def __init__(self):
        self.rust_build = "cargo build --release --manifest-path=./modules/libkayn/Cargo.toml"
        self.copy_lib = "cp ./modules/libkayn/target/release/liblibkayn.dylib ./libkayn.so"
        self.execute_python = "python3 kayn.pyw"
        self.compile_python = "nuitka3 kayn.pyw --follow-imports"

if __name__ == "__main__":
    # Recognize the OS
    system = sys.platform
    if sys.platform == "linux":
        system = Linux()
    elif sys.platform == "win32":
        system = Windows()
    elif sys.platform == "darwin":
        system = Mac()
    else:
        print("Unsupported OS")
        sys.exit(1)

    if len(sys.argv) > 1:
        if sys.argv[1] == "dev":
            system.dev_build()
        if sys.argv[1] == "devb":
            system.lib_build()
            system.dev_build()
        elif sys.argv[1] == "release":
            system.release_build()
        elif sys.argv[1] == "install":
            system.install_deps()
        else:
            print("Unsupported argument")
            sys.exit(1)
    else:
        print("No argument given. Available arguments: dev, release")
        sys.exit(1)


import pickle as pkl
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.ticker import FixedLocator, FixedFormatter

def log_histogram(data, name):
    """
    Create ridgeline plots, where each individual plot is a
    log-y scaled histogram.

    data: 2D Numpy array
    Each row contains all the values to include in a single histogram.
    0th row will be in the lowest ridgeline plot.
    Every Nth row will be a separate histogram. N is selected according
    to n_target_rows, below to avoid overwhelming the viewer.

    name: str
    A name stem for the results image.
    """
    dpi = 300
    border = .125
    eps = 1e-6
    # n_bins = 50
    n_bins = 100
    n_target_rows = 16

    fig = plt.figure()
    ax = fig.gca()

    n_rows, n_cols = data.shape
    d_keep = np.maximum(1, np.floor(n_rows / n_target_rows))
    i_keep = np.arange(0, n_rows, d_keep, dtype=int)
    data = data[i_keep, :]
    n_rows, n_cols = data.shape

    x_min = np.min(data)
    x_max = np.max(data)
    x_range = x_max - x_min + eps
    x_border = x_range * border
    bin_edges = np.linspace(x_min, x_max, n_bins + 1)
    bin_centers = ((bin_edges[:-1] + bin_edges[1:]) / 2)

    y_range = n_rows
    y_border = y_range * border
    ax.set_ylabel("Epoch")
    scale = .1

    for i in np.arange(n_rows)[::-1]:
        y_0 = i

        ax.plot(
            [x_min, x_max],
            [y_0, y_0],
            color="gray",
            alpha=.4,
            linewidth=.1,
        )

        counts, bins = np.histogram(data[i, :], bins=bin_edges)
        x = np.array([x_min] + list(bin_centers) + [x_max])
        y = y_0 + np.log2(np.array([0] + list(counts) + [0]) + 1) * scale
        ax.add_patch(patches.Polygon(
            np.concatenate((x[:, np.newaxis], y[:, np.newaxis]), axis=1),
            alpha=1,
            edgecolor="black",
            facecolor="lightsteelblue",
            linewidth=.2,
        ))

    y_formatter = FixedFormatter([str(i) for i in i_keep])
    y_locator = FixedLocator(list(np.arange(i_keep.size)))
    ax.yaxis.set_major_formatter(y_formatter)
    ax.yaxis.set_major_locator(y_locator)

    plt.savefig("hist_" + name + ".png", dpi=dpi)
    plt.close()

# This code is specific to my use case.
# It parses the values I pulled out and stashed in a dict with a
# key naming scheme that lets me re-create a Numpy array from it.

# filename = "data/params/scs_weight_01.pkl"
filename = "data/params/scs_p_01.pkl"
# filename = "data/params/scs_out_01.pkl"

def main():
    with open(filename, "rb") as f:
        scs_out = pkl.load(f)
        n_vals_per_iter = 0
        n_iters = 0
        n_epochs = 0
        out_vals = {}
        for name, vals in scs_out.items():
            n_vals_per_iter = vals.size
            layer, i_epoch, i_iter = name.split("_")
            n_iters = np.maximum(n_iters, int(i_iter) + 1)
            n_epochs = np.maximum(n_epochs, int(i_epoch) + 1)
            out_vals[layer] = None

        for layer in out_vals.keys():
            out_vals[layer] = np.zeros((n_epochs, n_iters * n_vals_per_iter))

        for name, vals in scs_out.items():
            layer, i_epoch, i_iter = name.split("_")
            i_iter = int(i_iter)
            i_epoch = int(i_epoch)
            out_vals[layer][
                    i_epoch,
                    n_vals_per_iter * i_iter: n_vals_per_iter * (i_iter + 1)
                    ] = vals

    for layer, vals in out_vals.items():
        log_histogram(vals, layer)

if __name__ == "__main__":
    main()


from zlib import adler32
from functools import wraps
from flask_restful import Resource as DefaultResurce, ResponseBase, OrderedDict, request, unpack, marshal

VALIDATION_ERROR_MESSAGE = 'Fields validation error'

class marshal_with(object):
    """A decorator that apply marshalling to the return values of your methods.

    >>> from flask_restful import fields, marshal_with
    >>> mfields = { 'a': fields.Raw }
    >>> @marshal_with(mfields)
    ... def get():
    ...     return { 'a': 100, 'b': 'foo' }
    ...
    ...
    >>> get()
    OrderedDict([('a', 100)])

    >>> @marshal_with(mfields, envelope='data')
    ... def get():
    ...     return { 'a': 100, 'b': 'foo' }
    ...
    ...
    >>> get()
    OrderedDict([('data', OrderedDict([('a', 100)]))])

    see :meth:`flask_restful.marshal`
    """
    def __init__(self, fields, envelope=None):
        """
        :param fields: a dict of whose keys will make up the final
                       serialized response output
        :param envelope: optional key that will be used to envelop the serialized
                         response
        """
        self.fields = fields
        self.envelope = envelope

    def __call__(self, f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            resp = f(*args, **kwargs)
            if isinstance(resp, tuple):
                data, code, headers = unpack(resp)
                if code != 200 or data.get('success') is False:
                    return resp
                return marshal(data, self.fields, self.envelope), code, headers
            else:
                return marshal(resp, self.fields, self.envelope)
        return wrapper

class Resource(DefaultResurce):

    def __serialize_errors(self, resp):
        """reformat the error structure and add the error codes"""
        if not resp.get('errors'):
            return resp
        elif resp['errors'].get('global_error'):
            ge_message = resp['errors'].pop('global_error')
            resp['errors']['global_error'] = {
                'code': adler32(ge_message.encode('UTF-16')),
                'message': ge_message
            }
            return resp
        else:
            # compose field errors
            field_errors = {}
            for field in resp['errors']:
                field_errors[field] = {
                    'code': adler32(resp['errors'][field].encode('UTF-16')),
                    'message': resp['errors'][field]
                }
            resp['errors'] = {
                'fields': field_errors
            }
            # compose global_error
            if resp['errors'].get('global_error'):
                ge_message = resp['errors']['global_error']
                resp['errors']['global_error'] = {
                    'code': adler32(ge_message.encode('UTF-16')),
                    'message': ge_message
                }
            else:
                resp['errors']['global_error'] = {
                    'code': adler32(VALIDATION_ERROR_MESSAGE.encode('UTF-16')),
                    'message': VALIDATION_ERROR_MESSAGE
                }
            return resp

    def dispatch_request(self, *args, **kwargs):

        # Taken from flask
        #noinspection PyUnresolvedReferences
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method

        for decorator in self.method_decorators:
            meth = decorator(meth)

        resp = meth(*args, **kwargs)

        # adds default success key
        if isinstance(resp, dict):
            if resp.get('success', None) is None:
                resp['success'] = True
            elif resp.get('success', True) is False:
                resp = self.__serialize_errors(resp)
        elif isinstance(resp, tuple):
            if resp[0].get('success', True) is False:
                list(resp)[0] = self.__serialize_errors(resp[0])
                resp = tuple(resp)

        if isinstance(resp, ResponseBase):  # There may be a better way to test
            return resp

        representations = self.representations or OrderedDict()

        #noinspection PyUnresolvedReferences
        mediatype = request.accept_mimetypes.best_match(representations, default=None)
        if mediatype in representations:
            data, code, headers = unpack(resp)
            resp = representations[mediatype](data, code, headers)
            resp.headers['Content-Type'] = mediatype
            return resp

        return resp


# Import the libraries
import nltk
from nltk.chat.util import Chat, reflections
import tkinter as tk
from tkinter import *

# Define the responses
pairs = [
    ['hi', ['Hello!', 'Hi there!']],
    ['what is your name', ['My name is Chatbot', 'I am Chatbot']],
    ['how are you', ['I am doing well', 'I am fine, thanks']],
    ['bye', ['Goodbye', 'See you later']],
]

# Create the chatbot
chatbot = Chat(pairs, reflections)

# Create the user interface
def send():
    msg = EntryBox.get("1.0", "end-1c").strip()
    EntryBox.delete("0.0", END)
    if msg != "":
        ChatLog.config(state=NORMAL)
        ChatLog.insert(END, "You: " + msg + "\n\n")
        ChatLog.config(foreground="#442265", font=("Verdana", 12 ))

        res = chatbot.respond(msg)
        ChatLog.insert(END, "Chatbot: " + res + "\n\n")

        ChatLog.config(state=DISABLED)
        ChatLog.yview(END)

root = Tk()
root.title("Chatbot")
root.geometry("400x500")
root.resizable(width=FALSE, height=FALSE)

ChatLog = Text(root, bd=0, bg="purple", height="8", width="50", font="Arial",)
ChatLog.config(state=DISABLED)

scrollbar = Scrollbar(root, command=ChatLog.yview, cursor="heart")
ChatLog['yscrollcommand'] = scrollbar.set

SendButton = Button(root, font=("Verdana", 12, 'bold'), text="Send", width="12", height=5, bd=0, bg="#32de97", activebackground="#3c9d9b", fg='#ffffff', command=send)

EntryBox = Text(root, bd=0, bg="white", width="29", height="5", font="Arial")

scrollbar.place(x=376, y=6, height=386)
ChatLog.place(x=6, y=6, height=386, width=370)
EntryBox.place(x=128, y=401, height=60, width=265)
SendButton.place(x=6, y=401, height=60)

root.mainloop()


import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import sys

def moving_averages(stock_name, tdays, mdays):

	data = pd.read_csv('log_data.csv')
	data = data.set_index('Date')
	data.index = pd.to_datetime(data.index, format = '%Y-%m-%d %H:%M:%S')
	ldata = data['Log_'+stock_name].to_list()[::-1]

	all_sum = ldata[0]
	all_avg = 0
	m_sum = sum(ldata[:mdays-1])

	moving_avg = [None for i in range(mdays-1)]
	all_time_avg = [0]

	for i in range(1,len(ldata)):

		all_sum = all_sum + ldata[i]
		all_avg = all_sum/(i+1)
		all_time_avg.append(all_avg)

		if(i >= mdays - 1):
			m_sum = m_sum + ldata[i]
			m_avg = m_sum/mdays
			moving_avg.append(m_avg)
			m_sum = m_sum - ldata[i - (mdays-1)]

	df = pd.DataFrame()
	df.index = data.index
	df['Log_'+stock_name] = data['Log_'+stock_name]
	df['all_time_avg'] = all_time_avg[::-1]
	df['moving_avg'] = moving_avg[::-1]
	df.to_csv('df_combine.csv')

	plt.figure(figsize = (10,7))
	# data['Log_'+stock_name].plot(label = 'Daily_return')
	plt.plot(data.index, all_time_avg[::-1], label = 'all_time_avg')
	plt.plot(data.index, moving_avg[::-1], label = 'Moving_avg')
	# df['Log_'+stock_name].plot(label = 'return')
	# df['All_time_avg'].plot(label = 'All_time_avg')
	# df['moving_averages'].plot(label = 'moving_avg')
	plt.title('all_time_avg & Moving_avg plotting', fontsize = 16)
	plt.xlabel('Year', fontsize = 14)
	plt.ylabel('Avg return', fontsize = 14)

	plt.legend()
	plt.grid(which = 'major', linestyle = '-.', linewidth = 0.5)
	plt.show()

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

moving_averages(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))


import json

import requests
from config.const import SPOTIFY_HEADERS, SPOTIFY_URLS
from config.tokens import SPOTIFY_OAUTH_TOKEN, SPOTIFY_USER_ID

class SpotifyAPI:

    name = 'spotify'

    @staticmethod
    def __get_request_headers():
        headers = SPOTIFY_HEADERS
        headers["Authorization"] = headers["Authorization"].format(spotify_oauth_token=SPOTIFY_OAUTH_TOKEN)
        return headers

    @staticmethod
    def __get_playlists_url():
        return SPOTIFY_URLS["playlists_url"]

    @staticmethod
    def __get_tracks_url():
        return SPOTIFY_URLS["tracks_url"]

    @classmethod
    def _ARS_retrieve_track_info(cls, song_info):
        try:
            track_id = song_info['metadata']['music'][0]['external_metadata']['spotify']['track']['id']
        except (KeyError, IndexError):
            return None
        return 'spotify:track:{}'.format(track_id)

    @classmethod
    def retrieve_track_info(cls, ARS_name, song_info):
        if ARS_name == 'acrcloud':
            return cls._ARS_retrieve_track_info(song_info)
        # raise ARS_NOT_RECOGNISED but who cares

    @classmethod
    def _retrieve_playlist_id(cls, playlist_request):
        return json.loads(playlist_request.text)['id']

    @classmethod
    def get_playlist_id(cls, playlist_request):
        return cls._retrieve_playlist_id(playlist_request)

    @classmethod
    def create_playlist(cls, playlist_name: str, description="", public=False):
        playlists_url = cls.__get_playlists_url().format(spotify_user_id=SPOTIFY_USER_ID)
        request_body = {
            "name": playlist_name,
            "description": description,
            "public": public
        }
        payload = json.dumps(request_body)
        return requests.post(playlists_url, data=payload, headers=cls.__get_request_headers())

    @classmethod
    def add_tracks_to_playlist(cls, tracks_ids: str, playlist_id: str):
        tracks_url = cls.__get_tracks_url().format(playlist_id=playlist_id)
        params = {
            "uris": ",".join(tracks_ids)
        }
        return requests.post(tracks_url, params=params, headers=cls.__get_request_headers())


from flask import Flask, render_template, request
#import requests
import pickle
#import numpy as np
import sklearn
from sklearn.preprocessing import StandardScaler

app = Flask(__name__)
model = pickle.load(open('model.pkl', 'rb'))
@app.route('/',methods=['GET'])
def index():
    return render_template('index.html')

standard_to = StandardScaler()
@app.route("/predict", methods=['POST'])
def predict():

    if request.method == 'POST':

        #1Age
        Age=float(request.form['Age'])

        #2BusinessTravel
        BusinessTravel=int(request.form['BusinessTravel'])

        #3Department
        Department=int(request.form['Department'])

        #4distanceFromHome
        DistanceFromHome=float(request.form['DistanceFromHome'])
        #5Education
        Education=float(request.form['Education'])

        #6EducationField
        EducationField=int(request.form['EducationField'])

        #7EnvironmentSatisfaction
        EnvironmentSatisfaction=float(request.form['EnvironmentSatisfaction'])

        #8JobInvolvement
        JobInvolvement=float(request.form['JobInvolvement'])

        #9JobLevel
        JobLevel=float(request.form['JobLevel'])

        #10JobRole
        JobRole=int(request.form['JobRole'])

        #11JobSatisfaction
        JobSatisfaction=float(request.form['JobSatisfaction'])

        #12MaritalStatus
        MaritalStatus=int(request.form['MaritalStatus'])

        #14MonthlyIncome
        MonthlyIncome=float(request.form['MonthlyIncome'])

        #15NumCompaniesWorked
        NumCompaniesWorked=float(request.form['NumCompaniesWorked'])

        #16OverTime
        OverTime=int(request.form['OverTime'])

        #17PercentSalaryHike
        PercentSalaryHike=float(request.form['PercentSalaryHike'])

        #18PerformanceRating
        PerformanceRating=float(request.form['PerformanceRating'])

        #19RelationshipSatisfaction
        RelationshipSatisfaction=float(request.form['RelationshipSatisfaction'])

       #20StockOptionLevel
        StockOptionLevel=float(request.form['StockOptionLevel'])

        #21TotalWorkingYears
        TotalWorkingYears=float(request.form['TotalWorkingYears'])

        #22TrainingTimesLastYear
        TrainingTimesLastYear=float(request.form['TrainingTimesLastYear'])

        #23YearsSinceLastPromotion
        WorkLifeBalance=float(request.form['WorkLifeBalance'])

        #24YearsAtCompany
        YearsAtCompany=float(request.form['YearsAtCompany'])

        #25YearsInCurrentRole
        YearsInCurrentRole=float(request.form['YearsInCurrentRole'])

        #26YearsSinceLastPromotion
        YearsSinceLastPromotion=float(request.form['YearsSinceLastPromotion'])

        #27YearsWithCurrManager
        YearsWithCurrManager=float(request.form['YearsWithCurrManager'])

        #28Stability
        Stability=float(request.form['Stability'])

        #29Fidelity
        Fidelity=float(request.form['Fidelity'])

        #30Income_YearsComp
        Income_YearsComp=float(request.form['Income_YearsComp'])

        #31Total_Satisfaction
        TotalSatisfaction_mean=float(request.form['Total_Satisfaction'])

        prediction=model.predict([[Age,
                                    Department,
                                    DistanceFromHome,
                                    Education,
                                    EducationField,
                                    EnvironmentSatisfaction,
                                    JobInvolvement,
                                    JobLevel,
                                    JobRole,
                                    BusinessTravel,
                                    JobSatisfaction,
                                    MaritalStatus,
                                    MonthlyIncome,
                                    NumCompaniesWorked,
                                    OverTime,
                                    PercentSalaryHike,
                                    PerformanceRating,
                                    RelationshipSatisfaction,
                                    StockOptionLevel,
                                    TotalWorkingYears,
                                    TrainingTimesLastYear,
                                    WorkLifeBalance,
                                    YearsAtCompany,
                                    YearsInCurrentRole,
                                    YearsSinceLastPromotion,
                                    YearsWithCurrManager,
                                     Stability,
                                     Fidelity,
                                    Income_YearsComp,
                                    TotalSatisfaction_mean
                                   ]])

        #output=prediction
        if(prediction==0):
            return render_template('result.html',prediction_text="This employee likely to leave Company")
        elif(prediction==1):
            return render_template('result.html',prediction_text="This employee will not leave the Company")
        else:
            return render_template('result.html',prediction_text=" data not present")

if __name__=="__main__":
  app.run()


from django.urls import path

from repairshop import views

urlpatterns = [
    path("", views.home, name="home"),
    path("categories/<slug:sub_category>/", views.sub_category, name="sub_category"),
    path("about/", views.about, name="about"),
    path("categories/", views.categories, name="categories"),
    path("contacts/", views.contacts, name="contacts"),
    path("products/<slug:product_url>/", views.product, name="product"),
]


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

__author__ = 'Michael Liao (askxuefeng@gmail.com)'

DEPRECATED = True

from google.appengine.ext import db

DEFAULT_GROUP = 'default'

def get_instance_settings_as_dict(widget_instance):
    '''
    get widget instance settings as dict which contains key-value pairs (both str/unicode).

    Args:
        widget_instance: WidgetInstance object.
    Returns:
        Settings as dict which both key and value are str/unicode.
    '''
    list = WidgetInstanceSetting.all().filter('widget_instance ==', widget_instance).fetch(100)
    d = {}
    for setting in list:
        d[setting.setting_key] = setting.setting_value
    import logging
    logging.info('get_instance_settings_as_dict: ' + str(d))
    return d

def update_instance_settings(widget_instance, setting_as_dict):
    '''
    Update instance settings.

    Args:
      widget_instance: WidgetInstance object.
      setting_as_dict: new settings as dict contains key as str and value as str or unicde.
    Returns:
      None
    '''
    group = widget_instance.widget_group
    db.delete(get_instance_settings(widget_instance))
    for name, value in setting_as_dict.items():
        WidgetInstanceSetting(
                widget_group=group,
                widget_instance=widget_instance,
                setting_name=name,
                setting_value=value
        ).put()


import server
import os
import config
import time
import threading

class Chord:
    def __init__(self):
        self.qnt_key = None
        self.qnt_nodes = None
        self.cfg = config.Config()
        self.host = self.cfg.getHost().strip("\n")
        self.portaInicial = int(self.cfg.getMinPort().strip("\n"))
        self.srv = [] #lista de servidores
        self.id = []
        self.portas = []
        self.qnt_key = None
        self.qnt_nodes = None

    def main(self):
        self.qnt_key = input("Quantidade de Bits: ")
        self.qnt_nodes = input("Quantidade de Nós: ")
        self.snap_time = input("Tempo Snapshot (seg): ")

        self.id.insert(0, 2**int(self.qnt_key) - 1)
        self.portas.insert(0, self.portaInicial)

        for i in range(1, int(self.qnt_nodes)):
            self.id.insert(i, int(self.id[i-1] - ((2**int(self.qnt_key))/int(self.qnt_nodes))))
            self.portas.insert(i, self.portaInicial+ 4*i)

        for i in range(0, int(self.qnt_nodes)):
            if i == 0:
                self.srv.insert(i, (self.id[int(self.qnt_nodes)-1], self.id[i], self.id[i+1],self.portas[int(self.qnt_nodes)-1], self.portas[i], self.portas[i+1]))
            elif i == (int(self.qnt_nodes )-1):
                self.srv.insert(i, (self.id[i-1], self.id[i], self.id[0],self.portas[i-1], self.portas[i], self.portas[0]))
            else:
                self.srv.insert(i, (self.id[i-1], self.id[i], self.id[i+1],self.portas[i-1], self.portas[i], self.portas[i+1]))

        chord = open("thisChord.txt","w")
        for i in range(0, int(self.qnt_nodes) ): #inicia os servidores passando os parametros criados pelo chord
            cAnt, cAtu, cSuc, pAnt, pAtu, pSuc = self.srv[i]
            chord.write(self.qnt_key + " " + self.qnt_nodes + " " + str(cAnt) + " " + str(cAtu) + " " + str(cSuc) + " " + str(pAnt) + " " + str(pAtu) + " " + str(pSuc) +  " " + self.snap_time + "\n")
            os.system("start /B start cmd.exe @cmd /k python server.py " + self.qnt_key + " " + self.qnt_nodes + " " + str(cAnt) + " " + str(cAtu) + " " + str(cSuc) + " " + str(pAnt) + " " + str(pAtu) + " " + str(pSuc) +  " " + self.snap_time)

        #inicia cliente
        os.system("start /B start cmd.exe @cmd /k python client.py ")

if __name__ == '__main__':
    chord = Chord()
    chord.main()