import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
from google.auth.transport.requests import Request
from apscheduler.schedulers.background import BackgroundScheduler
from ics import Calendar
from pick import pick
import requests
import signal
import html
import arrow
from redis import Redis

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/calendar.events']
sched = BackgroundScheduler()

redis = Redis.from_url(os.getenv('REDIS_URL') or 'redis://')

# This is copy/pasted from https://developers.google.com/calendar/quickstart/python#step_3_set_up_the_sample
def handle_google_auth():
    creds = None
    # we store the user's access and refresh tokens in redis and is added automatically
    # when the authorization flow completes for the first time.
    token = redis.get(redis_key('token'))
    if token:
      creds = pickle.loads(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = OAuth2WebServerFlow(client_id=os.environ['GOOGLE_CLIENT_ID'],
                                       client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
                                       scope=SCOPES,
                                       redirect_uri='urn:ietf:wg:oauth:2.0:oob')
            auth_uri = flow.step1_get_authorize_url()
            print(auth_uri)
            code = input("code: ")
            creds = flow.step2_exchange(code)

        # Save the credentials for the next run
        pickled = pickle.dumps(creds)
        redis.set(redis_key('token'), pickled)

    return build('calendar', 'v3', credentials=creds)

def get_google_calendar_id():
    cal_id = redis.get(redis_key('calendar_id'))
    if cal_id:
      return cal_id.decode()
    calendars = service.calendarList().list().execute()
    option, index = pick([c['summary'] for c in calendars['items']], 'Pick the correct calendar: ')
    cal_id = calendars['items'][index]['id']
    redis.set(redis_key('calendar_id'), cal_id)
    return cal_id

# Helpers
def redis_key(suffix):
  return 'discuss_cal_sync:{}'.format(suffix)

def fetch_discuss_calendar():
    url = "{}/calendar.ics?time_zone=America/Los_Angeles".format(os.environ['DISCUSS_URL'])
    cookies = { "_t": os.environ['DISCUSS_TOKEN'] }
    resp = requests.get(url, cookies=cookies)
    return Calendar(resp.text)

def transform_to_google_event(discuss_event):
  return {
      'summary': html.unescape(discuss_event.name),
      'location': discuss_event.location,
      'description': html.unescape(discuss_event.description),
      'start': {
          'dateTime': discuss_event.begin.format(fmt="YYYY-MM-DDTHH:mm:ssZZ"),
          'timeZone': 'America/Los_Angeles',
      },
      'end': {
          'dateTime': discuss_event.end.format(fmt="YYYY-MM-DDTHH:mm:ssZZ"),
          'timeZone': 'America/Los_Angeles',
      },
  }

def dedupe_key(gevent):
    return "::".join([
        gevent['summary'],
        gevent.get('description', ''),
        gevent['start']['dateTime'],
        gevent['end']['dateTime'],
    ])

@sched.scheduled_job('cron', minute="*/5")
def sync_calendar():
    print("Starting sync")
    calendarId = get_google_calendar_id()
    if calendarId == None:
        return

    service = handle_google_auth()

    # Fetch and normalize discuss events
    c = fetch_discuss_calendar()
    events_from_discuss = [transform_to_google_event(e) for e in c.events if e.begin > arrow.now()]
    events_from_discuss = { dedupe_key(e): e for e in events_from_discuss }

    # Fetch and normalize google events
    now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
    events_from_google = {
        dedupe_key(e): e
        for e in service.events().list(calendarId=calendarId, timeMin=now).execute()['items']
        if e['status'] == 'confirmed'
    }

    # Diff the two to prevent creating dupe calendar events and add any new events found to gcal
    diff = set(events_from_discuss) - set(events_from_google)
    for k in diff:
        print("Adding calendar event")
        service.events().insert(calendarId=calendarId, body=events_from_discuss[k]).execute()

if __name__ == '__main__':
    # Auth with google and pick which google calendar to write to
    service = handle_google_auth()

    # Force a fetch of the google calendar to trigger selection if needed
    get_google_calendar_id()

    # Kick off an immediate sync
    sync_calendar()

    # Start the cron
    sched.start()

    # Sleep indefinitely
    while True:
        signal.pause()


from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from nodeeditor.utils import dumpException
from vpl_node import *
from conf import *
from model.variables import VariablesData
from model.node_data import NodeData
from nodeeditor.node_node import *

import re

class SwitchNodeContent(QDMNodeContentWidget):
    TotalIfs = 1

    def __init__(self, parent, variablesRef):
        self.vars = variablesRef
        super().__init__(parent)

    def initUI(self):
        self.layout = QVBoxLayout()

        self.comboBox = []
        self.comboBox.append(QComboBox(self))
        self.initialList = ['true', 'false']

        for var in self.vars.variables:
            self.initialList.append(var)

        self.initialList.append('value')
        self.comboBox[0].addItems(self.initialList)

        self.edit = []
        self.edit.append(QLineEdit('', self))
        self.comboBox[0].setLineEdit(self.edit[0])

        self.edit[0].setAlignment(Qt.AlignCenter)
        self.subBtn = QPushButton('-', self)
        self.addBtn = QPushButton('+', self)
        self.elseLbl = QLabel('Else', self)
        self.elseLbl.setAlignment(Qt.AlignCenter)

        self.layout.addWidget(self.comboBox[0])

        self.groupBox = QGroupBox()
        self.layout.addWidget(self.groupBox)

        self.innerHbox = QHBoxLayout()
        self.groupBox.setLayout(self.innerHbox)

        self.innerHbox.addWidget(self.addBtn)
        self.innerHbox.addWidget(self.subBtn)
        self.innerHbox.addWidget(self.elseLbl)

        self.layout.setStretch(0, 1)
        self.layout.setStretch(1, 1)
        self.layout.setSizeConstraint(0)

        self.redrawComboBox()
        self.setLayout(self.layout)

    def redrawComboBox(self): # function displays new variables in dropdown. (GUI REFRESH)
        self.comboBox[self.TotalIfs - 1].clear()
        self.comboBox[self.TotalIfs - 1].addItems(['true', 'false'])
        for var in self.vars.variables:
            self.comboBox[self.TotalIfs - 1].addItem(var.name)
        self.comboBox[self.TotalIfs - 1].addItem('value')

    def setContentVariables(self, variables):
        self.vars = variables

    #I hope these are correct
    def serialize(self):
        res = super().serialize()
        values = []
        for i in range(len(self.edit)):
            values.append(self.edit[i].text())

        res['value'] = values
        return res

    def deserialize(self, data, hashmap={}):
        res = super().deserialize(data, hashmap)
        try:
            value = data['value']

            for i in range(len(value)):
                self.edit[i].setText(value[i])

                if i < len(value) - 1:
                    self.increaseWidgetSize()

            return True & res
        except Exception as e:
            dumpException(e)
        return res

    def registerButtons(self):
        self.addBtn.clicked.connect(self.increaseWidgetSize)

    def increaseWidgetSize(self):

        self.TotalIfs = self.TotalIfs + 1

        self.innerHbox.removeWidget(self.subBtn)
        self.subBtn.deleteLater()
        self.subBtn = None
        self.innerHbox.removeWidget(self.elseLbl)
        self.elseLbl.deleteLater()
        self.elseLbl = None
        self.innerHbox.removeWidget(self.addBtn)
        self.addBtn.deleteLater()
        self.addBtn = None
        self.layout.removeWidget(self.groupBox)
        self.groupBox.deleteLater()
        self.groupBox = None

        self.comboBox.append(QComboBox(self))
        self.comboBox[self.TotalIfs - 1].addItems(self.initialList)

        self.edit.append(QLineEdit('' , self))
        self.comboBox[self.TotalIfs - 1].setLineEdit(self.edit[self.TotalIfs - 1])

        self.edit[self.TotalIfs - 1].setAlignment(Qt.AlignCenter)

        self.subBtn = QPushButton('-', self)
        self.addBtn = QPushButton('+', self)
        self.elseLbl = QLabel('Else', self)

        self.layout.addWidget(self.comboBox[self.TotalIfs - 1])

        self.groupBox = QGroupBox()
        self.layout.addWidget(self.groupBox)

        self.innerHbox = None

        self.innerHbox = QHBoxLayout()
        self.groupBox.setLayout(self.innerHbox)

        self.innerHbox.addWidget(self.addBtn)
        self.innerHbox.addWidget(self.subBtn)
        self.innerHbox.addWidget(self.elseLbl)

        self.layout.setStretch(self.TotalIfs - 1, 1)
        self.layout.setStretch(self.TotalIfs, 1)

        self.layout.setSizeConstraint(0)

        self.redrawComboBox()

        self.registerButtons()

class SwitchNode(VplNode):
    icons = "Capstone/icons/in.png"
    op_code = OP_CODE_SWITCH
    op_title = "Switch"
    content_label_objname = "VplNodeSwitch"
    TotalOutputs = [1,1]

    def __init__(self, scene, title:str="If", outputList=TotalOutputs):
        self.variablesRef = VariablesData()
        super().__init__(scene, title, inputs = [0], outputs = outputList)

    def initInnerClasses(self):
        self.content = SwitchNodeContent(self, self.variablesRef)
        self.TotalIfs = self.content.TotalIfs
        self.grNode = VplGraphicsNode(self)

        self.grNode.height = 225
        self.grNode.width = 298
        self.data = NodeData() # THIS FIXES SCOPING ISSUE,
        self.data.nodeType = self.op_code

        self.registerButtons()

    def setVariableData(self, variables): # wires up stuff, see subWindow.py
        self.variablesRef = variables
        self.content.setContentVariables(self.variablesRef)

    def doEval(self, input=None):
        for index in range(len(self.content.edit)):
            statement = self.content.edit[index].text()

            if statement == 'true':
                self.data.val = index
                self.data.valType = TYPE_INT
                return
            elif statement == 'false':
                pass
            else:
                if 'value' in statement:
                    self.inp = input.val
                    statement = self.inp + "==" + statement

                for var in self.content.vars.variables:
                    statement = re.sub(fr'\bstate.{var.name}\b', str(var.val), statement)

                # issue with multiple variables sharing parts of the same name

                self.final = eval(statement)

                if(type(self.final) is bool):
                    if(self.final is True):
                        self.data.val = index
                        self.data.valType = TYPE_INT
                        return
                else:
                    print("ERROR: The statement in the text field does not result in a Boolean.")
                    self.data.val = None

        self.data.val = -1
        self.data.valType = TYPE_INT
        return

    def registerButtons(self):
        self.content.subBtn.clicked.connect(self.decreaseWidgetSize)
        self.content.addBtn.clicked.connect(self.increaseWidgetSize)

    def increaseWidgetSize(self):

        self.TotalIfs = self.TotalIfs + 1

        self.content.innerHbox.removeWidget(self.content.subBtn)
        self.content.subBtn.deleteLater()
        self.content.subBtn = None
        self.content.innerHbox.removeWidget(self.content.elseLbl)
        self.content.elseLbl.deleteLater()
        self.content.elseLbl = None
        self.content.innerHbox.removeWidget(self.content.addBtn)
        self.content.addBtn.deleteLater()
        self.content.addBtn = None
        self.content.layout.removeWidget(self.content.groupBox)
        self.content.groupBox.deleteLater()
        self.content.groupBox = None

        self.content.comboBox.append(QComboBox(self.content))
        self.content.comboBox[self.TotalIfs - 1].addItems(self.content.initialList)

        self.content.edit.append(QLineEdit('' , self.content))
        self.content.comboBox[self.TotalIfs - 1].setLineEdit(self.content.edit[self.TotalIfs - 1])

        self.content.edit[self.TotalIfs - 1].setAlignment(Qt.AlignCenter)

        self.content.subBtn = QPushButton('-', self.content)
        self.content.addBtn = QPushButton('+', self.content)
        self.content.elseLbl = QLabel('Else', self.content)

        self.content.layout.addWidget(self.content.comboBox[self.TotalIfs - 1])

        self.content.groupBox = QGroupBox()
        self.content.layout.addWidget(self.content.groupBox)

        self.content.innerHbox = None

        self.content.innerHbox = QHBoxLayout()
        self.content.groupBox.setLayout(self.content.innerHbox)

        self.content.innerHbox.addWidget(self.content.addBtn)
        self.content.innerHbox.addWidget(self.content.subBtn)
        self.content.innerHbox.addWidget(self.content.elseLbl)

        self.content.layout.setStretch(self.TotalIfs - 1, 1)
        self.content.layout.setStretch(self.TotalIfs, 1)

        self.TotalOutputs.insert(-2, 1)
        self.newSockets([0], self.TotalOutputs, True)

        self.grNode.height += 43

        self.content.layout.setSizeConstraint(0)

        self.registerButtons()

    def decreaseWidgetSize(self):

        if (self.TotalIfs > 1):
            self.content.innerHbox.removeWidget(self.content.subBtn)
            self.content.subBtn.deleteLater()
            self.content.subBtn = None
            self.content.innerHbox.removeWidget(self.content.elseLbl)
            self.content.elseLbl.deleteLater()
            self.content.elseLbl = None
            self.content.innerHbox.removeWidget(self.content.addBtn)
            self.content.addBtn.deleteLater()
            self.content.addBtn = None
            self.content.layout.removeWidget(self.content.groupBox)
            self.content.groupBox.deleteLater()
            self.content.groupBox = None

            self.content.layout.removeWidget(self.content.comboBox[self.TotalIfs - 1])
            self.content.comboBox[self.TotalIfs - 1].setParent(None)
            self.content.comboBox.pop()
            self.content.edit.pop()

            self.TotalIfs = self.TotalIfs - 1

            self.content.subBtn = QPushButton('-', self.content)
            self.content.addBtn = QPushButton('+', self.content)
            self.content.elseLbl = QLabel('Else', self.content)
            self.content.elseLbl.setAlignment(Qt.AlignCenter)

            self.content.layout.setSizeConstraint(3)

            self.content.groupBox = QGroupBox()
            self.content.layout.addWidget(self.content.groupBox)

            self.content.innerHbox = QHBoxLayout()
            self.content.groupBox.setLayout(self.content.innerHbox)

            self.content.innerHbox.addWidget(self.content.addBtn)
            self.content.innerHbox.addWidget(self.content.subBtn)
            self.content.innerHbox.addWidget(self.content.elseLbl)

            self.TotalOutputs.pop(-2)
            self.newSockets([0], self.TotalOutputs, True)

            self.grNode.height -= 43

            self.content.innerHbox.setSizeConstraint(3)

            self.registerButtons()

        else:
            print('Must have at least one if statement!')


"""
В отделе работают 3 сотрудника, которые получают заработную плату в рублях.
Требуется определить: на сколько зарплата самого высокооплачиваемого из
них отличается от самого низкооплачиваемого.
Для решения использовать списки. Наполнять список зарплатами 3 раза.
"""
fee = []
for i in range(3):
    a = int(input(f'Введите зарплату сотрудника {i + 1}: '))
    fee.append(a)

print(max(fee) - min(fee))

01_зряплата.py


"""Change `rate` to `grade`

Revision ID: da4abbdca2ad
Revises: 010d01c2d522
Create Date: 2023-10-16 11:47:51.510649

"""
from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "da4abbdca2ad"
down_revision: str | None = "010d01c2d522"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###t
    op.alter_column("teacher_product", "rate_counter", new_column_name="grade_counter")
    op.alter_column("teacher_product", "average_rate", new_column_name="average_grade")
    conn = op.get_bind()
    print(conn.engine.url)
    op.alter_column("student_product", "teacher_rate", new_column_name="teacher_grade")
    op.alter_column(
        "student_product",
        "teacher_rate_date",
        new_column_name="teacher_graded_at",
        type_=sa.DateTime,
        existing_type=sa.Date,
    )
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.alter_column("teacher_product", "grade_counter", new_column_name="rate_counter")
    op.alter_column("teacher_product", "average_grade", new_column_name="average_rate")
    op.alter_column("student_product", "teacher_grade", new_column_name="teacher_rate")
    op.alter_column(
        "student_product",
        "teacher_graded_at",
        new_column_name="teacher_rate_date",
        type_=sa.Date,
        existing_type=sa.DateTime,
    )
    # ### end Alembic commands ###


# 对有序序列进行查找，分治的思想

def binary_search(list, num):
    left = 0
    right = len(list) - 1
    while left <= right:  # 循环条件
        mid = (left + right) // 2  # 获取中间位置，数字的索引（序列前提是有序的）
        if num < list[mid]:  # 如果查询数字比中间数字小，那就去二分后的左边找，
            right = mid - 1  # 来到左边后，需要将右变的边界换为mid-1
        elif num > list[mid]:  # 如果查询数字比中间数字大，那么去二分后的右边找
            left = mid + 1  # 来到右边后，需要将左边的边界换为mid+1
        else:
            return mid  # 如果查询数字刚好为中间值，返回该值得索引
    return -1  # 如果循环结束，左边大于了右边，代表没有找到

def binary_search_rec(list, left, right, num):
    if left > right:  # 递归结束条件
        return -1
    mid = (left + right) // 2
    if num < list[mid]:
        right = mid - 1
    elif num > list[mid]:
        left = mid + 1
    else:
        return mid
    return binary_search_rec(list, left, right, num)
    # 这里之所以会有return是因为必须要接收值，不然返回None
    # 回溯到最后一层的时候，如果没有return，那么将会返回None

# 对于表长较大，分布比较均匀的查找表来说，平均性能优于折半查找
# 对于分布不均匀的数据，折半查找比较好
def binary_search_insert(list, num):
    length = len(list)
    low = 0
    heigth = length - 1
    time = 0
    while low <= heigth:
        time += 1
        # 插值计算公式
        # 根据离所求值得距离进行切分
        mid = low + int((heigth - low) * (num - list[low]) / (list[heigth] - list[low]))
        if num > list[mid]:
            low = mid + 1
        elif num < list[mid]:
            heigth = mid - 1
        else:
            print("times: %s" % time)
            return mid

# 斐波那契查找，更准确地猜测寻找的值落在那个区间
def binary_search_fib(list, num):
    # 生成斐波那契数列
    fib = lambda n: n if n < 2 else fib(n - 1) + fib(n - 2)
    key = 0
    while fib(key) < len(list):
        key += 1

    left = 0
    right = len(list) - 1
    while left <= right:
        # 当x在分隔的后半部分时，fib计算的mid值可能大于arr长度
        # 计算fib的值，这个值是大于等于list的长度的，所以使用时要对key-1
        mid = min(left + fib(key - 1) - 1, len(list) - 1)  # 这里fib(key-1)-1的-1其实就是去掉索引重复值
        if num < list[mid]:  # 查找值小于分割记录
            right = mid - 1
            key -= 1
        elif num > list[mid]:  # 查找值大于分割记录
            left = mid + 1
            key -= 2
        else:  # 查找值等于分割记录
            return mid
    return -1

# 寻找与给定值最接近的数，若有多个返回最小的
def binary_search_value(list, num):
    if list[0] > num:  # 要查找小于最小值，返回最小值
        return list[0]
    if list[len(list) - 1] < num:  # 要查找大于最大值，返回最大值
        return list[len(list) - 1]
    left = 0
    right = len(list) - 1
    while left < right - 1:
        mid = (left + right) // 2
        if num < list[mid]:
            right = mid
        elif num > list[mid]:
            left = mid
        elif num == list[mid]:
            return list[mid]
    if (num - list[left]) <= (list[right] - num):
        return list[left]
    else:
        return list[right]

if __name__ == '__main__':
    list = [11, 32, 51, 21, 42, 9, 5, 6, 7, 8]
    print(list)
    list.sort()
    print(list)
    num = int(input('输入要查找的数：'))
    # res = binary_search(list, num)
    # res = binary_search_rec(list, 0, len(lis) - 1, num)
    # res = binary_search_insert(list, num)
    res = binary_search_fib(list, num)
    # res = binary_search_value(list, num)
    print(res)
    if res == -1:
        print('未找到！')
    else:
        print('找到！')


from os import makedirs, listdir
from os.path import dirname, join
from languageflow.reader.tagged_corpus import TaggedCorpus
import argparse
import re

def preprocess(file):
    sentences = []
    for line in open(file):
        sentence = []
        line = line.strip()
        line = re.sub(r"_+", "_", line)
        if not line:
            continue
        tokens = line.strip().split(" ")
        words = []
        for token in tokens:
            if token.startswith("//"):
                word = "/"
            else:
                word = token.split("/")[0]
            words.append(word)
        for word in words:
            syllabels = word.split("_")
            syllabels = [item for item in syllabels if item]
            for i, syllabel in enumerate(syllabels):
                label = "B-W" if i == 0 else "I-W"
                sentence.append([syllabel, label])
        sentences.append(sentence)
    return sentences

def raw_to_corpus(sample, output):
    if output:
        output_folder = output
    else:
        output_folder = join(dirname(dirname(__file__)), "tmp", "vlsp2013")
    try:
        makedirs(output_folder)
    except Exception as e:
        pass
    raw_folders = ["Trainset-POS-full", "Testset-POS"]
    output_names = ["train.txt", "test.txt"]
    data_folder = join(dirname(dirname(__file__)), "data", "vlsp2013", "raw")
    for i, raw_folder in enumerate(raw_folders):
        tagged_corpus = TaggedCorpus()
        sentences = []
        files = listdir(join(data_folder, raw_folder))
        files = [join(data_folder, raw_folder, file) for file in files]
        for file in files:
            sentences += preprocess(file)
            if sample != None:
                if len(sentences) > sample:
                    sentences = sentences[:sample]
                    break
        tagged_corpus.sentences = sentences
        output_file = join(output_folder, output_names[i])
        tagged_corpus.save(output_file)
        print("{} sentences is saved to file {}".format(len(sentences), output_file))

if __name__ == '__main__':
    parser = argparse.ArgumentParser("preprocess_vlsp2013.py")
    parser.add_argument("--sample", help="sample size", type=int)
    parser.add_argument("--output", help="output path")
    args = parser.parse_args()
    if args.sample:
        if not args.output:
            parser.error("You must set --output when use option --sample")
    raw_to_corpus(sample=args.sample, output=args.output)


""" Create the test data needed for test_search_info_10_15_7.py """

# reads data from the author's system photo library to build the test data
# used to test SearchInfo
# run as:
# python3 tests/generate_search_info_test_data.py >tests/search_info_test_data_10_15_7.json

import json

import osxphotos

UUID = [
    "DC09F4D8-6173-452D-AC15-725C8D7C185E",
    "AFECD4AB-937C-46AF-A79B-9C9A38AA42B1",
    "A1C36260-92CD-47E2-927A-35DAF16D7882",
]

data = {
    "UUID_SEARCH_INFO": {},
    "UUID_SEARCH_INFO_NORMALIZED": {},
    "UUID_SEARCH_INFO_ALL": {},
    "UUID_SEARCH_INFO_ALL_NORMALIZED": {},
}

photosdb = osxphotos.PhotosDB()

for uuid in UUID:
    photo = photosdb.get_photo(uuid)
    search = photo.search_info
    search_norm = photo.search_info_normalized
    data["UUID_SEARCH_INFO"][uuid] = search.asdict()
    data["UUID_SEARCH_INFO_NORMALIZED"][uuid] = search_norm.asdict()
    data["UUID_SEARCH_INFO_ALL"][uuid] = search.all
    data["UUID_SEARCH_INFO_ALL_NORMALIZED"][uuid] = search_norm.all

print(json.dumps(data))


from dataclasses import dataclass, field
from decimal import Decimal

class ItemNotFoundException(Exception):
    pass

@dataclass
class Item:
    name: str
    price: Decimal
    quantity: int

    @property
    def subtotal(self) -> Decimal:
        return self.price * self.quantity

@dataclass
class ShoppingCart:
    items: list[Item] = field(default_factory=list)

    def add_item(self, item: Item) -> None:
        self.items.append(item)

    def remove_item(self, item_name: str) -> None:
        found_item = self.find_item(item_name)
        self.items.remove(found_item)

    def find_item(self, item_name: str) -> Item:
        for item in self.items:
            if item.name == item_name:
                return item
        raise ItemNotFoundException(f"Item '{item_name}' not found.")

    @property
    def subtotal(self) -> Decimal:
        return Decimal(sum(item.subtotal for item in self.items))

@dataclass
class ShoppingCartWithDiscount(ShoppingCart):
    discount_code: str = ""

    def apply_discount(self, discount_code: str) -> None:
        self.discount_code = discount_code

    # Issue: Lack of Extensibility
    # Hardcoded discount codes:
    # The discount codes and their handling are hardcoded within
    # a single method, resulting in a lack of flexibility. Adding
    # or modifying discount codes requires modifying the code
    # directly, making it difficult to manage and maintain.

    @property
    def discount(self) -> Decimal:
        subtotal = self.subtotal
        if self.discount_code == "SAVE10":
            return subtotal * Decimal("0.1")
        elif self.discount_code == "5BUCKSOFF":
            return Decimal("5.00")
        elif self.discount_code == "FREESHIPPING":
            return Decimal("2.00")
        elif self.discount_code == "BLKFRIDAY":
            return subtotal * Decimal("0.2")
        else:
            return Decimal("0")

    @property
    def total(self) -> Decimal:
        return self.subtotal - self.discount

    def display(self) -> None:
        # Print the cart
        print("Shopping Cart:")
        print(f"{'Item':<10}{'Price':>10}{'Qty':>7}{'Total':>13}")
        for item in self.items:
            print(
                f"{item.name:<12}${item.price:>7.2f}{item.quantity:>7}     ${item.subtotal:>7.2f}"
            )
        print("=" * 40)
        print(f"Subtotal: ${self.subtotal:>7.2f}")
        print(f"Discount: ${self.discount:>7.2f}")
        print(f"Total:    ${self.total:>7.2f}")

def main() -> None:
    # Create a shopping cart and add some items to it
    # Issue: Not using the ShoppingCart class.

    cart = ShoppingCartWithDiscount(
        items=[
            Item("Apple", Decimal("1.50"), 10),
            Item("Banana", Decimal("2.00"), 2),
            Item("Pizza", Decimal("11.90"), 5),
        ],
        discount_code="SAVE10",
    )

    cart.display()

if __name__ == "__main__":
    main()


from multiprocessing import Manager
from concurrent.futures import ProcessPoolExecutor

from util import threading
from netio.io_handler import IOHandler
from util.dispatcher import Dispatcher
from util.logging_config import logging_config

if __name__ == '__main__':
    logging_config()

    data_queue  = Manager().Queue()
    anom_queue  = Manager().Queue()
    cmd_queue   = Manager().Queue()
    res_queue   = Manager().Queue()
    num_workers = 18
    dispatcher = Dispatcher(
        k=5,
        data_queue=data_queue,
        anom_queue=anom_queue,
        cmd_queue=cmd_queue,
        res_queue=res_queue,
        num_workers=num_workers,
        debug=False
    )
    io_handler = IOHandler(
        interval=3.0,
        num_workers=num_workers,
        data_queue=data_queue,
        anom_queue=anom_queue,
        cmd_queue=cmd_queue,
        res_queue=res_queue,
        send_reports=True,
    )

    pool = ProcessPoolExecutor(max_workers=1)
    pool.submit(io_handler.run).add_done_callback(threading.thread_done_callback)
    dispatcher.run()


class ProjectMessage:
    NAME_REQ_MSG = "Name field is required!"
    NAME_LEN_MSG = "Name field must between 10 and 80 characters!"
    DESC_REQ_MSG = "Description field is required!"
    DESC_LEN_MSG = "Description field must be between 30 and 300 characters!"
    RD_FORMAT_MSG = "Release Date field is wrong! Must be like '2022-12-31'"
    RD_REQ_MSG = "Release Date field is required!"
    HAS_REPO_REQ_MSG = "Has Repository field is required!"
    HAS_REPO_INVALID_MSG = "Has Repository field is wrong!"
    REPO_URL_REQ_MSG = "Repo URL field is required!"
    REPO_URL_LEN_MSG = "Repo URL field must be between 20 and 240 characters!"
    HAS_DEMO_REQ_MSG = "Has Demo field is required!"
    HAS_DEMO_INVALID_MSG = "Has Demo field is wrong!"
    DEMO_URL_REQ_MSG = "Demo URL field is required!"
    DEMO_URL_LEN_MSG = "Demo URL field must be between 20 and 240 characters!"
    STACKS_REQ_MSG = "Tech Stacks field is required!"
    STACKS_LEN_MSG = "Tech Stacks field must contain at least one item!"


class TransactionHistory:
    def __init__(self, data):
        """
        :param data: parsed json response from DB API
        """
        self.data = data

    def sort_recommendations(self, items, key_extractor=None):
        """ sort items by occurences in financial history """

        def rate_item(item):
            if key_extractor:
                key = key_extractor(item)
            else:
                key = item
            value = sum(
                [key.lower() in record["counterPartyName"].lower()
                 for record in self.data]
            )
            return value

        items = sorted(items, key=rate_item, reverse=True)
        return items


import csv
import os
from glob import glob

import numpy as np

with open("train_ids.csv", "rU") as f:
    reader = csv.reader(f)
    next(reader)
    train_ids = list(reader)

train_ids = set(np.squeeze(train_ids))

filenames = sorted(glob("/media/maciej/Thyroid/thyroid-nodules/Nodules/*.PNG"))

fids = set()

for fname in filenames:
    fid = fname.split("/")[-1].split(".")[0]
    fid = fid.lstrip("0")
    fids.add(fid)

diff = fids.difference(train_ids)

for fid in diff:
    parts = fid.split("_")
    parts[0] = parts[0].zfill(4)
    to_remove = glob(
        "/media/maciej/Thyroid/thyroid-nodules/Nodules/{}*.PNG".format("_".join(parts))
    )
    for fname in to_remove:
        print("Removing {}".format(fname))
        os.remove(fname)


__author__ = 'Shiva'
from time import localtime

activities = {8: 'Sleeping',
              9: 'Commuting',
              17: 'Working',
              18: 'Commuting',
              20: 'Eating',
              24: 'Resting' }

time_now = localtime()
print(time_now.tm_hour,':',time_now.tm_min,':',time_now.tm_sec)
hour = time_now.tm_hour

for activity_time in sorted(activities.keys()):
    if hour < activity_time:
        print(activities[activity_time])
        break
else:
    print('Unknown, AFK or sleeping!')


import requests
import time
import re
from flask import current_app, jsonify

from lablib.app.util.db import db
from lablib.app.models import Book
from xml.dom import NotFoundErr

def self_register(books):
    ngList = []
    try:
        for book_req in books:
            barcode = book_req.get('barcode')

            if len(Book.query.filter_by(barcode=barcode).all()) != 0:
                ngList.append(
                    {"barcode": barcode, "msg": "this book is already registered"})
                continue

            title = book_req.get('title')
            author = book_req.get('author')
            detail = book_req.get('detail')
            publishmonth = book_req.get('publishmonth')
            publisher = book_req.get('publisher')
            amount = stock = 1

            barcode_pat = re.compile("\d+")
            publishmonth_pat = re.compile("\d{4}-\d{2}")

            if len(title) == 0 or len(author) == 0:
                raise ValueError

            if not barcode_pat.fullmatch(barcode):
                raise ValueError

            if publishmonth is not None and not publishmonth_pat.fullmatch(publishmonth):
                raise ValueError

            book = Book(barcode=barcode, title=title, author=author,
                        detail=detail, publishmonth=publishmonth, publisher=publisher,
                        amount=amount, stock=stock)
            db.session.add(book)

        db.session.commit()
    except ValueError:
        ngList.append({"status": "ng", "msg": "The format is invalid. Please check barcode and publishmonth."})
    except Exception as e:
        ngList.append({"status": "ng", "msg": "Some error occured. Please check your book data. barcode, title, author are required."})

    if len(ngList) == 0:
        return jsonify({"status": "ok"})
    else:
        return jsonify({"status": "partial ng", "ngList": ngList})

# search for external apis
# default api is OpenBD
RAKUTEN_ICHIBA = "https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706"
RAKUTEN_BOOKS = "https://app.rakuten.co.jp/services/api/BooksTotal/Search/20170404"
OPENBD = "https://api.openbd.jp/v1/get"
MAGAZINE_CODELIST = {"05827": "Software Design",
                     "01619": "Interface"}
AUTHOR_LIST = {"05827": "技術評論社",
               "01619": "ＣＱ出版"}
TRANSLATE_LIST = {
    '\u3000': ' ',
}

def search_external_api(books):
    notFoundList = []
    for books_req in books:

        barcode = books_req.get("barcode")
        if barcode is None:
            notFoundList.append({"msg": "Please pass barcodes"})
            continue

        try:
            wait_time = current_app.config.get("LAST_API_CALL") + 1 - time.time()
            if wait_time > 0:
                print("wait! {}".format(wait_time))
                time.sleep(wait_time)

            if len(Book.query.filter_by(barcode=barcode).all()) != 0:
                notFoundList.append(
                    {"barcode": barcode, "msg": "this book is already registered"})
                continue

            newbook = Book(barcode=barcode)

            # 雑誌の場合
            # 楽天市場APIを検索
            # 雑誌コードを事前に登録しておき、年月だけ抽出する
            if barcode[:3] == '491':

                data = {"applicationId": current_app.config['RAKUTEN_APPLICATION_ID'],
                        "format": "json",
                        "keyword": str(barcode)}
                res = requests.get(RAKUTEN_ICHIBA, data)
                current_app.config["LAST_API_CALL"] = time.time()
                if res.status_code != 200:
                    raise ConnectionRefusedError

                if res.json().get('hits') == 0:
                    raise NotFoundErr

                year_month = ""
                publish_month = ""
                for item in res.json().get("Items"):
                    tmp = item.get("Item").get("itemName")
                    reg = re.search("(\d+).*年.*(\d+).*月", tmp)
                    if reg is not None:
                        year_month = "{}年{:0>2}月号".format(
                            reg.groups()[0], reg.groups()[1])
                        publish_month = "-".join([reg.groups()[0], "{:0>2}".format(reg.groups()[1])])
                        break

                magazine_code = barcode[4:9]
                if magazine_code in MAGAZINE_CODELIST:
                    newbook.title = " ".join(
                        [MAGAZINE_CODELIST.get(magazine_code), year_month])
                    newbook.publishmonth = publish_month
                    newbook.author = newbook.publisher = AUTHOR_LIST.get(
                        magazine_code)
                    newbook.amount = newbook.stock = 1

                else:
                    notFoundList.append(
                        {"barcode": barcode, "msg": "Not registered in the magazine list.Please tell the administrator."})
                    continue

            # JAN以外の場合（ほぼISBN）
            else:
                data = {"isbn": barcode, "pretty": True}
                res = requests.get(OPENBD, data)

                if res.status_code != 200:
                    raise ConnectionRefusedError

                if res.json()[0] is None:
                    raise NotFoundErr
                result = res.json()[0]

                newbook.title = result.get("summary").get("title")
                newbook.author = result.get("summary").get("author")
                newbook.detail = result.get("onix").get("CollateralDetail", {}).get("TextContent", {})[0].get("Text", None)

                pubdate = result.get("summary").get("pubdate")
                if '-' in pubdate:
                    reg = re.search("(\d+)-(\d+)", pubdate).groups()
                    newbook.publishmonth = "{}-{:0>2}".format(reg[0], reg[1])
                else:
                    newbook.publishmonth = "-".join(
                        [pubdate[:4], pubdate[4:6]])

                newbook.publisher = result.get("summary").get("publisher")
                newbook.amount = newbook.stock = 1
                newbook.large_url = result.get("summary").get("cover")

            db.session.add(newbook)
            db.session.commit()

        except NotFoundErr:
            notFoundList.append(
                {"barcode": barcode, "msg": "barcode not found."})

        except ConnectionRefusedError:
            return jsonify({"status": "ng", "msg": "Connection to API was Refused. Please check token."})

        except KeyError:
            notFoundList.append(
                {"barcode": barcode, "msg": "Required information was not available. Please register manually."})

        except Exception as e:
            print(e)
            notFoundList.append(
                {"barcode": barcode, "msg": "some error occured."})

        finally:
            db.session.rollback()
            db.session.close()

    if len(notFoundList) == 0:
        return jsonify({"status": "ok"})
    else:
        return jsonify({"status": "partial ng", "ngList": notFoundList})


# https://www.acmicpc.net/problem/1182

# 자연수 n, m

# 1~n까지 자연수 중에서
# 중복 없이 m개를 고른 수열

from itertools import permutations

n, m = map(int, input().split())

num_list = list(range(1, n+1))

ans = list(permutations(num_list, m))

for i in ans:
    print(*i)


__author__ = 'atulgupta'

from rest_framework import serializers

from bus_attendance.models import *

from .models import Student, Parent, AdditionalDetails, House, DOB

class StudentSerializer(serializers.ModelSerializer):
    # we use SlugRelatedField because current_class and current_section are foreign keys in Student Model. Not using
    # slug related field would return the primary key instead of their respective names
    current_class = serializers.SlugRelatedField(read_only=True, slug_field='standard')
    current_section = serializers.SlugRelatedField(read_only=True, slug_field='section')
    parent = serializers.SlugRelatedField(read_only=True, slug_field='parent_name')

    class Meta:
        model = Student
        fields = ('id', 'student_erp_id', 'fist_name', 'last_name',
                  'roll_number', 'current_class', 'current_section', 'parent')

class ParentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Parent
        fields = ('parent_name', 'parent_mobile1', 'parent_mobile2',)

class StudentDetailSerializer(serializers.ModelSerializer):
    # we use SlugRelatedField because current_class and current_section are foreign keys in Student Model. Not using
    # slug related field would return the primary key instead of their respective names
    current_class = serializers.SlugRelatedField(read_only=True, slug_field='standard')
    current_section = serializers.SlugRelatedField(read_only=True, slug_field='section')
    parent = serializers.SlugRelatedField(read_only=True, slug_field='parent_name')
    parent_mob = serializers.SerializerMethodField()
    father_occ = serializers.SerializerMethodField()
    mother_occ = serializers.SerializerMethodField()
    mother = serializers.SerializerMethodField()
    mother_mob = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()
    dob = serializers.SerializerMethodField()
    gender = serializers.SerializerMethodField()
    blood_group = serializers.SerializerMethodField()
    adhar = serializers.SerializerMethodField()
    address = serializers.SerializerMethodField()
    bus_user = serializers.SerializerMethodField()
    bus_rout = serializers.SerializerMethodField()
    bus_stop = serializers.SerializerMethodField()
    house = serializers.SerializerMethodField()

    def get_bus_user(self, obj):
        try:
            bus_user = BusUser.objects.get(student=obj)
            print ('%s is a bus user' % obj)
            return 'bus_user'
        except Exception as e:
            print('exception 12102019-A from student serializers.py %s %s' % (e.message, type(e)))
            print('%s is not a bus user' % obj)
            return 'walker'

    def get_bus_rout(self, obj):
        try:
            rout = Student_Rout.objects.get(student=obj)
            print ('%s is a bus user' % obj)
            return rout.bus_root.bus_root
        except Exception as e:
            print('exception 12102019-B from student serializers.py %s %s' % (e.message, type(e)))
            print('%s is not a bus user. Hence rout is N/A' % obj)
            return 'N/A'

    def get_bus_stop(self, obj):
        try:
            rout = Student_Rout.objects.get(student=obj)
            print ('%s is a bus user' % obj)
            return rout.bus_stop.stop_name
        except Exception as e:
            print('exception 12102019-C from student serializers.py %s %s' % (e.message, type(e)))
            print('%s is not a bus user. Hence bus stop is N/A' % obj)
            return 'N/A'

    def get_house(self, obj):
        try:
            house = House.objects.get(student=obj)
            return house.house
        except Exception as e:
            print('exception 12102019-D from student serializers.py %s %s' % (e.message, type(e)))
            print('no house assigned for %s' % obj)
            return 'Not Assigned'

    def get_mother(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.mother_name
        except Exception as e:
            print('exception 13102019-A from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (mother) not entered for %s' % obj)
            return 'Not Available'

    def get_mother_occ(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.mother_occupation
        except Exception as e:
            print('exception 13102019-H from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (mother occupation) not entered for %s' % obj)
            return 'Not Available'

    def get_father_occ(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.father_occupation
        except Exception as e:
            print('exception 13102019-I from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (father occupation) not entered for %s' % obj)
            return 'Not Available'

    def get_parent_mob(self, obj):
        return obj.parent.parent_mobile1

    def get_mother_mob(self, obj):
        return obj.parent.parent_mobile2

    def get_email(self, obj):
        return obj.parent.parent_email

    def get_dob(self, obj):
        try:
            dob = DOB.objects.get(student=obj)
            return dob.dob
        except Exception as e:
            print('exception 13102019-B from student serializers.py %s %s' % (e.message, type(e)))
            print('DOB not entered for %s' % obj)
            return 'Not Available'

    def get_gender(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.gender
        except Exception as e:
            print('exception 13102019-C from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (gender) not entered for %s' % obj)
            return 'Not Available'

    def get_blood_group(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.blood_group
        except Exception as e:
            print('exception 13102019-D from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (blood group) not entered for %s' % obj)
            return 'Not Available'

    def get_adhar(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.adhar
        except Exception as e:
            print('exception 13102019-E from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (adhar) not entered for %s' % obj)
            return 'Not Available'

    def get_address(self, obj):
        try:
            ad = AdditionalDetails.objects.get(student=obj)
            return ad.address
        except Exception as e:
            print('exception 13102019-F from student serializers.py %s %s' % (e.message, type(e)))
            print('addtional details (address) not entered for %s' % obj)
            return 'Not Available'

    class Meta:
        model = Student
        fields = ('id', 'student_erp_id', 'fist_name', 'last_name', 'roll_number', 'current_class',
                  'current_section', 'parent', 'parent_mob', 'mother', 'mother_mob', 'email', 'dob', 'gender',
                  'blood_group', 'adhar', 'address', 'bus_user', 'bus_rout', 'bus_stop', 'house',
                  'father_occ', 'mother_occ')


from sqlalchemy import (
    Index,
    Column,
    Integer,
    String,
    Text,
    ForeignKey,
    func,
    select,
)

from pycds.alembic.extensions.replaceable_objects import ReplaceableNativeMatview

# These cannot be imported from pycds because it is not yet fully initialized.
from pycds.orm.tables import Variable
from pycds.orm.native_matviews import VarsPerHistory
from pycds.orm.view_base import Base

class CollapsedVariables(Base, ReplaceableNativeMatview):
    """
    This class defines a materialized view that supports the view CrmpNetworkGeoserver,
    to improve that view's performance.
    """

    __tablename__ = "collapsed_vars_mv"

    history_id = Column(
        Integer, ForeignKey("meta_history.history_id"), primary_key=True
    )
    vars = Column(String)
    display_names = Column(String)

    __selectable__ = (
        # This query is likely wrong.
        # See https://github.com/pacificclimate/pycds/issues/180
        select(
            VarsPerHistory.history_id.label("history_id"),
            func.array_to_string(
                func.array_agg(
                    Variable.standard_name
                    + func.regexp_replace(Variable.cell_method, "time: ", "_", "g")
                ),
                ", ",
            ).label("vars"),
            func.array_to_string(func.array_agg(Variable.display_name), "|").label(
                "display_names"
            ),
        )
        .select_from(VarsPerHistory)
        .join(Variable, Variable.id == VarsPerHistory.vars_id)
        .group_by(VarsPerHistory.history_id)
    )

Index("collapsed_vars_idx", CollapsedVariables.history_id)


import pytest
from httpx import AsyncClient
from app.main import app, get_database, ApiEmployee
from database.scheme import SchemeConfig
from datetime import datetime

# strange test, but for example

class FakeDB:
    def __getitem__(self, item):
        assert item == SchemeConfig.collection_employee
        return FakeCollection()

class FakeCollection:
    def find(self, query):
        return FakeCursor(query)

class FakeCursor:
    def __init__(self, query):
        self.query = query

    async def to_list(self, unnecessary):
        return [ApiEmployee(name="fake", company="fake", email="fake@email.fake",
                            age=99, join_date=datetime(2020, 1, 1), job_title="fake",
                            gender="other", salary=1234
                            ) for i in range(len(self.query))]

def fake_get_database():
    return FakeDB()

app.dependency_overrides[get_database] = fake_get_database

@pytest.fixture(scope="function")
async def client():
    async with AsyncClient(app=app, base_url="http://testserver") as client:
        yield client

@pytest.mark.parametrize(
    "url,query_len",
    [("/employee", 0),
     ("/employee?name=John Titor", 1),
     ("/employee?company=Abra&company=Cadabra", 1),
     ("/employee?company=Apple&job=director&name=Steve Jobs", 3)]
)
@pytest.mark.asyncio
async def test_app(client, url, query_len):
    result = await client.get(url)
    assert len(result.json()) == query_len


from datetime import datetime
import numpy as np
import pandas as pd
#import DATAFRAME_TO_REDSHIFT_TABLE_V2 as pd2rs
import sys
import os
#import boto3
from Class_GeneralUtilitiesNonTF import GeneralUtilitiesNonTF
import matplotlib.pyplot as plt
from google.cloud import storage

def create_output(key,Segment,n_var,n_iter,Description,test_month,atts,ui_identifier,train_churn,test_churn,bq_table,vol_view):
    print('*'*10+"\nStarting generation of model output statistics", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    ##Create a connection to the GCP
    bucket = 'alticeusa-am'
    project_id = 'alticeusa-am'
    dataset = 'poc'
    auth_file = 'alticeusa-am-b639e404289b.json'

    gcs_client = storage.Client(project= project_id)

    ##File locations
    lift_file_location = 'model_results/lift_analysis/'
    image_path = 'model_results/churn_model_images/'
    output_log_path = 'model_results/churn_output_logs/'
    atts_location = 'model_results/churn_model_attributes/'

    #Instantiate the util obj that will be used to interact with BQ
    try:
        util_obj = GeneralUtilitiesNonTF(project_id = project_id,dataset = dataset,bucket_name = bucket,json_path = auth_file)
        print('*'*10+"\nInstantiated the utility object to interact with the Google Cloud Platform",flush = True)
        print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    except Exception as e:
        print('*'*10+"\nFailed to instantiate the utility object due to exception: " + str(e))

    output_dict = {}
    output_dict['Unique_Identifier'] = ui_identifier
    output_dict['Output_File_Name'] = key
    output_dict['Segment'] = Segment
    output_dict['No_of_variables'] = n_var
    output_dict['No_of_iterations'] = n_iter
    output_dict['Description'] = Description
    output_dict['Train_data_actives'] = train_churn.loc[0,'chc_id'].item()
    output_dict['Train_data_churners'] = train_churn.loc[1,'chc_id'].item()
    output_dict['Test_data_actives'] = test_churn.loc[0,'chc_id'].item()
    output_dict['Test_data_churners'] = test_churn.loc[1,'chc_id'].item()

    ##Calculate the actual test churn rate
    avg_churn_rate_actual_test = float(test_churn.loc[1,'chc_id'].item())/(float(test_churn.loc[1,'chc_id'].item()) + float(test_churn.loc[0,'chc_id'].item()))
    avg_churn_rate_actual_train = float(train_churn.loc[1,'chc_id'].item())/(float(train_churn.loc[1,'chc_id'].item()) + float(train_churn.loc[0,'chc_id'].item()))

    ts_string = str(datetime.strftime(datetime.now(), '%Y%m%d_%H%M%S'))

    a = int(test_month)
    z = []
    for i in range(6):
        if(i < 1):
            if(a % 100 == 12):
                z.append(a + 89)
            else:
                z.append(a+1)

        else:
            if(z[i-1] % 100 == 12):
                z.append(z[i-1] + 89)
            else:
                z.append(z[i-1] + 1)

    list_6m =  list(map(str,z))
    list_3m = list(map(str,z[0:3]))
    str_3m = "('" + "','".join(list_3m) + "')"
    str_6m = "('" + "','".join(list_6m) + "')"

    print('*'*10 + "\nCalculating the conversion metrics for the following 3 months "+str_3m+" and 6 months: " + str_6m)

    print('*'*10 + "\nStarting the Conversion metrics calculation.", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    ##Monthly
    monthly_conversion ="""select count(*) hh_count,
    CASE WHEN prediction_probability >= 0.5 and prediction_probability < 0.6 THEN 'GTE_0.5 and LT_0.6 Count'
    WHEN prediction_probability >= 0.6 and prediction_probability < 0.7 THEN 'GTE_0.6 and LT_0.7 Count'
    WHEN prediction_probability >= 0.7 and prediction_probability < 0.8 THEN 'GTE_0.7 and LT_0.8 Count'
    WHEN prediction_probability >= 0.8 and prediction_probability < 0.9 THEN 'GTE_0.8 and LT_0.9 Count'
    WHEN prediction_probability >= 0.9 THEN 'GTE_0.9 Count'
    END prediction_probability,
    'Counts' pending_disco_month,
    1 rank_order
    from {bq_table} a
    where predicted_label in (1)
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    CASE WHEN prediction_probability >= 0.5 and prediction_probability < 0.6 THEN 'GTE_0.5 and LT_0.6 Pending Disconnect'
    WHEN prediction_probability >= 0.6 and prediction_probability < 0.7 THEN 'GTE_0.6 and LT_0.7 Pending Disconnect'
    WHEN prediction_probability >= 0.7 and prediction_probability < 0.8 THEN 'GTE_0.7 and LT_0.8 Pending Disconnect'
    WHEN prediction_probability >= 0.8 and prediction_probability < 0.9 THEN 'GTE_0.8 and LT_0.9 Pending Disconnect'
    WHEN prediction_probability >= 0.9 THEN 'GTE_0.9 Pending Disconnect'
    END prediction_probability
    ,dt as pending_disco_month,
    2 rank_order
    from {bq_table} a
    JOIN (select distinct CAST(CONCAT(CAST(corp AS STRING), LPAD(house,6,'0'),LPAD(cust,2,'0')) as STRING) corp_house_cust,
    min(dt) dt
    from {view}
    where (pending_drform = 'VOLUNTARY' or pending_mover = 'FALSE')
    and dt in {str_6m}
    group by 1)b ON a.corp_house_cust = b.corp_house_cust
    where predicted_label in (1)
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    CASE WHEN prediction_probability >= 0.5 and prediction_probability < 0.6 THEN 'GTE_0.5 and LT_0.6 Voluntary Disconnect'
    WHEN prediction_probability >= 0.6 and prediction_probability < 0.7 THEN 'GTE_0.6 and LT_0.7 Voluntary Disconnect'
    WHEN prediction_probability >= 0.7 and prediction_probability < 0.8 THEN 'GTE_0.7 and LT_0.8 Voluntary Disconnect'
    WHEN prediction_probability >= 0.8 and prediction_probability < 0.9 THEN 'GTE_0.8 and LT_0.9 Voluntary Disconnect'
    WHEN prediction_probability >= 0.9 THEN 'GTE_0.9 Voluntary Disconnect'
    END prediction_probability
    ,dt as pending_disco_month,
    3 rank_order
    from {bq_table} a
    JOIN (select distinct CAST(CONCAT(CAST(corp AS STRING), LPAD(house,6,'0'),LPAD(cust,2,'0')) as STRING) corp_house_cust, min(dt) dt
    from {view}
    where drform = 'VOLUNTARY'
    and dt in {str_6m}
    group by 1)b ON a.corp_house_cust = b.corp_house_cust where predicted_label in (1)
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    'GTE_0.7 Count' prediction_probability,
    'Counts' pending_disco_month,
    1 rank_order
    from {bq_table} a
    where predicted_label in (1)
    and prediction_probability >=0.7
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    'GTE_0.8 Count' prediction_probability,
    'Counts' pending_disco_month,
    1 rank_order
    from {bq_table} a
    where predicted_label in (1)
    and prediction_probability >=0.8
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    'GTE_0.7 Pending Disconnect' prediction_probability
    ,dt as pending_disco_month,
    4 rank_order
    from {bq_table} a
    JOIN (select distinct CAST(CONCAT(CAST(corp AS STRING), LPAD(house,6,'0'),LPAD(cust,2,'0')) as STRING) corp_house_cust, min(dt) dt
    from {view}
    where (pending_drform = 'VOLUNTARY' or pending_mover = 'FALSE')

    and dt in {str_6m}
    group by 1)b
    ON a.corp_house_cust = b.corp_house_cust
    where predicted_label in (1)
    and prediction_probability >=0.7
    group by 2,3

    UNION DISTINCT

    select count(*) hh_count,
    'GTE_0.8 Pending Disconnect' prediction_probability
    ,dt as pending_disco_month,
    4 rank_order
    from {bq_table} a
    JOIN (select distinct CAST(CONCAT(CAST(corp AS STRING), LPAD(house,6,'0'),LPAD(cust,2,'0')) as STRING) corp_house_cust, min(dt) dt
    from {view}
    where (pending_drform = 'VOLUNTARY' or pending_mover = 'FALSE')

    and dt in {str_6m}
    group by 1)b
    ON a.corp_house_cust = b.corp_house_cust
    where predicted_label in (1)
    and prediction_probability >=0.8
    group by 2,3

    order by 4,3 asc,2;
    """.format(view = vol_view, str_6m = str_6m, bq_table = bq_table)
    print(monthly_conversion)

    ##Monthly conversion df creation. Creating an empty df to get all possible rows and filling the ones missing with 0.

    conversion_monthly_def = pd.DataFrame(columns = ['prediction_probability'])

    monthly_df_rows = []

    monthly_df_rows.extend(["""GTE_0.5 and LT_0.6 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.6 and LT_0.7 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.7 and LT_0.8 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.7 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.8 and LT_0.9 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.8 Count(Counts)"""])
    monthly_df_rows.extend(["""GTE_0.9 Count(Counts)"""])

    monthly_df_rows.extend([ """GTE_0.5 and LT_0.6 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.6 and LT_0.7 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.7 and LT_0.8 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.7 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.8 and LT_0.9 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.8 Pending Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.9 Pending Disconnect("""+ x + """)"""  for x in list_6m])

    monthly_df_rows.extend([ """GTE_0.5 and LT_0.6 Voluntary Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.6 and LT_0.7 Voluntary Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.7 and LT_0.8 Voluntary Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.8 and LT_0.9 Voluntary Disconnect("""+ x + """)"""  for x in list_6m])
    monthly_df_rows.extend([ """GTE_0.9 Voluntary Disconnect("""+ x + """)"""  for x in list_6m])

    ui_list_6m = ['month_'+str(i) for i in range(1,7)]

    ui_monthly_rows = []

    ui_monthly_rows.extend(["""GTE_0.5 and LT_0.6 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.6 and LT_0.7 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.7 and LT_0.8 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.7 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.8 and LT_0.9 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.8 Count(Counts)"""])
    ui_monthly_rows.extend(["""GTE_0.9 Count(Counts)"""])

    ui_monthly_rows.extend([ """GTE_0.5 and LT_0.6 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.6 and LT_0.7 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.7 and LT_0.8 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.7 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.8 and LT_0.9 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.8 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.9 Pending Disconnect("""+ x + """)"""  for x in ui_list_6m])

    ui_monthly_rows.extend([ """GTE_0.5 and LT_0.6 Voluntary Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.6 and LT_0.7 Voluntary Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.7 and LT_0.8 Voluntary Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.8 and LT_0.9 Voluntary Disconnect("""+ x + """)"""  for x in ui_list_6m])
    ui_monthly_rows.extend([ """GTE_0.9 Voluntary Disconnect("""+ x + """)"""  for x in ui_list_6m])

    conversion_monthly_def = conversion_monthly_def.append(pd.DataFrame({'prediction_probability':monthly_df_rows}))
    conversion_monthly_def.set_index('prediction_probability',inplace=True)

    ##Clean up the names for bigquery. Remove all special characters and replace with _
    monthly_df_rows = [str(row).translate ({ord(c): "_" for c in "!@#$%^&*()[]{};:,./<>?\|`~-=+ "}) for row in monthly_df_rows]

    ui_monthly_rows = [str(row).translate ({ord(c): "_" for c in "!@#$%^&*()[]{};:,./<>?\|`~-=+ "}) for row in ui_monthly_rows]

    ##Create a dict object to rename the monthly columns
    ui_monthly_dict = dict(zip(monthly_df_rows,ui_monthly_rows))

    ##Get the output df which is the result of the above query
    try:
        output_df = util_obj.read_gbq_lite(monthly_conversion)
        if output_df is None:
            print('*'*10 + "\nFailed to fetch the conversion metrics from the bq. Please check the query",flush = True)
            sys.exit(1)
        else:
            print('*'*10 + "\nSuccessfully fetched the conversion data.", flush = True)
            print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    except Exception as e:
        print('*'*10 + "\nException occurred while fetching the conversion data: "+ str(e))

    ##Get 3m and 6m conversion metrics
    output_3m_df = output_df.loc[output_df['pending_disco_month'].isin(list_3m)].groupby('prediction_probability').agg({'hh_count':sum}).reset_index()
    output_3m_df['prediction_probability'] = output_3m_df['prediction_probability'] + ' (3m CR)'

    output_6m_df = output_df.loc[output_df['pending_disco_month'].isin(list_6m)].groupby('prediction_probability').agg({'hh_count':sum}).reset_index()
    output_6m_df['prediction_probability'] = output_6m_df['prediction_probability'] + ' (6m CR)'

    ##Get the monthly conversion df by joining the empty df and the result of output df
    output_df['prediction_probability'] = output_df['prediction_probability'] + '(' + output_df['pending_disco_month'] + ')'
    #output_df.set_index('prediction_probability',inplace = True)
    del output_df['pending_disco_month']

    conversion_monthly_df = conversion_monthly_def.join(output_df.set_index('prediction_probability'),rsuffix = '_res',how = 'left',sort = False)
    conversion_monthly_df['hh_count'].fillna(0,inplace = True)
    conversion_monthly_df.reset_index(inplace = True)

    ##Concat all 3 to get the final df
    final_output_df = pd.concat([conversion_monthly_df[['prediction_probability','hh_count']],output_3m_df,output_6m_df]).reset_index(drop = True)

    final_output_df['prediction_probability'] = final_output_df['prediction_probability'].apply(lambda x: str(x).translate ({ord(c): "_" for c in "!@#$%^&*()[]{};:,./<>?\|`~-=+ "}))

    ##Create a dictionary object of the conversion dictionary
    conversion_dict = dict(zip(final_output_df.prediction_probability, final_output_df.hh_count))

    ##Add the conversion dict to the output dict
    output_dict.update(conversion_dict)

    print('*'*10 + "\nCompleted the conversion metrics calculation",flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    print('*'*10 + "\nStarting model performance metric calculation",flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    ##Read in the bq_table for calculation of metrics and for lift metrics
    pred_df_sql = """
    SELECT * from {}
    """.format(bq_table)
    y_test_prediction_df = util_obj.read_gbq_lite(pred_df_sql)

    ##Correct the prediction_probability for AUC and lift
    y_test_prediction_df0 = y_test_prediction_df.loc[y_test_prediction_df['predicted_label']==0,:]
    y_test_prediction_df1 = y_test_prediction_df.loc[y_test_prediction_df['predicted_label']==1,:]

    y_test_prediction_df0.loc[:,'prediction_probability'] = 1- y_test_prediction_df0['prediction_probability']

    y_test_prediction_df = pd.concat([y_test_prediction_df0,y_test_prediction_df1])

    ##Metric calculations
    tp = len(y_test_prediction_df[(y_test_prediction_df['original_labels'] == 1) & \
                              (y_test_prediction_df['predicted_label'] == 1)])

    tn = len(y_test_prediction_df[(y_test_prediction_df['original_labels'] == 0) & \
                              (y_test_prediction_df['predicted_label'] == 0)])

    fp = len(y_test_prediction_df[(y_test_prediction_df['original_labels'] == 0) & \
                              (y_test_prediction_df['predicted_label'] == 1)])

    fn = len(y_test_prediction_df[(y_test_prediction_df['original_labels'] == 1) & \
                              (y_test_prediction_df['predicted_label'] == 0)])
    print ('*'*10 + "\nTest statistics.",flush=True)
    try:
        tpr = tp/(tp+fn)
    except ZeroDivisionError as err:
        print('tpr:', err)
        tpr = 0.0

    try:
        tnr = tn/(tn+fp)
    except ZeroDivisionError as err:
        print('tnr:', err)
        tnr = 0.0

    try:
        fpr = fp/(tn+fp)
    except ZeroDivisionError as err:
       print('fpr:', err,flush = True)
       fpr = 0.0

    # accuracy = (tpr+tnr)/2

    accuracy = (tp + tn)/(tp+tn+fp+fn)

    balanced_accuracy = (tpr+tnr)/2

    try:
        precision_metric = tp/(tp+fp)
    except ZeroDivisionError as err:
        print('precision_metric:', err)
        precision_metric = 0.0

    try:
        recall_metric = tp/(tp+fn)
    except ZeroDivisionError as err:
        print('recall_metric:', err)
        recall_metric = 0.0

    #MCC:
    from sklearn.metrics import matthews_corrcoef
    mcc = matthews_corrcoef(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'])
    mcc_weighted = matthews_corrcoef(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'])#, sample_weight = weights)

    #F1 Score:
    from sklearn.metrics import f1_score
    regular_f1_score = f1_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], average=None) #average='macro' averages both without weighing claas imbalance
    weighted_avg_f1_score = f1_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], average='weighted')

    #Precision
    from sklearn.metrics import precision_score
    precision_ACTIVE = precision_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], pos_label = 0, average = 'binary')
    precision_NON_PAY = precision_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], pos_label = 1, average = 'binary')
    weighted_avg_precision = precision_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], average='weighted')

    #Recall
    from sklearn.metrics import recall_score
    recall_ACTIVE = recall_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], pos_label = 0, average = 'binary')
    recall_NON_PAY = recall_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], pos_label = 1, average = 'binary')
    weighted_avg_recall = recall_score(y_test_prediction_df['original_labels'], y_test_prediction_df['predicted_label'], average='weighted')

    #AUC
    from sklearn.metrics import roc_auc_score
    auc = roc_auc_score(y_test_prediction_df['original_labels'],y_test_prediction_df['prediction_probability'])

    f_active = 2*(precision_ACTIVE*recall_ACTIVE)/(recall_ACTIVE + precision_ACTIVE)
    f_churn = 2*(precision_NON_PAY*recall_NON_PAY)/(recall_NON_PAY + precision_NON_PAY)

    metrics_names = [
    'TPR',
    'TNR',
    'FPR',
    'Accuracy',
    'Balanced_Accuracy',
    'AUC',
    'Precision_ACTIVE',
    'Precision_CHURN',
    'Precision_Weighted_Avg',
    'Recall_ACTIVE',
    'Recall_CHURN',
    'Recall_Weighted_Avg',
    'F1_ACTIVE',
    'F1_CHURN',
    'F1_Weighted_Avg',
    'MCC']

    metrics_list = [float(tpr),
    float(tnr),
    float(fpr),
    float(accuracy),
    float(balanced_accuracy),
    float(auc),
    float(precision_ACTIVE),
    float(precision_NON_PAY),
    float(weighted_avg_precision),
    float(recall_ACTIVE),
    float(recall_NON_PAY),
    float(weighted_avg_recall),
    float(f_active),
    float(f_churn),
    float(weighted_avg_f1_score),
    float(mcc)]

    metrics_dict = dict(zip(metrics_names,metrics_list))

    print('*'*10 + "\nCalculation of the model performance metrics complete.", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    output_dict.update(metrics_dict)
    ##Get the output df from the output dict
    ##Convert the metrics types as well as some beautification to reduce the precision of the metrics
    output_df_temp = pd.DataFrame(list(output_dict.items()))
    output_df_temp.columns = ['Metric','Value']
    output_df = output_df_temp.set_index('Metric').T
    output_df[list(output_df.columns)] = output_df[list(output_df.columns)].apply(pd.to_numeric,errors='ignore',downcast='integer')
    metrics_cols = list(metrics_dict.keys())
    output_df[metrics_cols] = output_df[metrics_cols].apply(lambda x:round(x,4))

    final_output_cols = [
            'Unique_Identifier',
            'Output_File_Name',
            'Segment',
            'Train_data_actives',
            'Train_data_churners',
            'Test_data_actives',
            'Test_data_churners',
            'No_of_variables',
            'No_of_iterations',
            'Description',
            'TPR',
            'TNR',
            'FPR',
            'Accuracy',
            'Balanced_Accuracy',
            'AUC',
            'Precision_ACTIVE',
            'Precision_CHURN',
            'Precision_Weighted_Avg',
            'Recall_ACTIVE',
            'Recall_CHURN',
            'Recall_Weighted_Avg',
            'F1_ACTIVE',
            'F1_CHURN',
            'F1_Weighted_Avg',
            'MCC']

    final_output_cols.extend(monthly_df_rows)
    output_df = output_df[final_output_cols]

    ##Rename the output columns with month 1 - 6 instead of the actual months
    output_df = output_df.rename(index = str, columns = ui_monthly_dict)

    #print(output_df.dtypes)
    print('#'*10+'\nOutput DF\n'+'#'*10)
    print(output_df.T,flush=True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    output_df.to_csv("Output_current_run.csv", index = False)
    #write the atts to a new table and csv
    atts_df = pd.DataFrame({'unique_identifier':ui_identifier,'Attributes':atts})

    print('*'*10 + "\nUploading the model performance images (AUC and PRC) to GCS bucket.Please check for images in "+ image_path,flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    #####################################################################
    ##Image upload
    #####################################################################
    bucket = gcs_client.get_bucket('alticeusa-am')

    AUC_file_name = image_path + ui_identifier + '_AUC.png'
    PRC_file_name = image_path + ui_identifier + '_PRC.png'

    blob = bucket.blob(AUC_file_name)
    blob.upload_from_filename('AUC.png')

    blob = bucket.blob(PRC_file_name)
    blob.upload_from_filename('PRC.png')
    print('*'*10 + "\nLift analysis starts now.", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    ######################################################################
    ##Lift analysis
    ######################################################################
    # df_data = y_test_prediction_df
    # df_data.index = df_data['corp_house_cust']
    # del df_data['corp_house_cust']
    # df_data = df_data.sort_values('prediction_probability',ascending = False)
    # ##Cut the prediction probability by decile
    # df_data['decile'] = pd.qcut(df_data['prediction_probability'], 10, labels=False,duplicates='drop') + 1
    # lift_table=df_data.reset_index().groupby('decile', as_index=False).agg({'prediction_probability':[np.min,np.max],'original_labels':np.mean,'corp_house_cust':'count'})
    # lift_table.columns = ['decile','min_prob','max_prob','conv_3m','count_subs']

    # lift_table['avg_churn_rate_actual']=np.mean(lift_table.conv_3m)
    # lift_table['lift']=lift_table.conv_3m/lift_table.avg_churn_rate_actual

    # lift_table.loc[:,'Unique_Identifier'] = ui_identifier

    # lift_cols = list(lift_table.columns)
    # ##Move the unique_identifier to the first location
    # lift_cols.insert(0, lift_cols.pop())

    # lift_table = lift_table[lift_cols]

    # ##Write the lift table to file
    # lift_table.to_csv('Lift_Table_'+ts_string+'.csv',index=False)

    # print('*'*10 + "\nUploading the lift images to GCS bucket at location: "+lift_file_location, flush = True)
    # print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    # ##Plot the lift chart
    # lt = lift_table.plot(x='decile',y='lift', kind='bar')
    # fig = lt.get_figure()
    # fig.savefig('lift_all_deciles.png')
    # ##TBD: Add labels to the bars

    # ##Save the lift image to bucket
    # ALL_LIFT_file_name = image_path + ui_identifier + '_Lift_Overall.png'
    # blob = bucket.blob(ALL_LIFT_file_name)
    # blob.upload_from_filename('lift_all_deciles.png')

    ##Get top 10000 for deciling
    ##TBD:parameterize the 10000

    top_n = 10000

    y_test_prediction_df1 = y_test_prediction_df1.sort_values('prediction_probability',ascending = False).reset_index(drop = True)

    ##Pick top 10000 or max of y_pred = 1 to get top decile df
    if y_test_prediction_df1.shape[0] < top_n:
        top_n = y_test_prediction_df1.shape[0]

    top_decile_df = y_test_prediction_df1.loc[0:top_n-1,:]

    top_decile_df.index = top_decile_df['corp_house_cust']
    del top_decile_df['corp_house_cust']
    #del top_decile_df['decile']

    #top_decile_df = top_decile_df.sort_values('prediction_probability',ascending = False)
    top_decile_df['decile'] = pd.qcut(top_decile_df['prediction_probability'], 10, labels=False,duplicates='drop') + 1
    lift_table_top_dec=top_decile_df.reset_index().groupby('decile', as_index=False).agg({'prediction_probability':[np.min,np.max],'original_labels':np.mean,'corp_house_cust':'count'})
    lift_table_top_dec.columns = ['decile','min_prob','max_prob','conv_3m','count_subs']
    lift_table_top_dec.loc[:,'avg_churn_rate_actual'] = avg_churn_rate_actual_test

    lift_table_top_dec['lift']=lift_table_top_dec.conv_3m/lift_table_top_dec.avg_churn_rate_actual
    lift_table_top_dec.loc[:,'Unique_Identifier'] = ui_identifier

    ##Send the ui_identifier to the start of the df
    lift_cols = list(lift_table_top_dec.columns)
    lift_cols.insert(0, lift_cols.pop())

    lift_table_top_dec = lift_table_top_dec[lift_cols]

    lift_table_top_dec.to_csv('Lift_top_decile_'+ts_string+'.csv',index=False)

    lt = lift_table_top_dec.plot(x='decile',y='lift', kind='bar')
    fig = lt.get_figure()
    fig.savefig('lift_top_deciles.png')

    ##Save the lift image to bucket
    TOP_LIFT_file_name = image_path + ui_identifier + '_Lift_top_decile.png'
    blob = bucket.blob(TOP_LIFT_file_name)
    blob.upload_from_filename('lift_top_deciles.png')

    print('*'*10 + "\nCompleted the lift analysis.", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)

    print('*'*10 + "\nUploading the dfs to respective tables.", flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    ###Upload the output df to bigquery
    output_key = output_log_path + 'Output_'+ts_string+'.csv'
    util_obj.df_to_gcp(output_df,output_key,'churn_output_log',False)

    ##Upload the lift table to bigquery
    #lift_key = lift_file_location + 'Lift_All_deciles_' + ts_string + '.csv'
    #util_obj.df_to_gcp(lift_table,lift_key,'lift_all_deciles',False)

    ##Upload the top decile to bigquery
    lift_top_key = lift_file_location + 'Lift_Top_deciles_' + ts_string + '.csv'
    util_obj.df_to_gcp(lift_table_top_dec,lift_top_key,'lift_top_decile',False)

    ##Upload the attributes df to bigquery
    atts_key = atts_location + 'Model_Attributes_' + ts_string + '.csv'
    util_obj.df_to_gcp(atts_df,atts_key,'churn_model_attributes',False)

    print('*'*10 + '\nOutput log generation complete.', flush = True)
    print(datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S'),flush=True)
    print('*'*30,flush = True)

    return


from Deque import Deque

def palindrome_checker(str):

    char_deque = Deque()

    for ch in str:
        char_deque.add_rear(ch)

    still_equal = True
    while char_deque.size() > 1 and still_equal:
        first = char_deque.remove_front()
        last = char_deque.remove_rear()

        if first != last:
            still_equal = False

    return still_equal

print(palindrome_checker("radar"))
print(palindrome_checker("apple"))


# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series

def bias(
    close: Series, length: Int = None, mamode: str = None,
    offset: Int = None, **kwargs: DictLike
) -> Series:
    """Bias (BIAS)

    Rate of change between the source and a moving average.

    Sources:
        Few internet resources on definitive definition.
        Request by Github user homily, issue #46

    Args:
        close (pd.Series): Series of 'close's
        length (int): The period. Default: 26
        mamode (str): See ``help(ta.ma)``. Default: 'sma'
        offset (int): How many periods to offset the result. Default: 0

    Kwargs:
        fillna (value, optional): pd.DataFrame.fillna(value)
        fill_method (value, optional): Type of fill method

    Returns:
        pd.Series: New feature generated.
    """
    # Validate
    length = v_pos_default(length, 26)
    close = v_series(close, length)

    if close is None:
        return

    mamode = v_mamode(mamode, "sma")
    offset = v_offset(offset)

    # Calculate
    bma = ma(mamode, close, length=length, **kwargs)
    bias = (close / bma) - 1

    # Offset
    if offset != 0:
        bias = bias.shift(offset)

    # Fill
    if "fillna" in kwargs:
        bias.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        bias.fillna(method=kwargs["fill_method"], inplace=True)

    # Name and Category
    bias.name = f"BIAS_{bma.name}"
    bias.category = "momentum"

    return bias


# Qingxiang Guo
# {2022/5/25} {16:38}
'''
类和对象，是面向对象中的两个重要概念，类是对事物的抽象，而对象是类的一个实例。对象就是实例。
Python类的定义：使用class关键字定义一个类，并且首字母要大写，类可以将变量和函数组合在一起，称为封装。
类里面包括成员变量，成员函数。不过类之中的成员变量，称为类的属性，成员函数称为类的方法。
通俗的来讲，类是人类，具体的对象，是人类中的一个人。类有属性方法，比如人类有性别属性，有语言的能力（方法）。
而具体到个体（对象），也有具体的属性和方法。
比如list就是一个类，具体的某个列表，是实例对象。
'''
#下面演示如何定义一个类

class Test:
    first=123  #成员变量，也就是类的属性
    second=456  #成员变量，也就是类的属性
    def f(self):    #成员函数，也就是定义类的方法。这里的self，是约定俗称，指代了各个实例传入
        print('hello')

qingxiang=Test()  #将Test类实例化出一个对象，qingxiang
qingxiang.f()   #调用对象的方法

#第二个例子，关于汽车
class Car:
    color=''   #成员变量，默认空
    def run(self):  #成员函数
        print('go go go!!')
bmw=Car()  #实例化一个bmw
bmw.color='red'  #将红色赋值给对象bmw的颜色属性
print(bmw.color)
bmw.run()  #运行bmw的run方法
Car.run(bmw)  #这样也可以，输出的结果和bmw.run()是等价的，相当于将实例传送给类方法


# -*- coding: utf-8 -*-
import os
import re
import sys
from collections import Counter
import glob
import json
import random
import Levenshtein
import numpy as np
from sklearn.model_selection import train_test_split
from sacremoses import MosesTruecaser
from nltk.tokenize import word_tokenize
import matplotlib.pyplot as plt
MAX_SENT_LENGTH = 250
seed = 42
random.seed(seed)

def split_train_dev(infile, split_size):
    with open(infile, "r", encoding="utf8") as open_file:
        reader = open_file.readlines()
    reader = reader[1:]  # skip header
    print("Data size: ", len(reader))

    # split data into train and test
    train, dev = train_test_split(reader, test_size=split_size, random_state=42)

    print("Train size: ", len(train))
    print("Dev size: ", len(dev))

    with open(infile + ".train.split", "w", encoding="utf8") as open_file1:
        open_file1.write(("sentence" + "\t" + "label" + "\n"))
        open_file1.writelines(train)

    with open(infile + ".dev.split", "w", encoding="utf8") as open_file2:
        open_file2.write(("sentence" + "\t" + "label" + "\n"))
        open_file2.writelines(dev)

def tokenzier_truecaser(inputfile, outputfile):
    with open(inputfile, "r", encoding="utf8") as open_file:
        reader = open_file.readlines()
    output_file = open(outputfile, "wb")
    mtr = MosesTruecaser()

    tok_sentances = []
    tok_sentances_with_label = []
    unique_labels = Counter()

    for line in reader:
        if line != "\n" and line.__contains__("\t"):
            label, _, sentence = line.split("\t")
            if unique_labels.__contains__(label) is False:
                unique_labels[label] = 1
            else:
                unique_labels[label] += 1
            if re.search('[a-zA-Z]', sentence) is not None:
                tok_sentance = word_tokenize(sentence)
                tok_sentances.append(tok_sentance)
                tok_sentances_with_label.append(label + "\t" + " ".join(tok_sentance))

    print("number of unique labels: ", len(unique_labels))

    if os.path.exists(outputfile + ".truecasemodel") is not True:
        mtr.train(tok_sentances, save_to=outputfile + ".truecasemodel")

    my_truecaser = MosesTruecaser(outputfile + ".truecasemodel")

    output_file.write(("sentence" + "\t" + "label" + "\n").encode("utf8"))

    for line in tok_sentances_with_label:
        label, sentence = line.split("\t")
        output_file.write((my_truecaser.truecase(sentence, return_str=True) + "\t" + label + "\n").encode("utf8"))

def binary_classification(inputfile, outputfile):
    with open(inputfile, "r", encoding="utf8") as open_file:
        reader = open_file.readlines()

    with open(outputfile, "w", encoding="utf8") as open_file:
        reader = reader[1:]  # skip header
        open_file.write("sentence" + "\t" + "label" + "\n")

        for line in reader:
            if line != "\n" and line.__contains__("\t"):
                sentence, label = line.split("\t")
                label = label.replace("\n", "")
                if label == "SUD":
                    open_file.write(sentence + "\t" + "0" + "\n")
                elif label == "HOL":
                    open_file.write(sentence + "\t" + "1" + "\n")

def process_germen_data(raw_input_file):
    tokenzie_file = raw_input_file + ".tok"
    binary_flag = True

    tokenzier_truecaser(raw_input_file, tokenzie_file)
    print("Tokenize and truecase done!")

    if binary_flag is True:
        binary_classification(tokenzie_file, tokenzie_file + ".bin")
        print("Binary  data selection done!")

    print("*********************************")
    if raw_input_file.__contains__("train"):
        print("Splitting data into train and dev...")
        if binary_flag is True:
            split_train_dev(tokenzie_file + ".bin", 0.2)
        else:
            split_train_dev(tokenzie_file, 0.2)
        print("Splitting done!")

def get_data_statistics_de(input_folder):
    # get all files in the folder
    for file in glob.glob(input_folder + "/*"):
        if file.endswith(".tsv") is True:
            with open(file, "r", encoding="utf8") as open_file:
                reader = open_file.readlines()
                number_of_sentences = 0
                number_of_words = 0
                number_of_characters = 0
                longest_sentence_length = 0
                list_words = []
                for index, line in enumerate(reader):
                    if index > 0:
                        number_of_sentences += 1
                        sentence = line.split("\t")[0]
                        number_of_words += len(sentence.split(" "))
                        number_of_characters += len(sentence)
                        list_words.append(len(sentence.split(" ")))
                        if len(sentence) > longest_sentence_length:
                            longest_sentence_length = len(sentence)
                plt.plot(list_words, linestyle='dotted')
                plt.show()
                print("*********************************")
                print("File name: ", file)
                print("number of sentences: ", number_of_sentences)
                print("number of words: ", number_of_words)
                print("number of characters: ", number_of_characters)
                print(" average number of words per sentence: ", number_of_words / number_of_sentences)
                print(" midient number of words per sentence: ", np.median(list_words))
                print("average number of characters per sentence: ", number_of_characters / number_of_sentences)
                print("longest sentence length: ", longest_sentence_length)
                print("max length sentence: ", max(list_words))
                print("*********************************")

def get_label(inputfile):
    path = inputfile.split("/")  # split path
    label = ""
    for folder in path:
        if folder.__contains__("_texts"):
            label = folder.split("_")[0].upper()
            break
    return label

def too_much_numbers(sentence):
    # check if half of the sentence is numbers
    # if more than one third of the sentence is numbers, then it is not a valid sentence
    numbers = re.findall(r'\d+', sentence)
    if len(numbers) > len(sentence.split(' ')) / 3:
        return True
    else:
        return False

def sentence_validation_check(sentence):
    if sentence != "\n" and \
            sentence != "" and \
            sentence != " " and \
            len(sentence) > 5 \
            and sentence.count(";/") < 1 \
            and sentence.count("<") < 1 \
            and sentence.count(">") < 1 \
            and sentence.count("timestamp") < 1 \
            and sentence.count("username") < 1 \
            and sentence.count("wiki") < 1 \
            and sentence.count("__NOEDITSECTION__") < 1 \
            and sentence.__contains__("http://") is False \
            and too_much_numbers(sentence) is False \
            and re.search('[a-zA-Z]', sentence) is not None:

        return True

    else:
        return False

def sentences_are_similar(current_sentence, last_sentence):
    distance = Levenshtein.distance(current_sentence, last_sentence)
    if distance >= 30:
        return False
    else:
        return True

def break_into_small_sentences(sentence, trimmed_sentences):
    symbols = [".", "!", "?"]
    symbol = random.choice(symbols)

    # keep breaking sentence into smaller sentences until the length of the sentence is less than the random length limit
    while len(sentence) > MAX_SENT_LENGTH:
        random_length_limit = random.randint(50, MAX_SENT_LENGTH)
        last_period_index = sentence.rfind(symbol, 0, random_length_limit)
        if last_period_index != -1:
            trimmed_sentences.append(sentence[0:last_period_index + 1])
            sentence = sentence[last_period_index + 1:]
        else:
            trimmed_sentences.append(sentence[0:random_length_limit])
            sentence = sentence[random_length_limit:]

    trimmed_sentences.append(sentence)

    return trimmed_sentences

def get_clean_sentence(text):
    sentence = text.strip(" ").strip("\n").replace("\n", "")
    sentence = sentence.replace("
", "")
    sentence = sentence.replace("
", "")
    sentence = sentence.replace("
", "")
    sentence = sentence.replace("
", "")
    sentence = sentence.replace(";/", "")
    sentence = sentence.replace("<", "")
    sentence = sentence.replace(">", "")
    sentence = sentence.replace("\u00a0", " ")
    return sentence

def extract_sentences(inputfile, outputfile, label):
    with open(inputfile, "r", encoding="utf8") as open_file:
        reader = open_file.readlines()

    with open(outputfile, "w", encoding="utf8") as open_file:
        open_file.write("sentence" + "\t" + "label" + "\n")
        longest_sentence_length = 0
        number_of_extracted_sentences = 0
        output_sentences_list = []
        for index, line in enumerate(reader):
            last_text = json.loads(reader[index - 1])["text"]
            last_last_text = json.loads(reader[index - 2])["text"]
            current_text = json.loads(line)["text"]

            if sentences_are_similar(last_text, current_text) is False and sentences_are_similar(last_last_text,
                                                                                current_text) is False:
                sentence = get_clean_sentence(current_text)
                if sentence_validation_check(sentence):
                    trimmed_sentences = []
                    if len(sentence) > MAX_SENT_LENGTH:
                        for sub_sentence in break_into_small_sentences(sentence, trimmed_sentences):
                            if sentence_validation_check(sub_sentence):
                                if len(sub_sentence) > longest_sentence_length:
                                    longest_sentence_length = len(sub_sentence)
                                # remove sentence's starting spaces
                                sub_sentence = sub_sentence.strip(" ").strip("\n")
                                output_sentences_list.append(sub_sentence)
                                number_of_extracted_sentences += 1
                    else:

                        # remove sentence's starting spaces
                        sentence = sentence.strip(" ").strip("\n")
                        output_sentences_list.append(sentence)
                        number_of_extracted_sentences += 1
                    # if number_of_extracted_sentences == 14:
                    #     print("here")

        output_sentences_list = double_check_for_duplicates(output_sentences_list)

        for sentence in output_sentences_list:
            open_file.write(sentence + "\t" + label + "\n")

def double_check_for_duplicates(output_sentences_list):
    for index, sentence in enumerate(output_sentences_list):
        # compare current sentence with all the sentences after it to check if they are similar
        for next_sentence in output_sentences_list[index + 1:]:
            distance = Levenshtein.distance(sentence, next_sentence)
            if distance < 30:
                # print("duplicate found: \n", sentence, "\n",next_sentence)
                output_sentences_list.remove(next_sentence)
    return output_sentences_list

def get_data_statistics_it(data_path):
    with open(data_path, "r", encoding="utf8") as open_file:
        reader = open_file.readlines()

        number_of_sentences = 0
        number_of_words = 0
        number_of_characters = 0
        longest_sentence_length = 0
        list_words = []
        for index, line in enumerate(reader):
            if index > 0:
                number_of_sentences += 1
               # sentence = json.loads(line)["text"]
                sentence = line.split("\t")[0]
                number_of_words += len(sentence.split(" "))
                number_of_characters += len(sentence)
                list_words.append(len(sentence.split(" ")))
                # if len(sentence) > longest_sentence_length:
                #     longest_sentence_length = len(sentence)

        plt.plot(list_words, linestyle='dotted')
        plt.show()
        print("*********************************")
        print("number of sentences: ", number_of_sentences)
        print("number of words: ", number_of_words)
        print("number of characters: ", number_of_characters)
        print(" average number of words per sentence: ", number_of_words / number_of_sentences)
        print(" midient number of words per sentence: ", np.median(list_words))
        print("average number of characters per sentence: ", number_of_characters / number_of_sentences)
        print("longest sentence length: ", longest_sentence_length)
        print("max length sentence: ", max(list_words))
        print("*********************************")

    # write the data statistics to a file
    # with open(data_path + "_data_statistics.txt", "w", encoding="utf8") as open_file:
    #     open_file.write("number of sentences: " + str(number_of_sentences) + "\n")
    #     open_file.write("number of words: " + str(number_of_words) + "\n")
    #     open_file.write("number of characters: " + str(number_of_characters) + "\n")
    #     open_file.write("average number of words per sentence: " + str(number_of_words / number_of_sentences) + "\n")
    #     open_file.write("average number of characters per sentence: " + str(number_of_characters / number_of_sentences) + "\n")
    #     open_file.write("longest sentence length: " + str(longest_sentence_length) + "\n")

def combine_extract_files(folder_path):
    with open(folder_path + "extracted.combined", "w", encoding="utf8") as open_file:
        open_file.write("sentence" + "\t" + "label" + "\n")
        for file in os.listdir(folder_path):
            if file.endswith(".extract"):
                with open(folder_path + file, "r", encoding="utf8") as open_file2:
                    reader = open_file2.readlines()
                    for index, line in enumerate(reader):
                        if index > 0:
                            open_file.write(line)

def process_italian_data(raw_italian_folder):
    # for sub_folder in glob.glob(raw_italian_folder + "/*"):
    #     sub_folder = sub_folder.replace("\\", "/")
    #     for AA in glob.glob(sub_folder + "/*"):
    #         label = get_label(AA)
    #
    #         for file in glob.glob(AA + "/*"):
    #             if file.endswith(".extract") is not True and file.__contains__("combined") is False\
    #                     and file.__contains__("statistics") is False:
    #                 extract_sentences(file, file + ".extract", label)
    #                 print("Extracting sentences from " + file + " done!\n")
    #
    #         combine_extract_files(AA + "/")
    #         get_data_statistics_it(AA + "/extracted.combined")
    #         print("Combining extracted sentences from " + label + " done!")
    #         print("************************************************************\n")
    #
    # # combine all the extracted.combined files from all the sub folders into one file
    # with open(raw_italian_folder + "all_extracted.combined", "w", encoding="utf8") as open_file:
    #     open_file.write("sentence" + "\t" + "label" + "\n")
    #     for sub_folder in glob.glob(raw_italian_folder + "/*"):
    #         sub_folder = sub_folder.replace("\\", "/")
    #         for AA in glob.glob(sub_folder + "/*"):
    #             with open(AA + "/extracted.combined", "r", encoding="utf8") as open_file2:
    #                 reader = open_file2.readlines()
    #                 for index, line in enumerate(reader):
    #                     if index > 0:
    #                         open_file.write(line)
    #
    # get_data_statistics_it(raw_italian_folder + "all_extracted.combined")
    # print("Combining extracted sentences from all the sub folders done!")
    # print("************************************************************\n")
    #

    extract_sentences(raw_italian_folder + "AA/wiki_00", raw_italian_folder + "AA/wiki_00.extract", "test-label")

def main():
    # process_germen_data(raw_input_file = "../data/LSDC/LSDC_1.1.test")
    # process_italian_data(raw_italian_folder="../temp")
    process_italian_data(raw_italian_folder="../temp/eml_texts/")
    get_data_statistics_it("/Users/rxyz/PycharmProjects/selfExplain-local/SelfExplain/temp/eml_texts/AA/wiki_00.extract")

if __name__ == "__main__":
    main()


import numpy as np
from pyglet.window import key

# map integer keypress to action
def key_press(k):
    action = [0.0, 0.0, 0.0]
    if k==key.LEFT:  action[0] = -1.0
    if k==key.RIGHT:  action[0] = +1.0
    if k==key.UP:  action[1] = +1.0
    if k==key.DOWN:  action[2] = +0.8   # set 1.0 for wheels to block to zero rotation
    return action

def get_control_vec(a):
    if a == 0: return np.add(key_press(key.LEFT), key_press(key.UP))
    elif a == 1: return np.array(key_press(key.UP))
    elif a == 2: return np.add(key_press(key.RIGHT), key_press(key.UP))
    elif a == 3: return np.array(key_press(key.DOWN))