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

# # －實作程式輔助判斷藥物造成SJS/TEN之可能性
# ## 本程式整合Naranjo score及ALDEN score，藉由輸入藥物資訊、病人資料及相關問題
# ## 經由分析後，即可比較各藥物間之懷疑程度，同時輸出藥物使用表格

# ### 讀取藥物資料(存於csv檔中)：
# * acetaminophen, 500 mg 1# QID, 2020/6/8-2020/6/10
# * prednisolone, 5 mg 2# QID, 2020/6/12-2020/6/24
# * prednisolone, 5 mg 1# QID, 2020/6/30-2020/7/4
# * TMP-SMX, 2# BID, 2020/6/12-2020/6/24

# In[1]:

input('歡迎使用Naranjo與ALDEN量表輔助工具\n')
Drug_list = []  # ['acetaminophen', 'prednisolone', 'TMP-SMX']
Drug_info_list = [] # [['500 mg 1# QID'], ['5 mg 2# QID', '5 mg 1# QID'], ['2# BID']]
Drug_date_list = [] # [['2020/6/8-2020/6/10'], ['2020/6/12-2020/6/24', '2020/6/30-2020/7/4'], ['2020/6/12-2020/6/24']]
Drug_score_list = []

print('讀取import.csv...\n')
with open('./import.csv', encoding='utf-8') as f:
    lines = f.readlines()
    for l in lines:
        l = l.replace('\n','')
        name = l.split(',')[0]
        info = l.split(',')[1]
        date = l.split(',')[2]

        print('讀取藥物: %s, 劑量: %s, 服用期間: %s' % (name, info, date))

        if name in Drug_list:  # 若該藥物已出現過
            drug_index = Drug_list.index(name)
            Drug_info_list[drug_index].append(info)
            Drug_date_list[drug_index].append(date)
        else:
            Drug_list.append(name)
            Drug_info_list.append([info])
            Drug_date_list.append([date])
print('\n')

# ### 輸入不良反應發生日期及症狀緩解日

# In[2]:

Index_day = input('請輸入不良反應發生日期(index day): ')
Resolution_day = input('請輸入症狀緩解日: ')

# ### ALDEN score & naranjo score

# In[3]:

import datetime
from dateutil.relativedelta import relativedelta

def transform_time(date):
    return datetime.datetime.strptime(date, '%Y/%m/%d')

def difference_day(date1, date2):
    try: diff = int(str(date1 - date2).split(' ')[0])
    except: diff = 0
    return diff

def ask_YN(question):
    ans = input(question).upper()
    if ans == 'Y' or ans == 'N':
        pass
    else:
        print('輸入錯誤，請再試一次')
        ans = ask_YN(question)
    return ans

def ask_YNU(question):
    ans = input(question).upper()
    if ans == 'Y' or ans == 'N' or ans == 'U':
        pass
    else:
        print('輸入錯誤，請再試一次')
        ans = ask_YNU(question)
    return ans

def ask_number(question):
    ans = input(question)
    try: ans = float(ans)
    except:
        print('輸入錯誤，請再試一次')
        ans = ask_number(question)

    return ans

def ask_SaSiO(question):
    ans = input(question).upper()
    if ans == 'SA' or ans == 'SI' or ans == 'O':
        pass
    else:
        print('輸入錯誤，請再試一次')
        ans = ask_SaSiO(question)
    return ans

PLACEBO = ask_YN('是否有任何一個藥物給予安慰劑，以測試不良反應是否有發生? (Y/N) ')  # Q6
TDM = ask_YN('是否有任何一個藥物監測濃度，以測試是否超出治療區間? (Y/N) ')  # Q7
OBJ = ask_YNU('此項不良反應是否有客觀的證據證明是藥品引起的？("Y"es/ "N"o/ "U"nknown) ')  #Q10

def analysis(drug, date_lst):
    Alden_score = []
    Naranjo_score = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    start_day = transform_time(date_lst[0].split('-')[0])
    end_day = transform_time(date_lst[0].split('-')[1])
    rechallenge = len(date_lst)
    if rechallenge > 1:
        restart_day = transform_time(date_lst[1].split('-')[0])
        reend_day = transform_time(date_lst[1].split('-')[1])
    index_day = transform_time(Index_day)
    resolution_day = transform_time(Resolution_day)
    s_to_idx_day = difference_day(index_day, start_day) # index day - start_day的天數
    e_to_res_day = difference_day(resolution_day, end_day)
    idx_to_e_day = difference_day(end_day, index_day)

    print('----------------------')
    print('目前評分藥物: %s, 使用日期: %s, 不良反應發生日期: %s, 症狀緩解日期: %s' % (drug, ', '.join(date_lst), Index_day, Resolution_day))

#     先評估Naranjo score

#     Nq1在C5

#     Nq2在C1
    if rechallenge == 1:
        if e_to_res_day > 0: Naranjo_score[2] = (1, 'Y')
        else:
            Nq3 = ask_YNU('　當停藥或服用此藥之解藥，不良反應是否減輕？("Y"es/ "N"o/ "U"nknown) ')
            if Nq3 == 'Y': Naranjo_score[2] = (1, 'Y')
            elif Nq3 == 'N': Naranjo_score[2] = (0, 'N')
            elif Nq3 == 'U': Naranjo_score[2] = (0, 'U')
    else:
        Nq3 = ask_YNU('　當停藥或服用此藥之解藥，不良反應是否減輕？("Y"es/ "N"o/ "U"nknown) ')
        if Nq3 == 'Y': Naranjo_score[2] = (1, 'Y')
        elif Nq3 == 'N': Naranjo_score[2] = (0, 'N')
        elif Nq3 == 'U': Naranjo_score[2] = (0, 'U')

    if rechallenge == 1: Naranjo_score[3] = (0, 'U')
    else:
        Nq4 = ask_YNU('　停藥一段時間再重新服用此藥，同樣的不良反應是否再度發生？ ("Y"es/ "N"o/ "U"nknown) ')
        if Nq4 == 'Y': Naranjo_score[3] = (2, 'Y')
        elif Nq4 == 'N': Naranjo_score[3] = (-1, 'N')
        elif Nq4 == 'U': Naranjo_score[3] = (0, 'U')

#     Nq5後面再評分

    if PLACEBO == 'Y':
        Nq6 = ask_YNU('　當給予安慰劑時，此項不良反應是否會再度發生？ ("Y"es/ "N"o/ "U"nknown) ')
        if Nq6 == 'Y': Naranjo_score[5] = (-1, 'Y')
        elif Nq6 == 'N': Naranjo_score[5] = (1, 'N')
        elif Nq6 == 'U': Naranjo_score[5] = (0, 'U')
    elif PLACEBO == 'N':
        Naranjo_score[5] = (0, 'U')

    if TDM == 'Y':
        Nq7 = ask_YNU('　此藥物的血中濃度是否達到中毒劑量？ ("Y"es/ "N"o/ "U"nknown) ')
        if Nq7 == 'Y': Naranjo_score[6] = (1, 'Y')
        elif Nq7 == 'N': Naranjo_score[6] = (0, 'N')
        elif Nq7 == 'U': Naranjo_score[6] = (0, 'U')
    elif TDM == 'N':
        Naranjo_score[6] = (0, 'U')

    if rechallenge == 1:
        Naranjo_score[7] = (0, 'U')
    else:
        Nq8 = ask_YNU('　對此病人而言，藥物劑量與不良反應的程度是否成正向關係？("Y"es/ "N"o/ "U"nknown) ')
        if Nq8 == 'Y': Naranjo_score[7] = (1, 'Y')
        elif Nq8 == 'N': Naranjo_score[7] = (0, 'N')
        elif Nq8 == 'U': Naranjo_score[7] = (0, 'U')

#   Nq9在C3

    if OBJ == 'Y': Naranjo_score[9] = (1, 'Y')
    elif OBJ == 'N': Naranjo_score[9] = (0, 'N')
    elif OBJ == 'U': Naranjo_score[9] = (0, 'U')

#   Criterion 1: Delay from initial drug component intake to onset of reaction (index day)

    if s_to_idx_day <= 28 and s_to_idx_day >= 5:
#       * Suggestive +3 (From 5 to 28 days)
        Alden_score.append(3)
        Naranjo_score[1] = (2, 'Y')

    elif s_to_idx_day <= 56 and s_to_idx_day >= 29:
#       * Compatible +2 (From 29 to 56 days)
        Alden_score.append(2)
        Naranjo_score[1] = (2, 'Y')

    elif s_to_idx_day <= 4 and s_to_idx_day >= 1:
#       * Likely +1 (From 1 to 4 days)
        Alden_score.append(1)
        Naranjo_score[1] = (2, 'Y')

    elif s_to_idx_day > 56:
#       * Unlikely −1 (>56 Days)
        Alden_score.append(-1)
        Naranjo_score[1] = (2, 'Y')

    else:
#       * Excluded −3 (Drug started on or after the index day)
        Alden_score.append(-3)
        Naranjo_score[1] = (-1, 'N')

#   Criterion 2: Drug present in the body on index day
    On_index_day = False
    if end_day == index_day or idx_to_e_day >= 0:
#       Index day 仍然服用藥物
        On_index_day = True

    if On_index_day == False:
        halflife = ask_number('　Index day 時無服用藥物, 請輸入藥物半衰期(hr): ')
        x5_halflife = halflife * 5 / 24
        End_day_plus_halflife = (end_day + datetime.timedelta(days=x5_halflife)).strftime("%Y/%m/%d")
        print('　%s 經過5倍半衰期後: %s ' % (end_day.strftime("%Y/%m/%d"), End_day_plus_halflife))
        end_day_plus_halflife = transform_time(End_day_plus_halflife)

        if end_day_plus_halflife == index_day or difference_day(end_day_plus_halflife, index_day) > 0:
            print('　Index day 時體內仍有藥物殘量 ')
            On_index_day = True

        else:
            function = ask_YN('　請輸入病人肝腎功能是否異常、或是懷疑有交互作用，導致index day時仍有藥物累積在體內(Y/N): ')
            if function == 'Y':
#              體內懷疑有藥物殘留
#              * Doubtful −1 (Drug stopped at a time point prior to the index day by more than five times the elimination half-life but liver or kidney function alterations or suspected drug interactions are present)')
                Alden_score.append(-1)
            elif function == "N":
#              體內應無藥物殘留，且代謝功能正常
#              * Excluded −3 (Drug stopped at a time point prior to the index day by more than five times the elimination half-life, without liver or kidney function alterations or suspected drug interactions)')
                Alden_score.append(-3)
    if On_index_day == True:
#       * Definite 0 (Drug stopped at a time point prior to the index day by more than five times the elimination half-life but liver or kidney function alterations or suspected drug interactions are present')
        Alden_score.append(0)

#   Criterion 3: Prechallenge/ rechallenge
    used = ask_YNU('　過去是否有服用過該藥物 ("Y"es/ "N"o/ "U"nknown) ')
    if used == 'U' or used == 'N':
#       Other reaction after use of similar drug: 0 (No known previous exposure to this drug)
        Alden_score.append(0)

    elif used == 'Y':
        his = ask_YN('　過去服用類似藥物時是否有造成傷害 (Y/N) ')

        if his == 'N':
#           * Negative −2 (Exposure to this drug without any reaction (before or after reaction)
            Alden_score.append(-2)
            Naranjo_score[8] = (0, 'N')

        elif his == 'Y':
            challenge = ask_SaSiO('　是否有因服用同樣藥物／類似藥物產生SJS/TEN，或是產生了其他副作用 ("Sa"me, "Si"miliar, "O"ther) ')
            if challenge == "Sa":
#               * Positive specific for disease and drug: 4 (SJS/TEN after use of same drug)
                Alden_score.append(4)
                Naranjo_score[8] = (1, 'Y')
            elif challenge == "Si":
#               * Positive specific for disease or drug: 2 (SJS/TEN after use of similar drug or other reaction with same drug)
                Alden_score.append(2)
                Naranjo_score[8] = (1, 'Y')
            elif challenge == 'O':
#               * Positive unspecific: 1　(Other reaction after use of similar drug)
                Alden_score.append(1)
                Naranjo_score[8] = (1, 'Y')
    if Naranjo_score[8] == 0: Naranjo_score[8] = (0, 'U')

#   Criterion 4: Dechallenge'
    if rechallenge == 1:
        if resolution_day == end_day or e_to_res_day < 0 : Alden_score.append(-2)
#       * Negative -2 (Drug continued without harm)
        else: Alden_score.append(0)
#       * Neutral 0 (Drug stopped (or unknown))
    else:
        if resolution_day == reend_day or resolution_day == end_day or difference_day(resolution_day, reend_day) < 0 :
    #       緩解後仍然服用藥物
    #       * Negative -2 (Drug continued without harm)')
            Alden_score.append(-2)
        else:
    #       * Neutral 0 (Drug stopped (or unknown))
            Alden_score.append(0)

#    Criterion 5: Type of drug (notoriety)
    strongly_associated = ['TMP-SMX', 'allopurinol', 'lamotrigine', 'sulfamethoxazole','carbamazepine','phenytoin','nevirapine','sulfasalazine','sulfonamides','piroxicam','tenoxicam','phenobarbital','etoricoxib']
    associated = ['diclofenac','doxycycline','amoxicillin','ampicillin','ciprofloxacin','levofloxacin','amifostine','oxcarbazepine','rifampin','rifampicin']
    suspected = ['pantoprazole','glucocorticoids','omeprazole','tetrazepam','dipyrone','metamizole','terbinafine','levetiracetam']
    if drug in strongly_associated:
#       Strongly associated +3 (Drug of the “high-risk” list according to previous case–control studies)
        Naranjo_score[0] = (1, 'Y')
        Alden_score.append(3)
    elif drug in associated:
#       Associated +2 (Drug with definite but lower risk according to previous case–control studies)
        Naranjo_score[0] = (1, 'Y')
        Alden_score.append(2)
    elif drug in suspected:
#       Suspected +1 (Several previous reports, ambiguous epidemiology results (drug under surveillance))
        Naranjo_score[0] = (1, 'Y')
        Alden_score.append(1)

    else:
        ask = ask_YNU('　過去是否有文獻指出%s會造成SJS/TEN? ("Y"es/ "N"o/ "U"nknown) ' % drug)
        if ask == 'Y':
#           Suspected +1 (Several previous reports, ambiguous epidemiology results (drug under surveillance))
            Naranjo_score[0] = (1, 'Y')
            Alden_score.append(1)
        if ask == 'U':
#           Unknown 0 (All other drugs including newly released ones))
            Naranjo_score[0] = (0, 'U')
            Alden_score.append(0)
        elif ask == 'N':
#           Not suspected −1 (No evidence of association from previous epidemiology studyd with sufficient number of exposed controlsc)
            Naranjo_score[0] = (0, 'N')
            Alden_score.append(-1)

    return [Alden_score, Naranjo_score]

for drug_idx in range(len(Drug_list)):
    Drug_score_list.append(analysis(Drug_list[drug_idx], Drug_date_list[drug_idx]))
print('輸入完畢\n')

# In[4]:

# 計算每一個的temp score

Sum_scores_ALDEN =[]
for ALD,NAR in Drug_score_list:
    temp_sum = 0
    for score in ALD:
        temp_sum += score
    Sum_scores_ALDEN.append(temp_sum)

idx = 0
for s in Sum_scores_ALDEN:
    temp_Sum_scores = []
    temp_Sum_scores.extend(Sum_scores_ALDEN)
    del temp_Sum_scores[idx]
    POSSIBLE_DRUG = False
    for ts in temp_Sum_scores:
        if ts > 3: POSSIBLE_DRUG = True
    if POSSIBLE_DRUG == True:
        Drug_score_list[idx][0].append(-1)
        Drug_score_list[idx][1][4] = (-1,'Y')
    else:
        Drug_score_list[idx][0].append(0)
        Drug_score_list[idx][1][4] = (2, 'N')
    idx += 1
input('預估分數...\n')

# In[8]:

def read_score(input_score, drug, date_lst):
    Alden_score = input_score[0]
    Naranjo_score = input_score[1]
    Start_day = date_lst[0].split('-')[0]
    start_day = transform_time(date_lst[0].split('-')[0])
    end_day = transform_time(date_lst[0].split('-')[1])
    rechallenge = len(date_lst)
    if rechallenge > 1:
        restart_day = transform_time(date_lst[1].split('-')[0])
        reend_day = transform_time(date_lst[1].split('-')[1])
    index_day = transform_time(Index_day)
    resolution_day = transform_time(Resolution_day)
    s_to_idx_day = difference_day(index_day, start_day) # index day - start_day的天數
    e_to_res_day = difference_day(resolution_day, end_day)
    idx_to_e_day = difference_day(end_day, index_day)

    print('\n[%s] Criterion 1: Delay from initial drug component intake to onset of reaction (index day)' % Alden_score[0])
    if Alden_score[0] == 3:
        print('　* Suggestive +3 (From 5 to 28 days)\n　initial day: %s, index day: %s, 相差%s天' % (Start_day, Index_day, s_to_idx_day))
    elif Alden_score[0] == 2:
        print('　* Compatible +2 (From 29 to 56 days)\n　initial day: %s, index day: %s, 相差%s天' % (Start_day, Index_day, s_to_idx_day))
    elif Alden_score[0] == 1:
        print('　* Likely +1 (From 1 to 4 days)\n　initial day: %s, index day: %s, 相差%s天' % (Start_day, Index_day, s_to_idx_day))
    elif Alden_score[0] == -1:
        print('　* Unlikely −1 (>56 Days)\n　initial day: %s, index day: %s, 相差%s天' % (Start_day, Index_day, s_to_idx_day))
    elif Alden_score[0] == -3:
        print('　* Excluded −3 (Drug started on or after the index day)\n　initial day: %s, index day: %s, 相差%s天' % (Start_day, Index_day, s_to_idx_day))

    print('[%s] Criterion 2: Drug present in the body on index day' % Alden_score[1])
    if Alden_score[1] == 0:
        print('　* Definite 0 (Drug continued up to index day or stopped at a time point less than five times the elimination half-life before the index day)')
        print('　最後服用藥物日: %s, index day: %s' % (end_day.strftime("%Y/%m/%d"), Index_day))
    elif Alden_score[1] == -1:
        print('　* Doubtful −1 (Drug stopped at a time point prior to the index day by more than five times the elimination half-life but liver or kidney function alterations or suspected drug interactions are present)')
        print('　體內懷疑有藥物殘留(超過五個半衰期，但肝腎功能不良))')
    elif Alden_score[1] == -3:
        print('　* Excluded −3 (Drug stopped at a time point prior to the index day by more than five times the elimination half-life, without liver or kidney function alterations or suspected drug interactions)')
        print('　體內應無藥物殘留(超過五個半衰期)，且代謝功能正常')

    print('[%s] Criterion 3: Prechallenge/ rechallenge' % Alden_score[2])
    if Alden_score[2] == 4:
        print('　* Positive specific for disease and drug +4 (SJS/TEN after use of same drug)')
    elif Alden_score[2] == 2:
        print('　* Positive specific for disease or drug +2 (SJS/TEN after use of similar drug or other reaction with same drug)')
    elif Alden_score[2] == 1:
        print('　* Positive unspecific +1　(Other reaction after use of similar drug)')
    elif Alden_score[2] == 0:
        print('  * Other reaction after use of similar drug 0 (No known previous exposure to this drug)')
    elif Alden_score[2] == -2:
        print('  * Negative −2 (Exposure to this drug without any reaction (before or after reaction)')

    print('[%s] Criterion 4: Dechallenge' % Alden_score[3])
    if Alden_score[3] == -2:
        print('　* Negative -2 (Drug continued without harm)')
        print('　緩解時仍然服用藥物')
    elif Alden_score[3] == 0:
        print('　* Neutral 0 (Drug stopped (or unknown))')
    if rechallenge == 1:    print('　最後服用藥物日: %s, 症狀緩解日: %s' % (end_day.strftime("%Y/%m/%d"), Resolution_day))
    else: print('服用藥物日期: %s, 症狀緩解日: %s' % (', '.join(date_lst), Resolution_day))

    print('[%s] Criterion 5: Type of drug (notoriety)' % Alden_score[4])
    if Alden_score[4] == 3:
        print('　* Strongly associated +3 (Drug of the “high-risk” list according to previous case–control studies)')
    elif  Alden_score[4] == 2:
        print('  * Associated +2 (Drug with definite but lower risk according to previous case–control studies)')
    elif Alden_score[4] == 1:
        print('  * Suspected +1 (Several previous reports, ambiguous epidemiology results (drug under surveillance))')
    elif Alden_score[4] == 0:
        print('  * Unknown 0 (All other drugs including newly released ones))')
    elif Alden_score[4] == -1:
        print('  * Not suspected −1 (No evidence of association from previous epidemiology studyd with sufficient number of exposed controlsc)')

    print('[%s] Criterion 6: Other cause' % Alden_score[5])
    if Alden_score[5] == 0:
        print('　No other possible drug')
    elif  Alden_score[5] == -1:
        print('  * Possible −1')

    sums_A = 0
    for s in Alden_score:
        sums_A += s
    print('\n')
    if sums_A >= 6: print('Alden_score總分: %s, very probable' % sums_A)
    elif sums_A >= 4: print('Alden_score總分: %s, probable' % sums_A)
    elif sums_A >= 2: print('Alden_score總分: %s, possible' % sums_A)
    elif sums_A >= 0: print('Alden_score總分: %s, unlikely' % sums_A)
    else: print('Alden_score總分: %s, very unlikely' % sums_A)

    sums_N = 0
    print('\n[%s] Are there previous conclusive reports of this reaction? %s' % (Naranjo_score[0][0], Naranjo_score[0][1]))
    print('[%s] Did the adverse event appear after the drug was given? %s' % (Naranjo_score[1][0], Naranjo_score[1][1]))
    print('[%s] Did the adverse reaction improve when the drug was discontinued or a specific antagonist was given? %s' % (Naranjo_score[2][0], Naranjo_score[2][1]))
    print('[%s] Did the adverse reaction reappear upon readministering the drug? %s' % (Naranjo_score[3][0], Naranjo_score[3][1]))
    print('[%s] Were there other possible causes for the reaction? %s' % (Naranjo_score[4][0], Naranjo_score[4][1]))
    print('[%s] Did the adverse reaction reappear upon administration of placebo? %s' % (Naranjo_score[5][0], Naranjo_score[5][1]))
    print('[%s] Was the drug detected in the blood or other fluids in toxic concentrations? %s' % (Naranjo_score[6][0], Naranjo_score[6][1]))
    print('[%s] Was the reaction worsened upon increasing the dose? Or, was the reaction lessened upon decreasing the dose? %s' % (Naranjo_score[7][0], Naranjo_score[7][1]))
    print('[%s] Did the patient have a similar reaction to the drug or a related agent in the past? %s' % (Naranjo_score[8][0], Naranjo_score[8][1]))
    print('[%s] Was the adverse event confirmed by any other objective evidence?) %s' % (Naranjo_score[9][0], Naranjo_score[9][1]))

    for i in range(10):
          sums_N += Naranjo_score[i][0]

    if sums_N >= 9: print('Naranjo score總分: %s, Definite' % sums_N)
    elif sums_N >= 5: print('Naranjo score總分: %s, Probable' % sums_N)
    elif sums_N >= 1: print('Naranjo score總分: %s, Possible' % sums_N)
    elif sums_N <= 0: print('Naranjo score總分: %s, Doubtful' % sums_N)

    return (sums_A, sums_N)

Sum_scores = []
for s in Drug_list:
    idx = Drug_list.index(s)
    print('--------------------------------------')
    print('目前評分藥物: %s, 使用日期: %s, 不良反應發生日期: %s' % (Drug_list[idx], ', '.join(Drug_date_list[idx]), Index_day))
    Sum_scores.append(read_score(Drug_score_list[idx], Drug_list[idx], Drug_date_list[idx]))
    print('--------------------------------------')
    input('完成度(%s / %s)' % (idx+1, len(Drug_list)))

# In[29]:

import pandas as pd

df_list = []
idx = 0
for i in range(len(Drug_list)):
    temp = []
    temp.append(Drug_list[i])
    temp.append(Sum_scores[i][0])
    temp.extend(Drug_score_list[i][0])
    temp.append(Sum_scores[i][1])
    temp.extend(Drug_score_list[i][1])
    df_list.append(temp)
Score_df = pd.DataFrame(df_list, columns = ["藥物名稱", "Alden_score總分", 'Criterion 1', 'Criterion 2', 'Criterion 3','Criterion 4','Criterion 5','Criterion 6', "Naranjo score總分",
                                       'Nar_Q1', 'Nar_Q2', 'Nar_Q3', 'Nar_Q4', 'Nar_Q5', 'Nar_Q6', 'Nar_Q7', 'Nar_Q8', 'Nar_Q9', 'Nar_Q10'])
Score_df.set_index("藥物名稱", inplace=True)
Score_df.sort_values('Alden_score總分', inplace=True, ascending=False )
Score_df.to_excel('./藥物分析結果.xls')
Score_df
input('儲存藥物分析結果至./藥物分析結果.xls...')

# In[30]:

import pandas as pd
import numpy as np
def date_range_to_lst(date):
    day_lst = []
    start_day = transform_time(date[0].split('-')[0])
    end_day = transform_time(date[0].split('-')[1])
    diff_day_counts = difference_day(end_day, start_day)
    for i in range(diff_day_counts+1):
        day_lst.append((start_day + datetime.timedelta(days=i)).strftime("%Y/%m/%d"))
    return day_lst

all_date_list = []

for dates in Drug_date_list:
    if len(dates) > 1:
        for d in dates:
            RESULT = date_range_to_lst([d])
    else:
        RESULT = date_range_to_lst(dates)
    for R in RESULT:
        if R not in all_date_list: all_date_list.append(R)

all_date_list = (pd.to_datetime(all_date_list).sort_values()).tolist()
all_date_list = str(all_date_list[0]).split(' 00:00:00')[0].replace('-','/') + '-' + str(all_date_list [-1]).split(' 00:00:00')[0].replace('-','/')
all_date_list = date_range_to_lst([all_date_list])
all_date_list = (pd.to_datetime(all_date_list).sort_values())

Empty_df = [[""] * len(all_date_list)] * len(Drug_list)

Date_df = pd.DataFrame(Empty_df, columns = all_date_list)
Date_df['藥物名稱'] = Drug_list
Date_df.set_index("藥物名稱", inplace=True)

for info in Drug_info_list:
    info_index = Drug_info_list.index(info)
    date_lst = Drug_date_list[info_index]
    drug = Drug_list[info_index]
    if len(info) > 1:
        for idx in range(len(date_lst)):
            date = pd.to_datetime(date_range_to_lst([date_lst[idx]]))
            Date_df.loc[drug, date] = info[idx]
    else:
        date = pd.to_datetime(date_range_to_lst(date_lst))
        Date_df.loc[drug, date] = info

t = []
for time in Date_df.columns.tolist():
    t.append(time.strftime('%Y/%m/%d'))

Date_df.columns = t
Date_df.to_excel('./使用藥物清單.xls')
Date_df
input('儲存藥物清單結果至./使用藥物清單.xls...')
input('執行完成。')


__author__="Kevin"

from db.db_model.mongo_websites_models import URLToGenre,URLBow
from db.db_model.mongo_websites_classification import URLBow_fulltxt
from classification_attribute import word_based
from util.base_util import normalize_genre_string

"""
This module has code that converts Full webpage text to bag of words
"""

def URLToGenre_to_bow():
    bow=word_based.BagOfWords()

    all_ref_index=set(i.ref_index for i in URLBow.objects.no_cache())

    URLBow_fulltxt.objects.delete()
    for c,db_obj in enumerate(URLToGenre.objects.no_cache()):
        if c%1000==0:
            print("Done with {}".format(c))

        if not hasattr(db_obj,"original") or not db_obj.original \
            or not hasattr(db_obj,"page") or not db_obj.page:
            continue

        ref_id=db_obj.ref_index
        if ref_id not in all_ref_index:
            continue

        page=db_obj.page

        if isinstance(page,list):
            print("{} is a list updating".format(ref_id))
            page="".join(page)
            #db_obj.update(page=page)

        try:
            word_dict=bow.get_word_count(page)
        except:
            print("Skipped {}".format(ref_id))
            continue
        short_genres=[normalize_genre_string(genre.genre) for genre in db_obj.genre]

        URLBow_fulltxt(ref_index=ref_id,attr_map=word_dict,short_genres=short_genres,\
                       url=db_obj.url).save()


from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for, current_app
from src.db import get_db

from src.auth import login_required

bp = Blueprint('page', __name__, url_prefix='/')

@bp.route('/movie', methods=('GET', 'POST'))
@login_required
def movie_handler():
	mid = request.args.get('id', type=int)
	db = get_db()
	uid = g.user['uid']
	if not mid:
		mid = 1
	else:
		mid += 1

	userlist = db.execute('SELECT mid FROM watchlist WHERE uid = ?', (uid,)).fetchall()
	while exists(userlist, mid):
		mid += 1

	movie = db.execute('SELECT * FROM movie WHERE mid = ?', (mid,)).fetchone()
	print(movie)

	return render_template('movie.html', mid=mid, movie=movie)

@bp.route('/add', methods=('GET', 'POST'))
@login_required
def add():
	mid = request.args.get('id', type=int)
	uid = session.get('user-id')
	db = get_db()
	db.execute('INSERT INTO watchlist (uid, mid) VALUES (?, ?)', (uid, mid))
	db.commit()

	return redirect(url_for('page.movie_handler', id=mid))

@bp.route('/watchlist', methods=('GET', 'POST'))
@login_required
def watchlist():
	user = g.user
	db = get_db()
	movies = db.execute(
            '''SELECT m.mid, m.title, m.picture, m.genre FROM
            movie m INNER JOIN watchlist w ON m.mid = w.mid
            INNER JOIN user u on w.uid = u.uid
            where u.uid = ?''',
            (str(user[0]),)
        ).fetchall()

	return render_template('watchlist.html', movies=movies)

@bp.route('/remove', methods=('GET', 'POST'))
@login_required
def wremover():
	user = g.user
	db = get_db()
	mid = request.args.get('id', type=int)
	uid = user['uid']
	print(uid, mid)

	db.execute(
		'''
		DELETE FROM watchlist
		WHERE uid = ? AND mid = ?

		''',
		(uid, mid)
	)
	db.commit()

	return redirect(url_for('page.watchlist'))

def exists(ulist, id):
	for row in ulist:
		if row['mid'] == id:
			return True

	return False


# 判断树是否平衡的条件 左子树是否平衡，右子树是否平衡
# 定义 需要返回两个值，是否平衡和树的高度
# 拆解 二叉树的拆解一般就是左子树和右子树
# 出口 if root is None: return True,0

def divideConquer(self,root):
    if not root:
        return True,0
    is_left_balanced,left_height = self.divideConquer(root.left)
    is_right_balanced,right_height = self.divideConquer(root.right)
    root_height = max(left_height,right_height)+1

    if not is_left_balanced or not is_right_balanced:
        return False,root_height
    if abs(left_height-right_height)>1:
        return False,root_height
    return True,root_height


# coding : utf-8
import re
import os
import sys
import getpass
import urllib3
import requests

from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB, USLT

# ID3 info:
tagMap = {'cover': APIC, 'title': TIT2,
          'artist': TPE1, 'album': TALB, 'lyric': USLT}
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
headers = {'User-agent': 'Mozilla/5.0'}
MSCDIR = './mp3'
# print(repr(s)[1:-1])  # deal with invalid encoded filename

class netease_music:
    def __init__(self, path):
        '''path: direcotory that contains cache files'''
        self.path = os.path.abspath(path)
        self.id_name = {i[:i.find('-')]: i for i in os.listdir(path)
                        if i.endswith('.uc') or i.endswith('.uc!')}
        if self.id_name:
            if not os.path.exists(MSCDIR):
                os.mkdir(MSCDIR)
            print('Input :', path)
            print('Output:', MSCDIR)
        else:
            print('No cache file found in "{}"'.format(path))

    def getInfoFromWeb(self, musicId):
        # xpath for name and lrc:
        # self.nameXpath ='//div[@class="tit"]/em[@class="f-ff2"]/text()'
        # self.lrcSentencePt=re.compile(r'\[\d+:\d+\.\d+\](.*?)\\n')         # wrong  (r'\[\d+,\d+\](\(\d+,\d+\)(\w))+\n')

        # api :
        # type=song, lyric, comments, detail, artist, album, search
        # eg  API = 'https://api.imjad.cn/cloudmusic/?type=song&id=1234132'    download music

        dic = {}
        url = 'http://music.163.com/api/song/detail/?ids=[' + musicId + ']'
        res = requests.get(url, headers=headers).json()
        info = res['songs'][0]
        dic['artist'] = [info['artists'][0]['name']]
        dic['title'] = [info['name']]
        dic['cover'] = [info['album']['picUrl']]
        dic['album'] = [info['album']['name']]
        return dic

    def getPath(self, dic, musicId):
        '''get the name of music from info dict'''
        title = dic['title'][0]
        artist = dic['artist'][0]
        name = title + '(' + artist+')'
        for i in '>?*/\:"|<':
            name = name.replace(i, '-')  # convert to valid chars for file name
        name = re.sub('\s', '_', name)
        self.id_name[musicId] = name
        return os.path.join(MSCDIR, name + '.mp3')

    def decrypt(self, musicId, name):
        def _decrypt(cachePath):
            with open(cachePath, 'rb') as f:
                btay = bytearray(f.read())
            for i, j in enumerate(btay):
                btay[i] = j ^ 0xa3
            return btay
        cachePath = os.path.join(self.path, name)
        idpath = os.path.join(MSCDIR, musicId + '.mp3')
        info = self.getInfoFromWeb(musicId)
        path = self.getPath(info, musicId)
        if not os.path.exists(path):
            with open(path, 'wb') as f:
                f.write(bytes(_decrypt(cachePath)))

        '''  get info from index file
        if not os.path.exists(idpath):
            with open(idpath, 'wb') as f:
                f.write(bytes(_decrypt(cachePath)))
        try:
            info = dict(MP3(idpath, ID3=EasyID3))
        except:
            info = {}
        if info != {}:
            path = self.getPath(info, musicId)
            if os.path.exists(path):
                os.remove(idpath)
            else:
                os.rename(idpath, path)
        else:
            os.remove(idpath)
        '''
        return info, path

    def getLyric(self, musicId):
        url = 'http://music.163.com/api/song/lyric?id=' + musicId + '&lv=1&tv=-1'
        lrc = ''
        try:
            lyric = requests.get(url, headers=headers).json()
            lrc = lyric['lrc']['lyric']
            tlrc = lyric['tlyric']['lyric']
            # merge multi-lang lyrics
            dic = {}
            for i in lrc.splitlines():
                a = i.replace('[', ']').strip().split("]")
                dic[a[1].strip()+' '] = a[-1].strip()
            tdic = {}
            for m in tlrc.splitlines():
                n = m.replace('[', ']').strip().split(']')
                tdic[n[1].strip()] = n[-1].strip()
            dicCopy = dic.copy()
            dicCopy.update(tdic)
            lines = []
            for k, v in sorted(dicCopy.items(), key=lambda item: item[0]):
                lines.append("[%s]%s" % (k.strip(), v))
            lrc = "\n".join(lines)
        except Exception as e:
            pass
        return lrc

    def setID3(self, lrc, info, path):
        tags = ID3(path)
        # remove old unsychronized lyrics
        len(tags.getall("USLT")) != 0 and tags.delall("USLT")
        for t in ['album', 'title', 'artist']:
            t in info and tags.add(
                tagMap[t](encoding=3, lang='', desc='', text=info[t][0]))
        'cover' in info and tags.add(APIC(
            encoding=3,
            mime='image/png',
            type=3,
            desc='cover',
            data=requests.get(info['cover'][0], stream=True,
                              headers=headers).raw.read()
        ))
        tags.add(USLT(encoding=3, lang='eng', desc='aaa', text=lrc))
        tags.save()

    def getMusic(self):
        ct = 0  # count converted files
        for musicId, name in self.id_name.items():
            try:
                info, path = self.decrypt(musicId, name)
                ct += 1
                print('[{}]'.format(ct).ljust(6) + self.id_name[musicId])
                self.setID3(self.getLyric(musicId), info, path)
            except Exception as e:
                pass

def main(path=''):
    if not path:
        pre = '/'.join(os.getcwd().split(os.sep)[:3])
        if os.sys.platform.lower().startswith('win'):  # windows
            path = pre + '/AppData/Local/Netease/CloudMusic/Cache/Cache'
        else:  # mac or linux
            path = pre + '/Library/Containers/com.netease.163music/Data/Caches/online_play_cache'
    if os.path.isdir(path):
        netease_music(path).getMusic()
    else:
        print('Directory "{}" does not exist, specify cache files directory instead'.format(path))

if __name__ == '__main__':
    main() if len(sys.argv) < 2 else main(sys.argv[1])


#여행경로 (Level 3)

# tickets = [["ICN", "JFK"], ["HND", "IAD"], ["JFK", "HND"]]
tickets = [["ICN", "SFO"], ["ICN", "ATL"], ["SFO", "ATL"], ["ATL", "ICN"], ["ATL","SFO"]]
# tickets = [["ICN", "AOO"], ["AOO", "BOO"], ["BOO", "COO"], ["COO", "DOO"], ["DOO", "EOO"], ["EOO", "DOO"], ["DOO", "COO"], ["COO", "BOO"], ["BOO", "AOO"]]

def solution(tickets):
    answer = []

    hash = {}
    for t in tickets:
        if t[0] not in hash:
            hash[t[0]] = [t[1]]
        else:
            hash[t[0]].append(t[1])
    for h in hash:
        hash[h].sort()

    stack = ["ICN"]
    while stack:
        src = stack.pop(0)
        answer.append(src)
        if src not in hash:
            continue
        if len(hash[src]) != 0:
            stack.append(hash[src].pop())

    return answer

print("FINAL:",solution(tickets))


######################################################################
# Verification of UAV track planning model based on DQN
#---------------------------------------------------------------
# author by younghow
# email: younghowkg@gmail.com
# --------------------------------------------------------------
#将训练好的DQN模型放入仿真模拟环境中进行测试，
#可以使用env类中的reset_test函数对测试环境进行设置，
#生成测试环境的UAV的起点与终点，并根据难度等级生成城市环境的建筑数目与风况。
#----------------------------------------------------------------
# Put the trained DQN model into the simulation environment for testing.
# You can use the reset_test function in the env class to set the test environment,
# generate the starting point and end point of the UAV of the test environment,
# and generate the number of buildings,the number of buildings and wind conditions
#  in the urban environment according to the difficulty level.
##############################################################################
import numpy as np
import torch
import matplotlib
import matplotlib.pyplot as plt
import time
from env import *
import torch
LEARNING_RATE = 0.00033   #学习率
num_episodes = 80000  #训练周期长度
space_dim = 42 # n_spaces   状态空间维度
action_dim = 27 # n_actions   动作空间维度
threshold = 200
env = Env(space_dim,action_dim,LEARNING_RATE)

if __name__ == '__main__':
    check_point_Qlocal=torch.load('Qlocal.pth')
    check_point_Qtarget=torch.load('Qtarget.pth')
    env.q_target.load_state_dict(check_point_Qtarget['model'])
    env.q_local.load_state_dict(check_point_Qlocal['model'])
    env.optim.load_state_dict(check_point_Qlocal['optimizer'])
    epoch=check_point_Qlocal['epoch']
    #真实场景运行
    env.level= 8 #环境难度等级
    state = env.reset_test()  #环境重置1
    total_reward = 0
    env.render(1)
    n_done=0
    count=0

    n_test=1  #测试次数
    n_creash=0   #坠毁数目
    for i in range(n_test):
        while(1):
            if env.uavs[0].done:
                #无人机已结束任务，跳过
                break
            action = env.get_action(FloatTensor(np.array([state[0]])) , 0.01)   #根据Q值选取动作

            next_state, reward, uav_done, info= env.step(action.item(),0)  #根据选取的动作改变状态，获取收益

            total_reward += reward  #求总收益
            #交互显示
            print(action)
            env.render()
            plt.pause(0.01)
            if uav_done:
                break
            if info==1:
                success_count=success_count+1

            state[0] = next_state  #状态变更
        print(env.uavs[0].step)
        env.ax.scatter(env.target[0].x, env.target[0].y, env.target[0].z,c='red')
        plt.pause(100)


# -*- coding: utf-8 -*-
import pytest

from intertwine.geos.models import Geo, GeoData, GeoID, GeoLevel
from intertwine.utils.space import GeoLocation

@pytest.mark.unit
@pytest.mark.parametrize("parent_name, parent_abbrev, child_name", [
    ('Parent Test Republic', 'PTR', 'Child Test Geo'),
])
def test_geo_model_create(session, parent_name, parent_abbrev, child_name):
    """Test simple geo model interaction"""
    parent = Geo(name=parent_name, abbrev=parent_abbrev)
    child = Geo(name=child_name, path_parent=parent, parents=[parent])

    session.add(parent)
    session.add(child)
    session.commit()

    assert Geo[parent.derive_key()] is parent
    assert Geo[child.derive_key()] is child
    assert parent[child_name] is child

    parent_from_db = (
        session
        .query(Geo)
        .filter(Geo.human_id == parent.human_id)
        .first()
    )

    child_from_db = (
        session
        .query(Geo)
        .filter(Geo.human_id == child.human_id)
        .first()
    )

    assert parent_from_db is parent
    assert parent_from_db.name == parent_name
    assert parent_from_db.abbrev == parent_abbrev
    assert parent_from_db.uses_the is True
    assert parent_from_db.human_id == parent_abbrev.lower()
    assert parent_from_db.path_children.all()[0] is child_from_db
    assert parent_from_db.children.all()[0] is child_from_db

    assert child_from_db is child
    assert child_from_db.name == child_name
    assert child_from_db.abbrev is None
    assert child_from_db.uses_the is False
    assert child_from_db.human_id == Geo.PATH_DELIMITER.join(
                [child.path_parent.human_id,
                 child.name.lower().replace(' ', '_')])

    assert child_from_db.path_parent is parent_from_db
    assert child_from_db.parents.all()[0] is parent_from_db

@pytest.mark.unit
@pytest.mark.smoke
def test_geo_data_model(session):
    """Test simple geo data model interaction"""
    total_pop, urban_pop = 1000, 800
    latitude, longitude = 30.0, -97.0
    land_area, water_area = 4321, 1234

    geo = Geo(name='Test Geo')
    GeoData(geo=geo,
            total_pop=total_pop,
            urban_pop=urban_pop,
            latitude=latitude,
            longitude=longitude,
            land_area=land_area,
            water_area=water_area)

    session.add(geo)
    session.commit()

    assert geo.data.total_pop == total_pop
    assert geo.data.urban_pop == urban_pop
    assert geo.data.latitude.value == latitude
    assert geo.data.longitude.value == longitude
    assert geo.data.land_area == land_area
    assert geo.data.water_area == water_area

    assert GeoData[geo.data.derive_key()] is geo.data

    geo_data_from_db = session.query(GeoData).filter(
                            GeoData.geo == geo).first()

    assert geo_data_from_db is geo.data
    assert geo_data_from_db.total_pop == geo.data.total_pop
    assert geo_data_from_db.urban_pop == geo.data.urban_pop
    assert geo_data_from_db.latitude == geo.data.latitude
    assert geo_data_from_db.longitude == geo.data.longitude
    assert geo_data_from_db.land_area == geo.data.land_area
    assert geo_data_from_db.water_area == geo.data.water_area

@pytest.mark.unit
@pytest.mark.smoke
def test_geo_level_model(session):
    """Test simple geo level model interaction"""
    level = 'place'
    designation = 'city'
    geo = Geo(name='Test Geo Place')
    glvl = GeoLevel(geo=geo, level=level, designation=designation)

    session.add(geo)
    session.add(glvl)
    session.commit()

    assert geo.levels[level] is glvl
    assert glvl.geo is geo
    assert glvl.level == level
    assert glvl.designation == designation

    assert GeoLevel[glvl.derive_key()] is glvl

    glvl_from_db = session.query(GeoLevel).filter(
                    GeoLevel.geo == geo, GeoLevel.level == level).first()

    assert glvl_from_db is glvl
    assert glvl_from_db.geo is geo
    assert glvl_from_db.level == level
    assert glvl_from_db.designation == designation

@pytest.mark.unit
@pytest.mark.smoke
def test_geo_id_model(session):
    """Test simple geo id model interaction"""
    TEST_STANDARD = 'Test Standard'

    GeoID.STANDARDS.add(TEST_STANDARD)
    standard = TEST_STANDARD
    code = '12345'

    geo = Geo(name='Test Geo Place')
    glvl = GeoLevel(geo=geo, level='place', designation='city')
    gid = GeoID(level=glvl, standard=standard, code=code)

    session.add(geo)
    session.add(glvl)
    session.add(gid)
    session.commit()

    assert gid.level is glvl
    assert gid.standard == standard
    assert gid.code == code

    assert GeoID[gid.derive_key()] is gid
    assert GeoID[(standard, code)] is gid

    gid_from_db = session.query(GeoID).filter(
                    GeoID.standard == standard, GeoID.code == code).first()

    assert gid_from_db is gid
    assert gid_from_db.level is glvl
    assert gid_from_db.standard == standard
    assert gid_from_db.code == code

@pytest.mark.unit
@pytest.mark.smoke
def test_form_aggregate_geo(session):
    """Test geo creation that aggregates children data at a geo level"""
    data_level = 'place'

    child_a_dict = {'total_pop': 100,
                    'urban_pop': 80,
                    'latitude': 42.0,
                    'longitude': -71.0,
                    'land_area': 321,
                    'water_area': 123}

    child_a_geo = Geo(name='Child Test Geo A')
    GeoData(geo=child_a_geo, **child_a_dict)
    GeoLevel(geo=child_a_geo, level='place', designation='village')

    child_b_dict = {'total_pop': 300,
                    'urban_pop': 240,
                    'latitude': 44.0,
                    'longitude': -73.0,
                    'land_area': 645,
                    'water_area': 21}

    child_b_geo = Geo(name='Child Test Geo B')
    GeoData(geo=child_b_geo, **child_b_dict)
    GeoLevel(geo=child_b_geo, level='place', designation='town')

    child_c_dict = {'total_pop': 1000,
                    'urban_pop': 800,
                    'latitude': 30.0,
                    'longitude': -97.0,
                    'land_area': 4321,
                    'water_area': 1234}

    child_c_geo = Geo(name='Child Test Geo C')
    GeoData(geo=child_c_geo, **child_c_dict)
    GeoLevel(geo=child_c_geo, level='subdivision2', designation='county')

    child_a_area = child_a_dict['land_area'] + child_a_dict['water_area']
    child_b_area = child_b_dict['land_area'] + child_b_dict['water_area']

    geo_location_a = GeoLocation(child_a_dict['latitude'],
                                 child_a_dict['longitude'])
    geo_location_b = GeoLocation(child_b_dict['latitude'],
                                 child_b_dict['longitude'])

    geo_location = GeoLocation.combine_locations(
        (geo_location_a, child_a_area),
        (geo_location_b, child_b_area))

    sum_fields = ('total_pop', 'urban_pop', 'land_area', 'water_area')

    aggregate_dict = {f: child_a_dict[f] + child_b_dict[f] for f in sum_fields}

    aggregate_dict['latitude'], aggregate_dict['longitude'] = (
        geo_location.coordinates)

    parent_geo = Geo(name='Parent Test Geo',
                     children=[child_a_geo, child_b_geo, child_c_geo],
                     child_data_level=data_level)  # aggregate places only

    session.add(parent_geo)
    session.add(child_a_geo)
    session.add(child_b_geo)
    session.add(child_c_geo)
    session.commit()

    parent_data = parent_geo.data
    assert parent_data is not None
    assert parent_data.total_pop == aggregate_dict['total_pop']
    assert parent_data.urban_pop == aggregate_dict['urban_pop']
    assert parent_data.latitude == aggregate_dict['latitude']
    assert parent_data.longitude == aggregate_dict['longitude']
    assert parent_data.land_area == aggregate_dict['land_area']
    assert parent_data.water_area == aggregate_dict['water_area']

    parent_geo_data_from_db = session.query(GeoData).filter(
        GeoData.geo == parent_geo).first()

    assert parent_geo_data_from_db is not None
    assert parent_geo_data_from_db is parent_geo.data
    assert parent_geo_data_from_db.total_pop == aggregate_dict['total_pop']
    assert parent_geo_data_from_db.urban_pop == aggregate_dict['urban_pop']
    assert parent_geo_data_from_db.latitude == aggregate_dict['latitude']
    assert parent_geo_data_from_db.longitude == aggregate_dict['longitude']
    assert parent_geo_data_from_db.land_area == aggregate_dict['land_area']
    assert parent_geo_data_from_db.water_area == aggregate_dict['water_area']

@pytest.mark.unit
@pytest.mark.smoke
def test_geo_aliases(session):
    """Test creation of geo aliases and promoting an alias"""
    geo_data_dict = {'total_pop': 100,
                     'urban_pop': 80,
                     'latitude': 42,
                     'longitude': -71,
                     'land_area': 321,
                     'water_area': 123}

    level = 'place'
    geo = Geo(name='Test Geo')
    gdata = GeoData(geo=geo, **geo_data_dict)
    glvl = GeoLevel(geo=geo, level=level, designation='city')

    parent_geo = Geo(name='Test Parent Geo', abbrev='TPG')
    geo.path_parent = parent_geo
    geo.parents = [parent_geo]

    sibling_geo = Geo(name='Test Sibling Geo')
    sibling_geo.path_parent = parent_geo
    sibling_geo.parents = [parent_geo]

    child_geo = Geo(name='Test Child Geo')
    child_geo.path_parent = geo
    child_geo.parents = [geo]

    geo_alias_1 = Geo(name='Test Geo Alias 1', alias_targets=[geo],
                      path_parent=geo)
    geo_alias_2 = Geo(name='Test Geo Alias 2', alias_targets=[geo],
                      path_parent=parent_geo)
    geo_alias_3 = Geo(name='Test Geo Alias 3', alias_targets=[geo],
                      path_parent=parent_geo)

    session.add(geo)
    session.commit()

    assert geo.data is gdata
    assert geo.levels[level] is glvl
    assert parent_geo in geo.parents
    assert geo in child_geo.parents

    assert geo.path_parent is parent_geo
    assert child_geo.path_parent is geo
    assert geo_alias_1.path_parent is geo
    assert geo_alias_2.path_parent is parent_geo
    assert geo_alias_3.path_parent is parent_geo

    assert geo_alias_1.alias_targets[0] is geo
    assert geo_alias_2.alias_targets[0] is geo
    assert geo_alias_3.alias_targets[0] is geo

    aliases = geo.aliases.all()
    assert geo_alias_1 in aliases
    assert geo_alias_2 in aliases
    assert geo_alias_3 in aliases

    geo_from_db = session.query(Geo).filter_by(
        human_id=geo.human_id).first()

    aliases = geo_from_db.aliases.all()
    assert geo_alias_1 in aliases
    assert geo_alias_2 in aliases
    assert geo_alias_3 in aliases

    geo_alias_1.promote_to_alias_target()
    assert not geo_alias_1.alias_targets
    assert geo_alias_2.alias_targets[0] is geo_alias_1
    assert geo_alias_3.alias_targets[0] is geo_alias_1
    assert geo.alias_targets[0] is geo_alias_1

    assert geo_alias_1.data is gdata
    assert geo_alias_1.levels[level] is glvl
    assert parent_geo in geo_alias_1.parents
    assert geo_alias_1 in child_geo.parents

    # paths are unchanged by design
    assert geo.path_parent is parent_geo
    assert child_geo.path_parent is geo
    assert geo_alias_1.path_parent is geo
    assert geo_alias_2.path_parent is parent_geo
    assert geo_alias_3.path_parent is parent_geo


#!/usr/bin/env python

import time
import json
import serial
import sqlite3
import requests
import subprocess
import adafruit_gps
import Adafruit_DHT

#initial data
kilometraje = 0
latitud = 0.0
longitud = 0.0
altitud = 0.0
velocidad = 0
tanqueConductor = 0.0
tanquePasajero = 0.0
fecha = ""
hora = ""
enviado = 0
timePost = 60
timeUpdate = 600
outPost = 15
outUpdate = 60
ipTest = "190.143.75.85"
headers = {'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest'}
url = "https://pruebas.extranet.com.co/coreiot/api/public/EYT.Tracker/Registro/Registro/Nuevo"
dataBase = "/home/pi/coreiot/itemdb"
idItem = "e67d85d2-cf20-4027-be3e-c488e2d64a80"
sensor = Adafruit_DHT.DHT11
gpioSensor = 17
countGps = 0

time.sleep(5)
print('-' * 5+'START'+'-' * 5, flush=True)

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

def post_request(body, tOut):
    success = False
    try:
        r = requests.post(url, data=json.dumps(body), headers=headers, timeout=tOut)
        r.raise_for_status()
        if(r.json()['Error']):
            print('Error', flush=True)
        else:
            flag = 1
            success = True
    except requests.exceptions.HTTPError as errh:
        print ("Http Error:",errh, flush=True)
    except requests.exceptions.ConnectionError as errc:
        print ("Error Connecting:",errc, flush=True)
    except requests.exceptions.Timeout as errt:
        print ("Timeout Error:",errt, flush=True)
    except requests.exceptions.RequestException as err:
        print ("OOps: Something Else",err, flush=True)
    finally:
        if not success:
            flag = 0
    return flag

def watcher():
    connection = False
    try:
        pingRes = subprocess.check_output("ping 190.143.75.85 -c3 -q | grep received | awk -F ',' '{print $2}' | awk '{print $1}'", shell=True)
        if int(pingRes) > 0:
            connection = True
        else:
            print("Network down", flush=True)
    except:
        print("Network Test Error", flush=True)
    if connection:
        cursor.execute("SELECT id, Kilometraje, Latitud, Longitud, Altitud, TanqueConductor, TanquePasajero, Velocidad, Fecha, Hora FROM item WHERE Enviado = 0")
        results = cursor.fetchall()
        if len(results) > 0:
            idList = []
            jsondict = {}
            for val in results:
                idList.append(val['id'])
                del val['id']
            jsondict['Registros'] = results
            jsondict['idItem'] = 'e67d85d2-cf20-4027-be3e-c488e2d64a80'
            #Update
            updated = post_request(jsondict, outUpdate)
            if updated==1:
                sql="UPDATE item SET Enviado = 1 WHERE id IN ({seq})".format(seq=','.join(['?']*len(idList)))
                cursor.execute(sql, idList)
                dbconnect.commit()
                print('{} Updated'.format(len(idList)), flush=True)
            else:
                print('Error POST', flush=True)
        else:
            print('Up to date', flush=True)

# serial uart access
uart = serial.Serial("/dev/ttyS0", baudrate=9600, timeout=3000)
# Create a GPS module instance.
gps = adafruit_gps.GPS(uart, debug=False)
# Turn on the basic GGA and RMC info
gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Set update rate to once a second (1hz)
gps.send_command(b'PMTK220,1000')

#database set up
dbconnect = sqlite3.connect(dataBase, check_same_thread=False)
dbconnect.row_factory = dict_factory
cursor = dbconnect.cursor()

lastPost = time.monotonic()
lastUpdate = time.monotonic()
while True:
    # Every 60s post, 600s update
    gps.update()
    if not gps.has_fix:
        countGps += 1
        time.sleep(1)
        if countGps == 100:
            raise Exception('Error GPS timeout')
        continue
    countGps = 0
    current = time.monotonic()
    currentUpdate = time.monotonic()
    if current - lastPost >= timePost:
        lastPost = current
        #Fetch data from sensor
        tanqueConductor, tanquePasajero = Adafruit_DHT.read_retry(sensor, gpioSensor)
        #Fetch data from gps
        latitud = gps.latitude
        longitud = gps.longitude
        if gps.speed_knots is not None:
            velocidad = int(gps.speed_knots * 1.852)
        if gps.altitude_m is not None:
            altitud = gps.altitude_m
        fecha = '{}/{}/{}'.format(
            gps.timestamp_utc.tm_mday,
            gps.timestamp_utc.tm_mon,
            gps.timestamp_utc.tm_year)
        hora = '{:02}:{:02}:{:02}'.format(
            gps.timestamp_utc.tm_hour,
            gps.timestamp_utc.tm_min,
            gps.timestamp_utc.tm_sec)

        payload = {'IdItem':idItem,
         'Registros':[{
             'Kilometraje':kilometraje,
             'Latitud':latitud,
             'Longitud':longitud,
             'Altitud':altitud,
             'TanqueConductor':tanqueConductor,
             'TanquePasajero':tanquePasajero,
             'Velocidad':velocidad,
             'Fecha':fecha,
             'Hora':hora
             }]
         }

        #POST
        enviado = post_request(payload, outPost)
        if enviado == 0:
            print('Enviado: {}'.format(enviado), flush=True)

        #execute insert statement
        cursor.execute("INSERT INTO item (Kilometraje, Latitud, Longitud, TanqueConductor, TanquePasajero, Velocidad, Altitud, Fecha, Hora, Enviado)VALUES(?,?,?,?,?,?,?,?,?,?)",
                       (kilometraje, latitud, longitud, tanqueConductor, tanquePasajero, velocidad, altitud, fecha, hora, enviado))
        dbconnect.commit()

        #print('Id: {}'.format(idItem), flush=True)
        #print('Kilometraje: {}'.format(kilometraje), flush=True)
        #print('Latitud: {0:.6f}'.format(latitud), flush=True)
        #print('Longitud: {0:.6f}'.format(longitud), flush=True)
        #print('Tanque Conductor: {}'.format(tanqueConductor), flush=True)
        #print('Tanque Pasajero: {}'.format(tanquePasajero), flush=True)
        #print('Velocidad: {} km/h'.format(velocidad), flush=True)
        #print('Altitud: {} metros'.format(altitud), flush=True)
        #print('Fecha: {}'.format(fecha), flush=True)
        #print('Hora: {}'.format(hora), flush=True)

    if currentUpdate - lastUpdate >= timeUpdate:
        lastUpdate = currentUpdate
        watcher()

#close the connection
dbconnect.close()


# stdlib Imports
import datetime
import math
import time

# Twisted Imports
from twisted.internet import defer

# PythonCollector Imports
from ZenPacks.zenoss.PythonCollector.datasources.PythonDataSource import PythonDataSourcePlugin

class FakeData(PythonDataSourcePlugin):
    @classmethod
    def config_key(cls, datasource, context):
        return (
            context.device().id,
            datasource.getCycleTime(context),
            context.id,
            datasource.id,
            )

    def collect(self, config):
        data = self.new_data()
        now = time.time()

        for datasource in config.datasources:
            for point in datasource.points:
                dpname = "{}_{}".format(datasource.datasource, point.id)
                data["values"][datasource.component][dpname] = {
                    "ten": 10,
                    "twenty": 20,
                    "fifty": 50,
                    "hundred": 100,
                    "epoch": now,
                    "pct1day": getpercentofday(),
                    "cos1min": getcos(now / 60),
                    "cos5min": getcos(now / 300),
                    "cos10min": getcos(now / 600),
                    "cos1hour": getcos(now / 3600),
                    }.get(point.id, None)

        return defer.succeed(data)

def getcos(n):
    """Return cosine of n between 0-10 instead of 0.0 and 1.0."""
    return (math.cos(n * math.pi) * 10) + 10

def getpercentofday():
    """Return the percent of the way through the day (0-100)."""
    now = datetime.datetime.now()
    return (now.hour * 3600 + now.minute * 60 + now.second) / 864.0


# -*- coding: utf-8 -*-
"""
Created on Sat Dec  3 22:34:00 2016

@author: iralp
"""
import pandas as pd
from copy import deepcopy
from sklearn import preprocessing
import warnings
from scipy import spatial
import numpy as np
import operator
from sklearn import decomposition

warnings.filterwarnings("ignore")
#Data Science Project
#For a given pair of players,get the most comparable players

playerDataBat = None
playerDataPitch = None

def normalizeColumns(playerDataBatCopy,reqColumns):
    for each_column in reqColumns:
        #replace invalid values with valid ones
        for indx in range(len(playerDataBatCopy)):
            if playerDataBatCopy.iloc[indx][each_column] == 'NULL':
                playerDataBatCopy.iloc[indx][each_column] = 0
        #processing for Nans
        playerDataBatCopy[each_column] = playerDataBatCopy[each_column].fillna(value = 0)
        print('column  :',each_column)
        #preprocessing.normalize(playerDataBatCopy[each_column],axis = 0)
        #print(" Values : ",len(playerDataBatCopy[each_column]))
        #playerDataBatCopy[each_column] = preprocessing.normalize(playerDataBatCopy[each_column],axis = 0)
        print(len(playerDataBatCopy[each_column]))
    print('Normalization Done!')

def scoreWithBatters(playerID,playerDataBatCopy,reqColumns,year_of_trade):
    #count = 0
    #sum_ = 0
    reqColumns = ['WAR']
    #, 5, 6, 7, 8, 9
    dict_of_scores = {}
    print('player1 : ',playerID)
    #get the player data
    player1_ = playerDataBatCopy[playerDataBatCopy['player_ID'] == playerID]
    player1_data = player1_[player1_['year_ID'] <= year_of_trade]
    print('player1_data : ',len(player1_data))
    #print('player1_data : ',player1_data)

    #sort by age
    player1_data = player1_data.sort(['age'])
    #for each year,get the corresponding similarity score
    for indx in range(len(player1_data)):
        #get the age
        age = player1_data.iloc[indx]['age']
        vec1 = player1_data.iloc[indx][reqColumns]
        #print('age : ',age)

        #get rows with same age
        rows_ = playerDataBatCopy[playerDataBatCopy['age'] == age]
        rows_with_age = rows_[rows_['player_ID'] != playerID]

        for index,row in rows_with_age.iterrows():
            #print('row : ',row)
            name = row['name_common']
            #print('name : ',name,' age:',age,' WAR : ',row['WAR'])
            vec2 = row[reqColumns]
            #print('vec1 :',np.array(vec1))
            #print('vec2: ',np.array(vec2))
            score = 1 - spatial.distance.cosine(np.array(vec1),np.array(vec2))
            #print('score : ',score)
            if name not in dict_of_scores.keys():
                dict_of_scores[name] = (score,1)
            else:
                tup = dict_of_scores[name]
                no = tup[1] + 1
                sco = (tup[0] + score)
                dict_of_scores[name] = (sco,no)

    for each_key in dict_of_scores:
        tp = dict_of_scores[each_key]
        dict_of_scores[each_key] = ((tp[0]/tp[1]),no)

    #return the top ten most comparable players
    sorted_dict = sorted(dict_of_scores.items(),key = operator.itemgetter(1),reverse = True)
    return sorted_dict[:10]

def getNextWAR(playerID,pl1_comparables,playerDataBatCopy,year_of_trade):
    #get the current age of player1
    player1_ = playerDataBatCopy[playerDataBatCopy['player_ID'] == playerID]
    player1_data = player1_[player1_['year_ID'] <= year_of_trade]

    #sort by age
    player1_data = player1_data.sort(['age'],ascending=False)
    if len(player1_data) == 0:
        print('No comparables')
        return

    age_p1 = player1_data.iloc[0]['age']
    WAR_years = []
    #print('comparables : ',pl1_comparables)
    #average the WAR for this for all the 10 most comparables
    for ind in range(1,4):
        cosine_sum = 0
        next_age = age_p1 + ind
        WAR_ = 0.0
        count = 0

        for indx in range(len(pl1_comparables)):

            each_player = pl1_comparables[indx][0]
            #print('each_player : ',each_player)
            player_ = playerDataBatCopy[playerDataBatCopy['name_common'] == each_player]
            player_data = player_[player_['age'] == next_age]
            if len(player_data) == 0:
                continue

            print('player_data[WAR] : ',player_data['copied_WAR'])
            WAR_ += (player_data.iloc[0]['copied_WAR'])
            cosine_sum += pl1_comparables[indx][1][0]
            count += 1
            print('WAR_predict : ',WAR_)
        if cosine_sum == 0:
            cosine_sum = 1
        if count == 0:
            count = 1
        WAR_ = WAR_/count
        WAR_years.append(WAR_)

    return WAR_years

def getComparablePlayers(playerID,year_of_trade):
    #Use all features and compute some similarity score after normalizing the features=>
    #but consider only data corresponding to the same age
    global playerDataBat
    global playerDataPitch
    #load all the data
    playerDataBat = pd.DataFrame().from_csv(r'C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/player_daily_bat_Normalized.csv',encoding='utf8',index_col = False)
    playerDataPitch = pd.DataFrame().from_csv(r'C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/player_daily_pitch_Normalized.csv',encoding='utf8',index_col = False)
    playerDataBatCopy = deepcopy(playerDataBat)
    playerDataPitchCopy = deepcopy(playerDataPitch)

    playerID = str(playerID).strip()
    print('get comparable players for : ',playerID)

    #Now determine if either player is a hitter or a pitcher
    player1_rows = playerDataBat[playerDataBat['player_ID'] == playerID]

    print('player1 length : ',len(player1_rows))

    if len(player1_rows) == 0:
        player1_rows = playerDataPitch[playerDataPitch['player_ID'] == playerID]

    print('player1 length : ',len(player1_rows))

    #Normalize the required columns for batters
    reqColumns = list(playerDataBat.columns)
    reqColumns.remove('name_common')
    reqColumns.remove('age')
    reqColumns.remove('mlb_ID')
    reqColumns.remove('player_ID')
    reqColumns.remove('year_ID')
    reqColumns.remove('team_ID')
    reqColumns.remove('stint_ID')
    reqColumns.remove('lg_ID')
    reqColumns.remove('salary')
    reqColumns.remove('pitcher')
    reqColumns.remove('teamRpG')
    reqColumns.remove('oppRpG')
    reqColumns.remove('oppRpPA_rep')
    reqColumns.remove('oppRpG_rep')
    reqColumns.remove('pyth_exponent')
    reqColumns.remove('pyth_exponent_rep')

    #Normalize the required columns for batters
    reqColumns_pitchers = list(playerDataPitchCopy.columns)
    reqColumns_pitchers.remove('name_common')
    reqColumns_pitchers.remove('age')
    reqColumns_pitchers.remove('mlb_ID')
    reqColumns_pitchers.remove('player_ID')
    reqColumns_pitchers.remove('year_ID')
    reqColumns_pitchers.remove('team_ID')
    reqColumns_pitchers.remove('stint_ID')
    reqColumns_pitchers.remove('lg_ID')
    reqColumns_pitchers.remove('salary')

    playerDataBatCopy['copied_WAR'] = playerDataBatCopy['WAR']
    playerDataPitchCopy['copied_WAR'] = playerDataPitchCopy['WAR']
    normalizeColumns(playerDataBatCopy,['WAR'])
    normalizeColumns(playerDataPitchCopy,['WAR'])
    '''matrix_of_batters = np.matrix(playerDataBatCopy[reqColumns])
    pca = decomposition.PCA(n_components = 5)
    pca_fit_batters = pca.fit_transform(matrix_of_batters)
    print(pca_fit_batters.shape)

    matrix_of_pitchers = np.matrix(playerDataPitchCopy[reqColumns_pitchers])
    pca = decomposition.PCA(n_components = 5)
    pca_fit_pitchers = pca.fit_transform(matrix_of_pitchers)
    print(pca_fit_pitchers.shape)

    pitchers_frame = pd.DataFrame(pca_fit_pitchers)
    pitchers_frame['age'] = playerDataPitchCopy['age']
    pitchers_frame['name_common'] = playerDataPitchCopy['name_common']
    pitchers_frame['player_ID'] = playerDataPitchCopy['player_ID']
    pitchers_frame['year_ID'] = playerDataPitchCopy['year_ID']
    print(pitchers_frame.columns)

    batters_frame = pd.DataFrame(pca_fit_batters)
    batters_frame['age'] = playerDataBatCopy['age']
    batters_frame['name_common'] = playerDataBatCopy['name_common']
    batters_frame['player_ID'] = playerDataBatCopy['player_ID']
    batters_frame['year_ID'] = playerDataBatCopy['year_ID']
    print(batters_frame.columns)
    '''

    pl1_comparables = None
    pl1_WAR = None

    #check if the player is a pitcher or a batter
    if len(player1_rows) == 0:
        return None
    player1IsPitcher = player1_rows.iloc[0]['pitcher']

    #if batter consider the rows corresponding to war_bat.csv
    if player1IsPitcher == 'N':
        pl1_comparables = scoreWithBatters(playerID,playerDataBatCopy,reqColumns,year_of_trade)
        pl1_WAR = getNextWAR(playerID,pl1_comparables,playerDataBatCopy,year_of_trade)
    else:
        pl1_comparables = scoreWithBatters(playerID,playerDataPitchCopy,reqColumns_pitchers,year_of_trade)
        pl1_WAR = getNextWAR(playerID,pl1_comparables,playerDataPitchCopy,year_of_trade)

    print('pl1_WAR : ',pl1_WAR)
    return pl1_WAR

#averaged over 3 years
def getActualWAR(playerID,year):
    playerID = str(playerID).strip()
    print('get comparable players for : ',playerID)

    #Now determine if either player is a hitter or a pitcher
    player1_rows = playerDataBat[playerDataBat['player_ID'] == playerID]

    print('player1 length : ',len(player1_rows))

    if len(player1_rows) == 0:
        player1_rows = playerDataPitch[playerDataPitch['player_ID'] == playerID]

    print('player1 length : ',len(player1_rows))

    actual_WAR = []
    #Now get the average WAR over next 3 years
    for ind in range(1,4):
        print('n th  year : ',ind)
        yr = year + ind
        row = player1_rows[player1_rows['year_ID'] == yr]
        if len(row) == 0:
            actual_WAR.append(0.0)
            continue
        WAR = row['WAR']
        WAR = str(WAR).split()
        actual_WAR.append(float(WAR[1]))

    return actual_WAR

def rmse(predictions, targets):
    return np.sqrt(((np.array(predictions) - np.array(targets)) ** 2).mean())

def getrmse(players_results,predict,actual):
    predicted = []
    targets = []

    for indx in range(len(players_results)):
        if players_results.iloc[indx][predict] != None and players_results.iloc[indx][predict] != 'nan':
            try:
                predicted.append(float(players_results.iloc[indx][predict]))
            except :
                predicted.append(0.0)
        else:
            predicted.append(0.0)

        if players_results.iloc[indx][actual] != None and players_results.iloc[indx][predict] != 'nan':
            try:
                targets.append(float(players_results.iloc[indx][actual]))
            except:
                targets.append(0.0)
        else:
            targets.append(0.0)

    print('players_results predict: ',players_results[predict])
    print('players_results actual: ',players_results[actual])

    rmse_score = rmse(np.array(predicted),np.array(targets))
    return rmse_score

def validatePreviousData():
    players_results = pd.DataFrame(columns = ['playerID','player_name','predicted WAR_1',\
    'actual WAR_1','predicted WAR_2','actual WAR_2','predicted WAR_3','actual WAR_3',\
    'start_year','trade_year','from_team','to_team','transaction-ID','rmse_scores_1',\
    'rmse_scores_2','rmse_scores_3'])
    columns = ['primary-date',  'time', 'approximate-indicator',  'secondary-date',  'approximate-indicator (for secondary-date)',\
               'transaction-ID',  'player', 'type',  'from-team',  'from-league',  \
               'to-team',  'to-league','draft-type',  \
               'draft-round', 'pick-number',  'info']

    file_count = 0
   #load the csv into a data frame
    data = pd.read_csv('C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/transactions/tran.txt',header = 0,\
       parse_dates = True,names = columns)
    #data cleaning
    data['time'].fillna(0,inplace = True)
    data['approximate-indicator'].fillna(0,inplace = True)
    data['secondary-date'].fillna(0,inplace = True)
    data['approximate-indicator (for secondary-date)'].fillna(0,inplace = True)
    data['draft-round'].fillna("NA",inplace = True)
    data['info'].fillna("NA",inplace = True)
    data['pick-number'].fillna("NA",inplace = True)
    data['draft-type'].fillna("NA",inplace = True)

    years = [int(str(x)[:4]) for x in data['primary-date']]
    months = [int(str(x)[4:6]) for x in data['primary-date']]

    #create a new column with year,month of trade
    data['year'] = years
    data['month'] = months

    #load the master data to get player name from his id
    master_data = pd.read_csv('C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/baseballdatabank-master/core/Master.csv',\
        index_col = False)
    #usecols = ['retroID','nameGiven']

    #consider trades in ranges 2005-2010
    data_temp = data[data['year'] >= 2010]
    data_trades_05_10 = data_temp[data_temp['year'] <= 2012]

    transactions = set(data_trades_05_10['transaction-ID'])
    count = 0

    for indx,row in data_trades_05_10.iterrows():
        #for each trade,get the players => calculate their WAR for next 1-3 years and validate with original WAR
        #for each transaction id get the players involved
            for each_trade in transactions:
                #get the cooresponding rows
                trades = data_trades_05_10[data_trades_05_10['transaction-ID'] == each_trade]
                #check the trade type and skip if not T
                if trades.iloc[0]['type'] != 'T ':
                    continue
                #get the players

                for indx,each_ in trades.iterrows():
                    playerID = each_['player']
                    #get the player name from
                    print('playerID: ',playerID)
                    player_data_indx = master_data[master_data['retroID'] == playerID].index
                    if player_data_indx == None:
                        continue
                    print('player_data : ',player_data_indx)
                    playerName = master_data.iloc[player_data_indx]['playerID']
                    print('playerName : ',playerName)
                    playr_ID = str(playerName).split()
                    #TODO get the comparable players also
                    player_WAR = getComparablePlayers(playr_ID[1],each_['year'])

                    if player_WAR == None:
                        continue

                    #computer the actual WAR average for next three years
                    actual_WAR = getActualWAR(playr_ID[1],each_['year'])

                    player_result = []
                    player_result.append(playr_ID[1] + " ")
                    player_result.append(master_data.iloc[player_data_indx]['nameGiven'])
                    player_result.append(str(player_WAR[0]) + "")
                    player_result.append(str(actual_WAR[0]) + "")
                    player_result.append(str(player_WAR[1]) + "")
                    player_result.append(str(actual_WAR[1]) + "")
                    player_result.append(str(player_WAR[2]) + "")
                    player_result.append(str(actual_WAR[2]) + "")

                    player_result.append(str(int(each_['year']) + 1)  + " ")
                    player_result.append(each_['year'])
                    player_result.append(each_['from-team'])
                    player_result.append(each_['to-team'])
                    player_result.append(each_trade)
                    player_result.append("0")
                    player_result.append("0")
                    player_result.append("0")

                    players_results.loc[count] = player_result

                    count += 1
                    print('length of frame :',len(players_results))
                    if count == 100:
                        rmse_score_1 = getrmse(players_results,'predicted WAR_1','actual WAR_1')
                        rmse_score_2 = getrmse(players_results,'predicted WAR_2','actual WAR_2')
                        rmse_score_3 = getrmse(players_results,'predicted WAR_3','actual WAR_3')
                        players_results['rmse_scores_1'] = [rmse_score_1]*100
                        players_results['rmse_scores_2'] = [rmse_score_2]*100
                        players_results['rmse_scores_3'] = [rmse_score_3]*100

                        players_results.to_csv('C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/Results_'+str(file_count)+'.csv',encoding = 'utf8')
                        count = 0
                        file_count += 1

    return players_results

data_results = validatePreviousData()
#data_results.to_csv('C:/Personal_Docs/Course_Work/Data Science/Course_Project/Data/Results_5.csv',encoding = 'utf8')
print('Done!!')


from game import Square, Peg

class ElementFactory:
    def __init__(
        self,
        peg: str = "X",
        square: str = "O",
        empty: str = "  ",
    ) -> None:
        self.peg = peg
        self.square = square
        self.empty = empty

    def get_element(self, square: Square | None) -> str:
        if square is None:
            return self.empty
        if isinstance(square, Peg):
            return self.peg
        return self.square


import cv2

img = cv2.imread("lena.jpg",-1) #flag: 1-color image;0-gray image;-1-image as such including alpha channel
# print(img) #pixels of the image

cv2.imshow('image',img) #flash the image
k = cv2.waitKey(0)&0xFF #cv2.waitKey(0) to wait for a key event
                        # 5000: 5 seconds, 0:forever stays
                        # 0xFF: if using 64 system machine

if k == 27: #escape key
    cv2.destroyAllWindows() #close the windows
elif k == ord('s'): #if s is pressed
    cv2.imwrite('lena_copy.png',img)


from MeDIT.ImageProcess import ResizeNiiFile, ResizeROINiiFile
import SimpleITK as sitk
import os
import numpy as np
from tkinter import _flatten
import shutil

def PathAcquire(path):
    pathlist = list(_flatten([[os.path.join(root, file) for file in files] for root, dirs, files in os.walk(path)
                              if len(files) > 0]))
    return pathlist

def GetResolution(root_pathlist):
    if type(root_pathlist) == str:
        root_pathlist = [root_pathlist]
    resolution = [list(sitk.ReadImage(path).GetSpacing()) for path in root_pathlist]
    return resolution

def ExpectedResolution(root_pathlist):
    resolution_list = GetResolution(root_pathlist)
    resolution_array = np.array(resolution_list)
    resolution_median = np.median(resolution_array, axis=0)
    resolution_var = np.var(resolution_array, axis=0)
    resolution_down = resolution_median - 10 * resolution_var
    resolution_up = resolution_median + 10 * resolution_var
    print(resolution_up)
    print(resolution_down)
    count = 0
    resolution_sum = np.zeros((1, 3))
    for path in root_pathlist:
        resolution = list(_flatten(GetResolution(path)))
        if resolution_down[0] < resolution[0] < resolution_up[0] and resolution_down[2] < resolution[2] < resolution_up[2]:
            resolution_sum += resolution
            count += 1
    resolution_range = resolution_sum / count
    return resolution_range.flatten()

# def RemoveWrongNiiShapeFile(root_pathlist):
#     for path in root_pathlist:
#         if GetResolution(path)[0][2] >= 2.0:
#             os.remove(path)

def GenerateDir(rootpath, storepath):
    for root, dirs, files in os.walk(rootpath):
        for file in files:
            filename, ext = os.path.splitext(file)
            foldername, _ = os.path.splitext(filename)
            if len(files) > 0:
                rootpath_old = os.path.join(root, file)
                storepath_new = os.path.join(storepath, foldername)
                if not os.path.isdir(storepath_new):
                    os.makedirs(storepath_new)
                shutil.copy(rootpath_old, storepath_new)

# def ResizeFile(store_pathlist, resolution):
#     for store_path in store_pathlist:
#         if os.path.splitext(store_path)[1] == '.gz':
#             ResizeNiiFile(store_path, expected_resolution=resolution)
#         elif os.path.splitext(store_path)[1] == '.nii':
#             ResizeROINiiFile(store_path, expected_resolution=resolution)
#         else:
#             print('the input file should be suffix .nii or .nii.gz')
#         os.remove(store_path)

def ResizeFile(store_pathlist):
    for store_path in store_pathlist:
        if os.path.splitext(store_path)[1] == '.gz':
            ResizeNiiFile(store_path, expected_resolution=[0.6834329, 0.6834329, 1.3852941])
        elif os.path.splitext(store_path)[1] == '.nii':
            ResizeROINiiFile(store_path, expected_resolution=[0.6834329, 0.6834329, 1.3852941])
        else:
            print('the input file should be suffix .nii or .nii.gz')
        os.remove(store_path)

rootpath = 'D:/JSYB'
storepath = 'Z:/JSYB_Resize'
# rootpath = r'D:\ZJ_assignment'
# storepath = r'D:\ZJ_assignment_resize'

# root_pathlist = PathAcquire(rootpath)
# RemoveWrongNiiShapeFile(root_pathlist)

# root_pathlist_new = PathAcquire(rootpath)
# resolution = ExpectedResolution(root_pathlist_new)

GenerateDir(rootpath, storepath)

store_pathlist = PathAcquire(storepath)
ResizeFile(store_pathlist)


import nntplib

from modules import *
import torch.nn.functional as F

class ST_encoder(nn.Module):
    def __init__(self, n_features, d_model, window):
        super(ST_encoder, self).__init__()
        self.linear = nn.Linear(n_features, d_model)
        self.tcna = TemporalConvNet(num_inputs=d_model, num_channels=[d_model], d_model=d_model, kernel_size=2,
                                   attention=True)
        self.tcnb = TemporalConvNet(num_inputs=d_model, num_channels=[d_model], d_model=d_model, kernel_size=2,
                                   attention=True)
        self.sgnn = SGNN(d_model)
        self.position = PositionalEncoding(d_model=d_model, max_len=window)
        encoder_layers = nn.TransformerEncoderLayer(d_model=d_model, nhead=1, dim_feedforward=256, dropout=0.3)
        self.transformer_encoder = nn.TransformerEncoder(encoder_layers, 2)

        decoder_layers1 = nn.TransformerDecoderLayer(d_model=d_model, nhead=1, dim_feedforward=256, dropout=0.3)
        self.transformer_decoder1 = nn.TransformerDecoder(decoder_layers1, 1)
        decoder_layers2 = nn.TransformerDecoderLayer(d_model=d_model, nhead=1, dim_feedforward=256, dropout=0.3)
        self.transformer_decoder2 = nn.TransformerDecoder(decoder_layers2, 1)

    def forward(self, x):
        x = F.relu(self.linear(x))
        x_a = self.tcna(x.permute(0, 2, 1)).permute(0, 2, 1)
        x_b = self.tcna(x.permute(0, 2, 1)).permute(0, 2, 1)
        x = torch.tanh(x_b)*torch.sigmoid(x_a)
        x = self.sgnn(x)
        x = self.position(x)
        x = self.transformer_encoder(x.permute(1, 0, 2))
        mu = self.transformer_decoder1(x[-1, :, :].unsqueeze(0), x)
        logvar = self.transformer_decoder1(x[-1, :, :].unsqueeze(0), x)
        return mu.squeeze(), logvar.squeeze()

class STAD(nn.Module):
    def __init__(self, n_features, window, d_model, n_latent=16):
        super(STAD, self).__init__()
        self.window = window
        self.d_model = d_model
        self.n_latent = n_latent
        self.n_hidden = n_latent * 4
        self.n_features = n_features
        self.linear = nn.Linear(n_features, d_model)
        self.ST_encoder = ST_encoder(d_model, d_model, window)
        self.linear2 = nn.Linear(d_model,n_features)
        self.linear3 = nn.Linear(d_model,n_features)
        self.end = nn.Sequential(

            # nn.Linear(d_model, d_model), nn.ReLU(),
            # nn.Linear(d_model, n_features), nn.ReLU(),
            nn.Linear(n_features, n_features), nn.Sigmoid(),
        )

    def forward(self, x):
        x = F.relu(self.linear(x))
        mu, logvar = self.ST_encoder(x)
        mu = self.linear2(mu)
        logvar = self.linear3(logvar)
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        z = mu + eps * std
        x = self.end(z.squeeze())
        return x, mu, logvar


import sys
import json

# Simple WSGI application.
# Derived from https://peps.python.org/pep-3333/#error-handling
def simple_app(environ, start_response):
    try:
        path_info = environ.get("PATH_INFO", "")
        if path_info == "/error":
            # When "/error" is requested, force an exception to be thrown,
            # which then falls into the except clause below.
            1 / 0

        # regular application code here
        status = "200 Froody"
        response_headers = [("content-type", "application/json")]
        start_response(status, response_headers)
        data = json.dumps({"msg": "Hello, Simple WSGI!"})
        return data.encode("UTF-8")
    except:
        # XXX should trap runtime issues like MemoryError, KeyboardInterrupt
        #     in a separate handler before this bare 'except:'...
        status = "500 Oops"
        response_headers = [("content-type", "application/json")]
        start_response(status, response_headers, sys.exc_info())
        data = json.dumps({"msg": "Oh, no! What have you done, Simple WSGI?"})
        return data.encode("UTF-8")

app = simple_app


from django.db import models
from rest_framework import status

from oarndb.models.base_model import BaseModel
from oarndb.models import Organization, OrganizationPersonRole

class Person(BaseModel):
    """
    The most important table of the whole system.
    All children, adults, and staff have a person record.
    """

    person_id = models.AutoField('person_id', primary_key=True)

    first_name = models.CharField('first_name', max_length=35, null=False)

    middle_name = models.CharField('middle_name', max_length=35, null=True, blank=True)

    last_name = models.CharField('last_name', max_length=35, null=False)

    # 'Jr.', 'III', etc.
    generation_code = models.CharField('generation_code', max_length=10, null=True, blank=True)

    prefix = models.CharField('prefix', max_length=30, null=True, blank=True)

    birth_date = models.DateField('birth_date', auto_now=False, auto_now_add=False, null=True, blank=True)

    ref_gender = models.ForeignKey('RefGender', verbose_name='ref_gender',
                                   blank=True, null=True, on_delete=models.SET_NULL)

    organization = models.ManyToManyField(Organization, through='OrganizationPersonRole', related_name='organizations')

    def get_instance_access(self, user):
        """
        Returns the read/write access for this instance and this user.

        :param user: django.contrib.auth.models.User
        :return: {read: Boolean, write: Boolean}
        """
        # Global admins can read anything:
        if user.groups.filter(name='global_admin').count() > 0:
            return {'read': True, 'write': True}
        # Does the user have admin or read/write access to this Person's org?
        elif (Organization.objects.get_readwrite_orgs(user) & self.organization.all()).count() > 0:
            return {'read': True, 'write': True}
        # Does the user have read-only rights to this Person's org?
        elif (Organization.objects.get_read_orgs(user) & self.organization.all()).count() > 0:
            return {'read': True, 'write': False}
        # If nothing else, is the user a member of global_readonly?
        elif user.groups.filter(name='global_readonly').count() > 0:
            return {'read': True, 'write': False}
        else:
            return {'read': False, 'write': False}

    @staticmethod
    def get_validated_data_access(user, validated_data):
        """
        Returns details on whether the supplied user has the write permissions needed
        to update/create with this validated_data

        As a model with a many-to-many relationship, Person requires a POST with

        :param user: django.contrib.auth.models.User
        :param validated_data: from the serializer
        :return: {write:Boolean, error: Boolean, error_msg: String, status_code: rest_framework.status}
        """
        # Creating a person record requires only that the user has *some* writable permissions.
        # The key will be in the linking of the new Person record with an organization, which must
        # be done via a POST to the OrganizationPersonRole view - without that, the new Person record
        # will not be visible to the user who created it. (This is not an ideal way to approach this,
        # in my view - we should be able to POST nested JSON data to a single endpoint and have the
        # nested data result in new records in both tables, but DRF currently appears only to support
        # hackish solutions to this problem, so we'll do it in two POSTs to two endpoints for now.)
        # Once a Person record is linked to an organization, updates require write access to that
        # organization.

        # Does the validated data reference an existing record?
        pk = validated_data.get('person_id')
        if pk is None:
            return {'write':False, 'error': True, 'error_msg': 'it is None!', 'status_code': status.HTTP_400_BAD_REQUEST}
        else:
            return {'write':False, 'error': True, 'error_msg': pk, 'status_code': status.HTTP_400_BAD_REQUEST}

    class Meta:
        db_table = "person"
        ordering = ["last_name", "first_name"]

    def __str__(self):
        return self.first_name + " " + self.last_name + " (" + str(self.person_id) + ")"

class Child(BaseModel):
    """
    Allows us to separate children from the set of person records.
    """

    person = models.OneToOneField('Person', primary_key=True, verbose_name='person')

    class Meta:
        db_table = "child"

    def __str__(self):
        return self.person.__str__()

class Adult(BaseModel):
    """
    Allows us to separate adults from the set of person records.
    """

    person = models.OneToOneField('Person', primary_key=True, verbose_name='person')

    class Meta:
        db_table = "adult"

    def __str__(self):
        return self.person.__str__()

class PersonRace(BaseModel):
    """
    The federal categories for race.
    """

    person = models.OneToOneField('Person', primary_key=True, verbose_name='person')

    hispanic_latino_ethnicity = models.BooleanField('hispanic_latino_ethnicity', default=False)

    american_indian = models.BooleanField(default=False)

    asian = models.BooleanField(default=False)

    black = models.BooleanField(default=False)

    pacific = models.BooleanField(default=False)

    white = models.BooleanField(default=False)

    multiracial = models.BooleanField(default=False)

    unreported = models.BooleanField(default=False)

    other = models.BooleanField(default=False)

    other_details = models.CharField(max_length=100, null=True, blank=True)

    class Meta:
        db_table = "person_race"

    def __str__(self):
        race_list = ""

        if self.american_indian:
            race_list = race_list + 'american_indian; '

        if self.asian:
            race_list = race_list + 'asian; '

        if self.black:
            race_list = race_list + 'black; '

        if self.pacific:
            race_list = race_list + 'pacific; '

        if self.white:
            race_list = race_list + 'white; '

        if self.other:
            race_list = race_list + 'other; '

        return self.person.__str__() + ': ' + race_list

class Family(BaseModel):
    """
    The family table acts as a hub around which any number of person
    records may be associated.
    """

    family_id = models.AutoField('family_id', primary_key=True)

    notes = models.CharField('notes', max_length=10000, null=True, blank=True)

    organizations = models.ManyToManyField("Organization", through="OrganizationFamilyLink")

    class Meta:
        db_table = "family"

    def __str__(self):
        return str(self.family_id)

class AdultFamilyRelationship(BaseModel):
    """
    Describes a relationship between an individual adult and a family.
    """

    adult_family_relationship_id = models.AutoField('adult_family_relationship_id', primary_key=True)

    adult = models.ForeignKey('Adult', verbose_name='adult')

    family = models.ForeignKey('Family', verbose_name='family')

    ref_adult_family_relationship_type = models.ForeignKey(
        'RefAdultFamilyRelationshipType',
        verbose_name='ref_adult_family_relationship_type_id',
        null=True,  # This may not be a necessary field at all, so nullable for now
        blank=True,
        on_delete=models.SET_NULL
    )

    primary_adult = models.BooleanField('primary_adult', default=False)

    relationship_begin_date = models.DateField(
        'relationship_begin_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    relationship_end_date = models.DateField(
        'relationship_end_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    notes = models.CharField('notes', max_length=10000, null=True, blank=True)

    class Meta:
        db_table = 'adult_family_relationship'

    def __str__(self):
        return self.adult.__str__() + ' linked to family ' + self.family.__str__()

class ChildFamilyRelationship(BaseModel):
    """
    Describes a relationship between an individual child and a family.
    This is where foster or adoptive relationships can be modeled.
    """

    child_family_relationship_id = models.AutoField('child_family_relationship_id', primary_key=True)

    child = models.ForeignKey('Child', verbose_name='child')

    family = models.ForeignKey('Family', verbose_name='family')

    ref_child_family_relationship_type = models.ForeignKey(
        'RefChildFamilyRelationshipType',
        verbose_name='ref_child_family_relationship_type',
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )

    relationship_begin_date = models.DateField(
        'relationship_begin_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    relationship_end_date = models.DateField(
        'relationship_end_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    notes = models.CharField('notes', max_length=10000, null=True, blank=True)

    class Meta:
        db_table = 'child_family_relationship'

    def __str__(self):
        return self.child.__str__() + ' linked to family ' + self.family.__str__()

class AdultChildRelationship(BaseModel):
    """
    Describes the relationship between a given adult and a given child.
    """
    adult_child_relationship_id = models.AutoField('adult_child_relationship_id', primary_key=True)

    adult = models.ForeignKey('Adult', verbose_name='adult')

    child = models.ForeignKey('Child', verbose_name='child')

    ref_adult_child_relationship_type = models.ForeignKey(
        'RefAdultChildRelationshipType',
        verbose_name='ref_adult_child_relationship_type',
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )

    can_pick_up = models.BooleanField('can_pick_up', default=True)  # Do we want to whitelist?

    legal_custody = models.BooleanField('legal_custody', default=True)  # Do we want to whitelist?

    physical_custody = models.BooleanField('physical_custody', default=True)  # Do we want to whitelist?

    relationship_begin_date = models.DateField(
        'relationship_begin_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    relationship_end_date = models.DateField(
        'relationship_end_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    notes = models.CharField('notes', max_length=10000)

    class Meta:
        db_table = 'adult_child_relationship'

    def __str__(self):
        return self.adult.__str__() + ' linked to child ' + self.child.__str__()

class PersonDemographicRace(BaseModel):
    """
    A lookup table for race as opposed to ethnicity, following CEDS and the
    federal guidelines.
    """

    person_demographic_race_id = models.AutoField('person_demographic_race_id', primary_key=True)

    person = models.ForeignKey('Person', verbose_name='person')

    ref_race = models.ForeignKey('RefRace', verbose_name='ref_race',
                                 blank=True, null=True, on_delete=models.SET_NULL)

    class Meta:
        db_table = 'person_demographic_race'

        # a person cannot be a given race multiple times:
        unique_together = (("person", "ref_race"))

    def __str__(self):
        return self.person.__str__() + ': ' + self.ref_race.__str__()

class PersonLanguage(BaseModel):
    """
    Represents a language spoken by a person.
    """

    person_language_id = models.AutoField('person_language_id', primary_key=True)

    person = models.ForeignKey('Person', verbose_name='person')

    ref_language = models.ForeignKey('RefLanguage', verbose_name='ref_language_id',
                                     blank=True, null=True, on_delete=models.SET_NULL)

    other_details = models.CharField(max_length=100, null=True, blank=True)

    ref_language_use_type = models.ForeignKey(
        'RefLanguageUseType',
        verbose_name='ref_language_use_type',
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )

    class Meta:
        db_table = 'person_language'

        # a person cannot be assigned the same language twice:
        unique_together = (("person", "ref_language"))

    def __str__(self):
        return self.person.__str__() + ': ' + self.ref_language.__str__() \
               + ' at ' + self.ref_language_use_type.__str__()

class PersonPregnancy(BaseModel):
    """
    An adult may be associated with a pregancy record.
    """

    person_pregnancy_id = models.AutoField('person_pregnancy_id', primary_key=True)

    adult = models.ForeignKey('Adult', verbose_name='adult')

    pregnancy_end_date = models.DateField(
        'pregnancy_end_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    pregnancy_notes = models.CharField('pregnancy_notes', max_length=5000)

    class Meta:
        db_table = 'person_pregnancy'

    def __str__(self):
        return self.adult.__str__() + ': person_pregnancy_id (' + \
               str(self.person_pregnancy_id) + ')'

class ChildPregnancyRelationship(BaseModel):
    """
    A pregnancy may result in more than one child.
    """

    child_pregnancy_relationship_id = models.AutoField(
        'child_pregnancy_relationship_id',
        primary_key=True
    )

    person_pregnancy = models.ForeignKey(
        "PersonPregnancy",
        verbose_name='person_pregnancy'
    )

    child = models.ForeignKey(
        "Child",
        verbose_name='child'
    )

    class Meta:
        db_table = 'child_pregnancy_relationship'

    def __str__(self):
        return self.child.__str__() + ': ' + \
               self.person_pregnancy.__str__()

class PersonTelephone(BaseModel):
    """
    A person may be linked to zero or more telephone numbers.
    """

    person_telephone_id = models.AutoField(
        'person_telephone_id',
        primary_key=True
    )

    person = models.ForeignKey("Person", verbose_name='person')

    telephone_number = models.CharField('telephone_number', max_length=24, null=False, blank=False)

    ref_person_telephone_number_type = models.ForeignKey(
        "RefPersonTelephoneNumberType",
        verbose_name='ref_person_telephone_number_type_id',
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )

    class Meta:
        db_table = 'person_telephone'

    def __str__(self):
        return self.person.__str__() + ': ' + self.telephone_number + \
               ' (' + self.ref_person_telephone_number_type.__str__() + ')'

class PersonEmailAddress(BaseModel):
    """
    A person may be linked to zero or more email addresses.
    """

    person_email_address_id = models.AutoField(
        'person_email_address_id',
        primary_key=True
    )

    person = models.ForeignKey("Person", verbose_name='person')

    email_address = models.EmailField('email_address', max_length=75)

    ref_email_type = models.ForeignKey("RefEmailType", verbose_name='ref_email_type',
                                       blank=True, null=True)

    class Meta:
        db_table = 'person_email_address'

    def __str__(self):
        return self.person.__str__() + ': ' + self.email_address + \
               ' (' + self.ref_email_type.__str__() + ')'

class FamilyAddress(BaseModel):
    """
    A family may be lined to zero or more physical/mailing addresses.
    """
    family_address_id = models.AutoField('family_address_id', primary_key=True)

    family = models.ForeignKey("Family", verbose_name='family')

    ref_location_type = models.ForeignKey(
        "RefLocationType",
        verbose_name='ref_location_type',
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )

    street_number_and_name = models.CharField('street_number_and_name', max_length=40)

    apartment_room_or_suite_number = models.CharField(
        'apartment_room_or_suite_number',
        max_length=30,
        null=True,
        blank=True
    )

    city = models.CharField('city', max_length=30)

    ref_state = models.ForeignKey(
        "RefState",
        verbose_name='ref_state',
        null=True,
        blank=True,
        on_delete=models.SET_NULL
    )

    postal_code = models.CharField('postal_code', max_length=17, null=True, blank=True)

    # Dropped from the SQL 'address_county_name' - this should be solely by reference table lookup.
    ref_county = models.ForeignKey("RefCounty", verbose_name='ref_county',
                                   blank=True, null=True, on_delete=models.SET_NULL)

    residence_end_date = models.DateField(
        'residence_end_date',
        auto_now=False,
        auto_now_add=False,
        null=True,
        blank=True
    )

    # Should this address appear in the family summary view?
    primary_address = models.BooleanField('primary_address', default=False)

    notes = models.CharField('notes', max_length=10000, blank=True, null=True)

    class Meta:
        db_table = 'family_address'

    def __str__(self):
        return 'Family ID ' + self.family.__str__() + ': ' +  self.street_number_and_name