import requests
import base64
import hmac
from hashlib import sha1, md5
import time
import os
from xml.etree import ElementTree
import re
import json
import threading

blog_config_file = 'sync_blog_config.json'
assets_config_file = 'sync_assets_config.json'

class OSSBucket(object):
    content_type_map = {
        '.*': 'application/octet-stream',
        '.001': 'application/x-001',
        '.301': 'application/x-301',
        '.323': 'text/h323',
        '.906': 'application/x-906',
        '.907': 'drawing/907',
        '.a11': 'application/x-a11',
        '.acp': 'audio/x-mei-aac',
        '.ai': 'application/postscript',
        '.aif': 'audio/aiff',
        '.aifc': 'audio/aiff',
        '.aiff': 'audio/aiff',
        '.anv': 'application/x-anv',
        '.asa': 'text/asa',
        '.asf': 'video/x-ms-asf',
        '.asp': 'text/asp',
        '.asx': 'video/x-ms-asf',
        '.au': 'audio/basic',
        '.avi': 'video/avi',
        '.awf': 'application/vnd.adobe.workflow',
        '.biz': 'text/xml',
        '.bmp': 'application/x-bmp',
        '.bot': 'application/x-bot',
        '.c4t': 'application/x-c4t',
        '.c90': 'application/x-c90',
        '.cal': 'application/x-cals',
        '.cat': 'application/vnd.ms-pki.seccat',
        '.cdf': 'application/x-netcdf',
        '.cdr': 'application/x-cdr',
        '.cel': 'application/x-cel',
        '.cer': 'application/x-x509-ca-cert',
        '.cg4': 'application/x-g4',
        '.cgm': 'application/x-cgm',
        '.cit': 'application/x-cit',
        '.class': 'java/*',
        '.cml': 'text/xml',
        '.cmp': 'application/x-cmp',
        '.cmx': 'application/x-cmx',
        '.cot': 'application/x-cot',
        '.crl': 'application/pkix-crl',
        '.crt': 'application/x-x509-ca-cert',
        '.csi': 'application/x-csi',
        '.css': 'text/css',
        '.cut': 'application/x-cut',
        '.dbf': 'application/x-dbf',
        '.dbm': 'application/x-dbm',
        '.dbx': 'application/x-dbx',
        '.dcd': 'text/xml',
        '.dcx': 'application/x-dcx',
        '.der': 'application/x-x509-ca-cert',
        '.dgn': 'application/x-dgn',
        '.dib': 'application/x-dib',
        '.dll': 'application/x-msdownload',
        '.doc': 'application/msword',
        '.dot': 'application/msword',
        '.drw': 'application/x-drw',
        '.dtd': 'text/xml',
        '.dwf': 'Model/vnd.dwf',
        # '.dwf': 'application/x-dwf',
        '.dwg': 'application/x-dwg',
        '.dxb': 'application/x-dxb',
        '.dxf': 'application/x-dxf',
        '.edn': 'application/vnd.adobe.edn',
        '.emf': 'application/x-emf',
        '.eml': 'message/rfc822',
        '.ent': 'text/xml',
        '.epi': 'application/x-epi',
        '.eps': 'application/x-ps',
        # '.eps': 'application/postscript',
        '.etd': 'application/x-ebx',
        '.exe': 'application/x-msdownload',
        '.fax': 'image/fax',
        '.fdf': 'application/vnd.fdf',
        '.fif': 'application/fractals',
        '.fo': 'text/xml',
        '.frm': 'application/x-frm',
        '.g4': 'application/x-g4',
        '.gbr': 'application/x-gbr',
        '.	application/x-': '.gif',
        '.gl2': 'application/x-gl2',
        '.gp4': 'application/x-gp4',
        '.hgl': 'application/x-hgl',
        '.hmr': 'application/x-hmr',
        '.hpg': 'application/x-hpgl',
        '.hpl': 'application/x-hpl',
        '.hqx': 'application/mac-binhex40',
        '.hrf': 'application/x-hrf',
        '.hta': 'application/hta',
        '.htc': 'text/x-component',
        '.htm': 'text/html',
        '.html': 'text/html',
        '.htt': 'text/webviewhtml',
        '.htx': 'text/html',
        '.icb': 'application/x-icb',
        '.ico': 'image/x-icon',
        # '.ico': 'application/x-ico',
        '.iff': 'application/x-iff',
        '.ig4': 'application/x-g4',
        '.igs': 'application/x-igs',
        '.iii': 'application/x-iphone',
        '.img': 'application/x-img',
        '.ins': 'application/x-internet-signup',
        '.isp': 'application/x-internet-signup',
        '.IVF': 'video/x-ivf',
        '.java': 'java/*',
        '.jfif': 'image/jpeg',
        '.jpe': 'image/jpeg',
        # '.jpe': 'application/x-jpe',
        '.jpeg': 'image/jpeg',
        '.jpg': 'image/jpeg',
        # '.jpg': 'application/x-jpg',
        '.js': 'application/x-javascript',
        '.jsp': 'text/html',
        '.la1': 'audio/x-liquid-file',
        '.lar': 'application/x-laplayer-reg',
        '.latex': 'application/x-latex',
        '.lavs': 'audio/x-liquid-secure',
        '.lbm': 'application/x-lbm',
        '.lmsff': 'audio/x-la-lms',
        '.ls': 'application/x-javascript',
        '.ltr': 'application/x-ltr',
        '.m1v': 'video/x-mpeg',
        '.m2v': 'video/x-mpeg',
        '.m3u': 'audio/mpegurl',
        '.m4e': 'video/mpeg4',
        '.mac': 'application/x-mac',
        '.man': 'application/x-troff-man',
        '.math': 'text/xml',
        '.mdb': 'application/msaccess',
        # '.mdb': 'application/x-mdb',
        '.mfp': 'application/x-shockwave-flash',
        '.mht': 'message/rfc822',
        '.mhtml': 'message/rfc822',
        '.mi': 'application/x-mi',
        '.mid': 'audio/mid',
        '.midi': 'audio/mid',
        '.mil': 'application/x-mil',
        '.mml': 'text/xml',
        '.mnd': 'audio/x-musicnet-download',
        '.mns': 'audio/x-musicnet-stream',
        '.mocha': 'application/x-javascript',
        '.movie': 'video/x-sgi-movie',
        '.mp1': 'audio/mp1',
        '.mp2': 'audio/mp2',
        '.mp2v': 'video/mpeg',
        '.mp3': 'audio/mp3',
        '.mp4': 'video/mpeg4',
        '.mpa': 'video/x-mpg',
        '.mpd': 'application/vnd.ms-project',
        '.mpe': 'video/x-mpeg',
        '.mpeg': 'video/mpg',
        '.mpg': 'video/mpg',
        '.mpga': 'audio/rn-mpeg',
        '.mpp': 'application/vnd.ms-project',
        '.mps': 'video/x-mpeg',
        '.mpt': 'application/vnd.ms-project',
        '.mpv': 'video/mpg',
        '.mpv2': 'video/mpeg',
        '.mpw': 'application/vnd.ms-project',
        '.mpx': 'application/vnd.ms-project',
        '.mtx': 'text/xml',
        '.mxp': 'application/x-mmxp',
        '.net': 'image/pnetvue',
        '.nrf': 'application/x-nrf',
        '.nws': 'message/rfc822',
        '.odc': 'text/x-ms-odc',
        '.out': 'application/x-out',
        '.p10': 'application/pkcs10',
        '.p12': 'application/x-pkcs12',
        '.p7b': 'application/x-pkcs7-certificates',
        '.p7c': 'application/pkcs7-mime',
        '.p7m': 'application/pkcs7-mime',
        '.p7r': 'application/x-pkcs7-certreqresp',
        '.p7s': 'application/pkcs7-signature',
        '.pc5': 'application/x-pc5',
        '.pci': 'application/x-pci',
        '.pcl': 'application/x-pcl',
        '.pcx': 'application/x-pcx',
        '.pdf': 'application/pdf',
        '.pdx': 'application/vnd.adobe.pdx',
        '.pfx': 'application/x-pkcs12',
        '.pgl': 'application/x-pgl',
        '.pic': 'application/x-pic',
        '.pko': 'application/vnd.ms-pki.pko',
        '.pl': 'application/x-perl',
        '.plg': 'text/html',
        '.pls': 'audio/scpls',
        '.plt': 'application/x-plt',
        '.png': 'image/png',
        # '.png': 'application/x-png',
        '.pot': 'application/vnd.ms-powerpoint',
        '.ppa': 'application/vnd.ms-powerpoint',
        '.ppm': 'application/x-ppm',
        '.pps': 'application/vnd.ms-powerpoint',
        # '.ppt': 'application/vnd.ms-powerpoint',
        '.ppt': 'application/x-ppt',
        '.pr': 'application/x-pr',
        '.prf': 'application/pics-rules',
        '.prn': 'application/x-prn',
        '.prt': 'application/x-prt',
        '.ps': 'application/x-ps',
        # '.ps': 'application/postscript',
        '.ptn': 'application/x-ptn',
        '.pwz': 'application/vnd.ms-powerpoint',
        '.r3t': 'text/vnd.rn-realtext3d',
        '.ra': 'audio/vnd.rn-realaudio',
        '.ram': 'audio/x-pn-realaudio',
        '.ras': 'application/x-ras',
        '.rat': 'application/rat-file',
        '.rdf': 'text/xml',
        '.rec': 'application/vnd.rn-recording',
        '.red': 'application/x-red',
        '.rgb': 'application/x-rgb',
        '.rjs': 'application/vnd.rn-realsystem-rjs',
        '.rjt': 'application/vnd.rn-realsystem-rjt',
        '.rlc': 'application/x-rlc',
        '.rle': 'application/x-rle',
        '.rm': 'application/vnd.rn-realmedia',
        '.rmf': 'application/vnd.adobe.rmf',
        '.rmi': 'audio/mid',
        '.rmj': 'application/vnd.rn-realsystem-rmj',
        '.rmm': 'audio/x-pn-realaudio',
        '.rmp': 'application/vnd.rn-rn_music_package',
        '.rms': 'application/vnd.rn-realmedia-secure',
        '.rmvb': 'application/vnd.rn-realmedia-vbr',
        '.rmx': 'application/vnd.rn-realsystem-rmx',
        '.rnx': 'application/vnd.rn-realplayer',
        '.rp': 'image/vnd.rn-realpix',
        '.rpm': 'audio/x-pn-realaudio-plugin',
        '.rsml': 'application/vnd.rn-rsml',
        '.rt': 'text/vnd.rn-realtext',
        # '.rtf': 'application/msword',
        '.rtf': 'application/x-rtf',
        '.rv': 'video/vnd.rn-realvideo',
        '.sam': 'application/x-sam',
        '.sat': 'application/x-sat',
        '.sdp': 'application/sdp',
        '.sdw': 'application/x-sdw',
        '.sit': 'application/x-stuffit',
        '.slb': 'application/x-slb',
        '.sld': 'application/x-sld',
        '.slk': 'drawing/x-slk',
        '.smi': 'application/smil',
        '.smil': 'application/smil',
        '.smk': 'application/x-smk',
        '.snd': 'audio/basic',
        '.sol': 'text/plain',
        '.sor': 'text/plain',
        '.spc': 'application/x-pkcs7-certificates',
        '.spl': 'application/futuresplash',
        '.spp': 'text/xml',
        '.ssm': 'application/streamingmedia',
        '.sst': 'application/vnd.ms-pki.certstore',
        '.stl': 'application/vnd.ms-pki.stl',
        '.stm': 'text/html',
        '.sty': 'application/x-sty',
        '.svg': 'text/xml',
        '.swf': 'application/x-shockwave-flash',
        '.tdf': 'application/x-tdf',
        '.tg4': 'application/x-tg4',
        '.tga': 'application/x-tga',
        # '.tif': 'application/x-tif',
        '.tif': 'image/tiff',
        '.tiff': 'image/tiff',
        '.tld': 'text/xml',
        '.top': 'drawing/x-top',
        '.torrent': 'application/x-bittorrent',
        '.tsd': 'text/xml',
        '.txt': 'text/plain',
        '.uin': 'application/x-icq',
        '.uls': 'text/iuls',
        '.vcf': 'text/x-vcard',
        '.vda': 'application/x-vda',
        '.vdx': 'application/vnd.visio',
        '.vml': 'text/xml',
        '.vpg': 'application/x-vpeg005',
        # '.vsd': 'application/vnd.visio',
        '.vsd': 'application/x-vsd',
        '.vss': 'application/vnd.visio',
        # '.vst': 'application/vnd.visio',
        '.vst': 'application/x-vst',
        '.vsw': 'application/vnd.visio',
        '.vsx': 'application/vnd.visio',
        '.vtx': 'application/vnd.visio',
        '.vxml': 'text/xml',
        '.wav': 'audio/wav',
        '.wax': 'audio/x-ms-wax',
        '.wb1': 'application/x-wb1',
        '.wb2': 'application/x-wb2',
        '.wb3': 'application/x-wb3',
        '.wbmp': 'image/vnd.wap.wbmp',
        '.wiz': 'application/msword',
        '.wk3': 'application/x-wk3',
        '.wk4': 'application/x-wk4',
        '.wkq': 'application/x-wkq',
        '.wks': 'application/x-wks',
        '.wm': 'video/x-ms-wm',
        '.wma': 'audio/x-ms-wma',
        '.wmd': 'application/x-ms-wmd',
        '.wmf': 'application/x-wmf',
        '.wml': 'text/vnd.wap.wml',
        '.wmv': 'video/x-ms-wmv',
        '.wmx': 'video/x-ms-wmx',
        '.wmz': 'application/x-ms-wmz',
        '.wp6': 'application/x-wp6',
        '.wpd': 'application/x-wpd',
        '.wpg': 'application/x-wpg',
        '.wpl': 'application/vnd.ms-wpl',
        '.wq1': 'application/x-wq1',
        '.wr1': 'application/x-wr1',
        '.wri': 'application/x-wri',
        '.wrk': 'application/x-wrk',
        '.ws': 'application/x-ws',
        '.ws2': 'application/x-ws',
        '.wsc': 'text/scriptlet',
        '.wsdl': 'text/xml',
        '.wvx': 'video/x-ms-wvx',
        '.xdp': 'application/vnd.adobe.xdp',
        '.xdr': 'text/xml',
        '.xfd': 'application/vnd.adobe.xfd',
        '.xfdf': 'application/vnd.adobe.xfdf',
        '.xhtml': 'text/html',
        # '.xls': 'application/vnd.ms-excel',
        '.xls': 'application/x-xls',
        '.xlw': 'application/x-xlw',
        '.xml': 'text/xml',
        '.xpl': 'audio/scpls',
        '.xq': 'text/xml',
        '.xql': 'text/xml',
        '.xquery': 'text/xml',
        '.xsd': 'text/xml',
        '.xsl': 'text/xml',
        '.xslt': 'text/xml',
        '.xwd': 'application/x-xwd',
        '.x_b': 'application/x-x_b',
        '.sis': 'application/vnd.symbian.install',
        '.sisx': 'application/vnd.symbian.install',
        '.x_t': 'application/x-x_t',
        '.ipa': 'application/vnd.iphone',
        '.apk': 'application/vnd.android.package-archive',
        '.xap': 'application/x-silverlight-app',
    }
    filename_ext = re.compile(r'(\.[^.]+?)$')

    def __init__(self, host, bucket, access_key_id, access_key_secret):
        self.host = host
        self.bucket = bucket
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret

    def make_auth(self, auth_info: dict) -> str:
        verb = auth_info.get('verb')
        content_md5 = auth_info.get('content-md5') if auth_info.get('content-md5') else ''
        content_type = auth_info.get('content-type') if auth_info.get('content-type') else ''
        date = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
        canonicalized_oss_headers = \
            auth_info.get('canonicalized_oss_headers') if auth_info.get('canonicalized_oss_headers') else ''
        canonicalized_resource = \
            auth_info.get('canonicalized_resource') if \
            auth_info.get('canonicalized_resource') else '/' + self.bucket + '/'

        signature = base64.b64encode(
            hmac.new(
                self.access_key_secret.encode(),
                (
                    verb + '\n' +
                    content_md5 + '\n' +
                    content_type + '\n' +
                    date + '\n' +
                    canonicalized_oss_headers +
                    canonicalized_resource
                ).encode(),
                sha1
            ).digest()
        ).decode()

        return 'OSS {ak_id}:{signature}'.format(ak_id=self.access_key_id, signature=signature)

    def list_object(self) -> list:
        objs = []
        marker = None

        while True:
            headers = {
                'Host': self.host,
                'Date': time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()),
                'Authorization': self.make_auth({
                    'verb': 'GET',
                })
            }
            res = requests.get(
                'https://' + self.host + '/{marker}'.format(marker=('?marker=' + marker) if marker else ''),
                headers=headers
            )
            etree = ElementTree.fromstring(res.text)
            for content in etree.findall('Contents'):
                objs.append((content.find('Key').text, content.find('ETag').text[1:-1]))

            marker = etree.findall('NextMarker')
            if marker:
                marker = marker[0].text
            else:
                break

        return objs

    def put_object(self, obj_name: str, data: bytes) -> str:
        # 检索Content-Type
        ext = OSSBucket.filename_ext.findall(obj_name)
        content_type = None
        if ext:
            content_type = OSSBucket.content_type_map.get(ext[0])
        if content_type is None:
            content_type = 'application/octet-stream'

        # 计算Content-MD5
        content_md5 = base64.b64encode(md5(data).digest()).decode()

        headers = {
            'Host': self.host,
            'Date': time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()),
            'Content-Type': content_type,
            'Content-MD5': content_md5,
            'Content-Disposition': 'inline',
            'Authorization': self.make_auth({
                'verb': 'PUT',
                'content-md5': content_md5,
                'content-type': content_type,
                'canonicalized_resource': '/' + self.bucket + '/' + obj_name
            })
        }
        res = requests.put('https://' + self.host + '/' + obj_name, data=data, headers=headers)
        return 'OK  ' if res.status_code == 200 else 'Fail'

    def get_object(self, obj_name: str) -> bytes:
        headers = {
            'Host': self.host,
            'Date': time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()),
            'Authorization': self.make_auth({
                'verb': 'GET',
                'canonicalized_resource': '/' + self.bucket + '/' + obj_name
            })
        }
        res = requests.get('https://' + self.host + '/' + obj_name, headers=headers)
        return res.content

    def del_object(self, obj_name: str) -> str:
        headers = {
            'Host': self.host,
            'Date': time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()),
            'Authorization': self.make_auth({
                'verb': 'DELETE',
                'canonicalized_resource': '/' + self.bucket + '/' + obj_name
            })
        }
        res = requests.delete('https://' + self.host + '/' + obj_name, headers=headers)
        return 'OK  ' if res.status_code == 204 else 'Fail'

class FileManager(object):
    def __init__(self, root_dir: str):
        self.root_dir = root_dir

    def list_file(self) -> list:
        root = self.root_dir
        if root[-1] in ['/', '\\']:
            root = root[:-1]

        root_len = len(self.root_dir) + 1
        res = []
        for path in os.walk(root):
            if path[2]:
                for file in path[2]:
                    res.append((path[0].replace('\\', '/') + '/' + file)[root_len:])
        return res

    def read_file(self, file_name: str) -> bytes:
        with open(os.path.join(self.root_dir, file_name), 'rb') as file:
            data = file.read()
        return data

    def write_file(self, file_name: str, data: bytes):
        path = os.path.join(self.root_dir, file_name)
        if not os.path.isdir(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        with open(path, 'wb') as file:
            file.write(data)

    def del_file(self, file_name: str):
        path = os.path.join(self.root_dir, file_name)
        if os.path.isfile(path):
            os.remove(path)

    def clear_empty_folder(self):
        for path in os.walk(self.root_dir, False):
            if path[0] == self.root_dir:
                continue
            if not path[1] and not path[2]:
                os.rmdir(path[0])

class OSSSynchronizer(object):
    def __init__(self, local_dir: FileManager, oss_bucket: OSSBucket):
        self.local_dir = local_dir
        self.oss_bucket = oss_bucket

    # 检查同步情况
    def sync_checking(self) -> list:
        file_list = self.local_dir.list_file()
        obj_list = self.oss_bucket.list_object()

        # 将(文件名, ETag)二元组列表转为键值映射字典
        obj_map = dict()
        for obj in obj_list:
            obj_map[obj[0]] = obj[1]

        # 同步列表，三元组(文件名, 是否在本地, ETag)的列表
        sync_list = []

        # 从本地文件列表更新同步列表
        for file in file_list:
            sync_list.append((file, True, obj_map.get(file)))
            if obj_map.get(file):
                obj_map.pop(file)

        # 从OSS对象字典更新同步列表
        for obj, etag in obj_map.items():
            sync_list.append((obj, False, etag))

        return sync_list

    # 从本地同步到OSS
    def sync_from_local_to_oss(self):
        # 进行同步
        def sync(target_list: list):
            for thing in target_list:
                if thing[1]:  # 文件在本地
                    if thing[2] is not None:  # 本地和OSS各有一份
                        data = self.local_dir.read_file(thing[0])
                        file_md5 = md5(data).hexdigest().upper()
                        if file_md5 != thing[2]:  # 内容不一致，上传本地文件到OSS
                            res = self.oss_bucket.put_object(thing[0], data)
                            print('{status} [M] {filename}'.format(status=res, filename=thing[0]))
                    else:  # 文件不在OSS，上传本地文件到OSS
                        data = self.local_dir.read_file(thing[0])
                        res = self.oss_bucket.put_object(thing[0], data)
                        print('{status} [+] {filename}'.format(status=res, filename=thing[0]))
                else:  # 文件不在本地，删除OSS上的对应对象
                    res = self.oss_bucket.del_object(thing[0])
                    print('{status} [-] {filename}'.format(status=res, filename=thing[0]))

        sync_lists = self.sync_checking()
        threads_num = 64
        if len(sync_lists) < 64:
            threads_num = len(sync_lists)
        target_num = len(sync_lists) // threads_num
        target_num_mod = len(sync_lists) % threads_num
        threads = []
        for i in range(threads_num):
            if i < target_num_mod:
                threads.append(threading.Thread(
                    target=sync,
                    args=(sync_lists[(target_num + 1) * i:(target_num + 1) * (i + 1)],)
                ))
            else:
                threads.append(threading.Thread(
                    target=sync,
                    args=(sync_lists[target_num * i + target_num_mod:target_num * (i + 1) + target_num_mod],)
                ))

        for t in threads:
            t.start()
        for t in threads:
            t.join()

    # 从OSS同步到本地
    def sync_from_oss_to_local(self):
        # 进行同步
        def sync(target_list: list):
            for thing in target_list:
                if thing[1]:  # 文件在本地
                    if thing[2] is not None:  # 本地和OSS各有一份
                        data = self.local_dir.read_file(thing[0])
                        file_md5 = md5(data).hexdigest().upper()
                        if file_md5 != thing[2]:  # 内容不一致，下载OSS对应文件
                            res = self.oss_bucket.get_object(thing[0])
                            if res:
                                self.local_dir.write_file(thing[0], res)
                            print('{status} [M] {filename}'.format(status='OK' if res else 'Fail', filename=thing[0]))
                    else:  # 文件不在OSS，删除本地文件
                        self.local_dir.del_file(thing[0])
                        print('{status} [-] {filename}'.format(status='OK', filename=thing[0]))
                else:  # 文件不在本地，下载OSS上的对应对象
                    res = self.oss_bucket.get_object(thing[0])
                    if res:
                        self.local_dir.write_file(thing[0], res)
                    print('{status} [+] {filename}'.format(status='OK' if res else 'Fail', filename=thing[0]))

        sync_lists = self.sync_checking()
        threads_num = 64
        if len(sync_lists) < 64:
            threads_num = len(sync_lists)
        target_num = len(sync_lists) // threads_num
        target_num_mod = len(sync_lists) % threads_num
        threads = []
        for i in range(threads_num):
            if i < target_num_mod:
                threads.append(threading.Thread(
                    target=sync,
                    args=(sync_lists[(target_num + 1) * i:(target_num + 1) * (i + 1)], )
                ))
            else:
                threads.append(threading.Thread(
                    target=sync,
                    args=(sync_lists[target_num * i + target_num_mod:target_num * (i + 1) + target_num_mod],)
                ))

        for t in threads:
            t.start()
        for t in threads:
            t.join()

        # 清理空文件夹
        self.local_dir.clear_empty_folder()

if __name__ == '__main__':
    # 同步博客
    with open(blog_config_file, 'r', encoding='utf-8') as fp:
        config = json.load(fp)

    oss = OSSBucket(
        config['oss']['host'],
        config['oss']['bucket'],
        config['oss']['access_key_id'],
        config['oss']['access_key_secret']
    )
    local = FileManager(config['local_dir'])
    oss_synchronizer = OSSSynchronizer(local, oss)
    oss_synchronizer.sync_from_local_to_oss()

    # 同步静态文件
    with open(assets_config_file, 'r', encoding='utf-8') as fp:
        config = json.load(fp)

    oss = OSSBucket(
        config['oss']['host'],
        config['oss']['bucket'],
        config['oss']['access_key_id'],
        config['oss']['access_key_secret']
    )
    local = FileManager(config['local_dir'])
    oss_synchronizer = OSSSynchronizer(local, oss)
    oss_synchronizer.sync_from_local_to_oss()


"""
uses iwlist regex parsing from https://github.com/iancoleman/python-iwlist

iwlist is part of the Linux iw suite
"""

from __future__ import annotations
import pprint
import re
import logging
import subprocess
import argparse
import datetime
import json
import time
from pathlib import Path

cellRe = re.compile(r"^Cell\s+(?P<cellnumber>.+)\s+-\s+Address:\s(?P<mac>.+)$")
dataRe = [
    re.compile(r"^ESSID:\"(?P<essid>.*)\"$"),
    re.compile(
        r"^Frequency:(?P<frequency>[\d.]+) (?P<frequency_units>.+) \(Channel (?P<channel>\d+)\)$"
    ),
    re.compile(
        r"^Quality=(?P<signal_quality>\d+)/(?P<signal_total>\d+)\s+Signal level=(?P<signal_level_dBm>.+) d.+$"
    ),
    re.compile(
        r"^Signal level=(?P<signal_quality>\d+)/(?P<signal_total>\d+).*$"
    ),
]

def scan(iface: str) -> str:
    """
    use command line to scan for Wifi networks
    """
    cmd = ["iwlist", iface, "scan"]
    ret = subprocess.run(cmd, text=True, stdout=subprocess.PIPE, timeout=15)

    return ret.stdout

def parse(raw: str) -> dict[str, dict[str, str]]:
    """
    parse iwlist scan output
    """

    aps: dict[str, dict[str, str]] = {}
    for line in raw.split("\n"):
        line = line.strip()

        cell = cellRe.search(line)
        if cell:
            bssid = cell.group(2)
            aps[bssid] = {}
            continue

        for dat in dataRe:
            r = dat.search(line)
            if r:
                aps[bssid].update(r.groupdict())

    return aps

if __name__ == "__main__":
    P = argparse.ArgumentParser(description="WiFi scanner using iw")
    P.add_argument("-N", default=10, type=int, help="number of scans")
    P.add_argument(
        "-i", "--interface", default="wlan0", help="WiFi interface to scan"
    )
    P.add_argument(
        "logpath", help="directory to write JSON output to", default="."
    )
    args = P.parse_args()

    FMT = "%Y-%m-%dT%H_%M_%S"

    print(f"Wifi scan: {args.N} loops")

    now = datetime.datetime.now().strftime(FMT)

    logpath = Path(args.logpath).expanduser()
    logpath.mkdir(parents=True, exist_ok=True)
    logfile = logpath / ("wifi_" + now + ".json")
    print("Logging data to", logfile)

    dat_all = {}
    jstr = ""

    for _ in range(args.N):
        now = datetime.datetime.now().strftime(FMT)

        raw = scan(iface=args.interface)

        aps = parse(raw)
        pprint.pprint(aps)

        if not aps:
            logging.error(f"no data recorded {now}")
            time.sleep(1)
            continue

        if logfile.is_file():
            jstr = logfile.read_text()
            dat_all = json.loads(jstr)

        dat_all[now] = aps

        jstr = json.dumps(dat_all) + "\n"
        # we add a line ending so the data isn't all on one line for all times
        logfile.write_text(jstr)

        # try not to overwhelm the iw command with too many scans
        time.sleep(1)


from django.shortcuts import render
from django.core.mail import EmailMessage
from django.http import HttpResponse
from django.conf import settings
from django.template.loader import render_to_string

def home(request):
  return render(request, 'base/index.html')

def sendEmail(request):
  if request.method == 'POST':

    template = render_to_string('base/email_template.html', {
      'name': request.POST['name'],
      'email': request.POST['email'],
      'message': request.POST['message'],
    })

    email = EmailMessage(
      request.POST['subject'],
      template,
      settings.EMAIL_HOST_USER,
      ['yahao_yan@outlook.com']
    )

    email.send()

    return HttpResponse(status=204)


"""
Serves JSON data with CORS allowed from *.json files at given path.
"""
import json
import os
import sys

from flask import Flask, render_template_string, jsonify

app = Flask(__name__)
INDEX = '''<title>CORS all</title>

 INDEX of *.json files at {{ path }}:

    {% for file in files %}

{{ file }}

    {% endfor %}

'''

@app.route('/')
def index():
    path = os.path.abspath(os.environ.get('CORSALL_PATH', '.'))
    files = [fn.replace('.json', '')
             for fn in os.listdir(path) if fn.endswith('.json')]

    return render_template_string(INDEX, path=path, files=files)

@app.route('/<string:page>')
def return_with_cors_enabled(page):
    base_path = os.path.abspath(os.environ.get('CORSALL_PATH', '.'))
    page_path = os.path.join(base_path, page + '.json')
    with open(page_path) as f:
        data = json.load(f)
        response = jsonify(data)
        response.headers.add('Access-Control-Allow-Origin', '*')
        return response

def cli():
    try:
        path = sys.argv[1]
    except IndexError:
        pass
    else:
        os.environ['CORSALL_PATH'] = path
    app.run(debug=True)

if __name__ == "__main__":
    cli()


"""empty message

Revision ID: 3546f9ff35de
Revises: 2ecc16058eb2
Create Date: 2015-06-08 13:55:15.973308

"""

# revision identifiers, used by Alembic.
revision = '3546f9ff35de'
down_revision = '2ecc16058eb2'

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('task', sa.Column('description', sa.String(length=120), nullable=True))
    op.add_column('task', sa.Column('difficulty', sa.Integer(), nullable=True))
    op.add_column('task', sa.Column('done', sa.Boolean(), nullable=True))
    op.add_column('task', sa.Column('end_date', sa.Date(), nullable=True))
    op.add_column('task', sa.Column('priority', sa.Integer(), nullable=True))
    op.add_column('task', sa.Column('start_date', sa.Date(), nullable=True))
    op.add_column('task', sa.Column('title', sa.String(length=200), nullable=True))
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('task', 'title')
    op.drop_column('task', 'start_date')
    op.drop_column('task', 'priority')
    op.drop_column('task', 'end_date')
    op.drop_column('task', 'done')
    op.drop_column('task', 'difficulty')
    op.drop_column('task', 'description')
    ### end Alembic commands ###


import RPi.GPIO as GPIO
import tweepy
import os
from time import sleep
from credentials import *
import sqlite3
from textblob import TextBlob
import time

GPIO.setmode(GPIO.BOARD)
servoPin=11
GPIO.setup(servoPin, GPIO.OUT)
pwm=GPIO.PWM(servoPin,50)
db = sqlite3.connect('database.db')
cursor = db.cursor()
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
pwm.start(7)

for tweet in tweepy.Cursor(api.search, q="china").items():
    if (not tweet.retweeted) and ('RT @' not in tweet.text):
        try:
            print(tweet.user.screen_name)
            print((tweet.text).encode('utf-8').strip())
            user = tweet.user.screen_name
            tweet_text = tweet.text
            tweet_time = tweet.created_at
            blob = TextBlob(tweet.text)
            for sentence in blob.sentences:
                print(sentence.sentiment.polarity)
                cursor.execute('''INSERT INTO tweet(created_at, tweet_sentiment)
                        VALUES(?,?)''', (tweet_time, sentence.sentiment.polarity))
            db.commit()
            sleep(3)

            range_of_motion = (((sentence.sentiment.polarity + 1) * 180) / 2)
            DC=1./18.*(range_of_motion)+2
            pwm.ChangeDutyCycle(DC)

        except tweepy.TweepError as e:
            print(e.reason)
        except StopIteration:
            break

        sleep(3)

pwm.stop()
GPIO.cleanup()


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

from flask import Flask

app = Flask(__name__)

@app.route('/')
def root():
    app.logger.info('info log')
    app.logger.warning('warning log')
    return 'hello'

if __name__ == '__main__':
    app.debug = True

    handler = logging.FileHandler('demo2.log', encoding='UTF-8')

    handler.setLevel(logging.DEBUG)

    logging_format = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s'
    )

    handler.setFormatter(logging_format)

    app.logger.addHandler(handler)

    app.run()


from bs4 import BeautifulSoup
import re, urllib, urllib2, os, requests

def lookup_by_name_on_NIST(cmpd):
	""" Takes the name of a chemical compound as a string and looks it up on the NIST chemical
		webbook; returns the text of the site reached by searching for the input chemical"""
	url_safe_cmpd = urllib.quote(cmpd.encode('utf-8'))
	address = "http://webbook.nist.gov/cgi/cbook.cgi?Name=" + url_safe_cmpd + "&Units=SI"
	site_text = requests.get(address).text
	#this is site_text input for parse_text_from_NIST_site or
	#for find_link_if_directed_to_search_page
	return BeautifulSoup(site_text)

def find_link_if_directed_to_search_page(BeautifulSoup_sitetext):
	""" If initial chemical search through NIST ends up
		on a search results page with multiple links to
		different chemical pages, choose the link to
		the first chemical page"""
	soup = BeautifulSoup_sitetext
	links = soup.find_all('a')
	l = links[0].get('href', None)
	link_to_chem = "http://webbook.nist.gov" + l
	chem_sitetext = requests.get(link_to_chem).text
	return BeautifulSoup(chem_sitetext)

def find_permanent_link(BeautifulSoup_sitetext):
	"""If there is a Permanent link to a page for a compound
	right after the list of 'Other names' instead of 'Information on this page',
	then follow the link to the permanent page and use this page to scrape instead"""
	perma_link = BeautifulSoup_sitetext.find_all('a', href=True, text="Permanent link")
	l = perma_link[0].get('href', None)
	link_to_chem = "http://webbook.nist.gov" + l
	chem_sitetext = requests.get(link_to_chem).text
	return BeautifulSoup(chem_sitetext)

def parse_text_from_NIST_site(BeautifulSoup_sitetext, dict):
	""" Takes the HTML text from the NIST lookup site and adds the title chemical
		as well as all of the chemical synonyms listed on the page as keys in a dictionary.
		All values are the title chemical. This will allow us to convert all chemical
		synonyms in our data sheets and kovats databases into a single name, so that we
		can search and match."""
	soup = BeautifulSoup_sitetext
	htmlless = soup.text #removes html tags, makes a unicode string
	nless = re.sub("\n", " ", htmlless) #removes line breaks
	#main_chem_start_index = nless.find("ChemistryWebBook", 0, len(nless)) +16
	#main_chem_end_index = nless.find("Formula", 0, len(nless))
	#main_chem = nless[main_chem_start_index : main_chem_end_index].strip()
	main_chem = soup.find('h1').text.strip()
	if main_chem in dict:
		pass
	else:
		val = main_chem
		dict[val] = val
		if nless.find("Other names", 0, len(nless))> 0:	#adds synonyms only if there are "Other names" on NIST site
			other_start_index = nless.find("Other names", 0, len(nless)) +13
			other_end_index = nless.find("Information on this page:  Notes ", 0, len(nless))
			indiv_synonyms = nless[other_start_index : other_end_index]. split(";")
			for indiv_synonym in indiv_synonyms:
				indiv_synonym = indiv_synonym.strip()
				dict[indiv_synonym] = val
	#REMEMBER, ALL VALUES ARE UNICODE STRINGS
	#WHEN WRITING MATCHING CODE, NEED TO MAKE SURE MATCHES
	#ARE IN UNICODE TOO

def scrape_chem_synonyms_from_NIST(cmpd, dict):
	""" Takes a compound string and dictionary and returns the
	dictionary with all synonyms for a given compound as keys,
	with values as the header from the nist webbook (a single
	synonym) added to the original dictionary. Accounts for the
	three different results you can get when you do the initial
	search in NIST (Name Not Found, mulitple results, one chem result)"""
	BS_sitetext = lookup_by_name_on_NIST(cmpd)
	header = BS_sitetext.find('h1').text
	if header == u'Name Not Found': #compound not in NIST database
		dict[unicode(cmpd)] = unicode(cmpd)
	elif header == u'Search Results': #multiple matches for search
		BS_chem_site = find_link_if_directed_to_search_page(BS_sitetext)
		if BS_chem_site.find_all('a', href=True, text="Permanent link") != []:
		#going to the 'permanent link' for a compound if it exists
		#because this takes the place of 'Other Information' that usually marks
		#the end of the synonyms ('Other names') list
			BS_chem_site = find_permanent_link(BS_chem_site)
		head_chem = BS_chem_site.find('h1').text
		parse_text_from_NIST_site(BS_chem_site, dict)
		if cmpd not in dict:
				dict[unicode(cmpd)] = head_chem
	else: #compound page reached (header =  compound name or synonym)
		if BS_sitetext.find_all('a', href=True, text="Permanent link") != []:
		#going to the 'permanent link' for a compound if it exists
		#because this takes the place of 'Other Information' that usually marks
		#the end of the synonyms ('Other names') list
			BS_sitetext = find_permanent_link(BS_sitetext)
		parse_text_from_NIST_site(BS_sitetext, dict)
		if cmpd not in dict:
			dict[unicode(cmpd)] = header
	return dict

def scrape_whole_list_from_NIST(cmpd_list, dict):
	for compound in cmpd_list:
		if compound not in dict.keys():
			dict = scrape_chem_synonyms_from_NIST(compound, dict)
	return dict


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Feb  8 14:29:58 2020

@author: Daniel Adamkovic
"""
##

from qtGUIsetup import Ui_CodeAnalysisTool
from PyQt5 import QtCore, QtGui, QtWidgets
from copy import copy
import GitRepo as grepo
import shutil

# For analysis
import os
from Sonar import Sonar
from sonarAPI import API
from analysisParser import analysisParseList

# Could be moved to something like utils, if more functions like this are made
from main import wait

class UserInterface(Ui_CodeAnalysisTool):
    def __init__(self,CodeAnalysisTool):
        super(UserInterface,self).__init__()
        self.setupUi(CodeAnalysisTool)

        # these will hold information about repos, also declares self.active_repo
        self.repo_list, self.active_repo = self._default_repo_setup()

        # binds widgets to functions
        self.bindActiveWidgets()

    ##
    ## Binds widgets to the methods that process inputs
    ##
    def bindActiveWidgets(self):
        # events that change how the  active screeen changes
        self.repo_screen_butt.clicked.connect(lambda :self.menuScreenSel(0))
        self.sonar_screen_butt.clicked.connect(lambda :self.menuScreenSel(1))
        self.cs_screen_butt.clicked.connect(lambda :self.menuScreenSel(2))
        self.pmd_screen_butt.clicked.connect(lambda :self.menuScreenSel(3))
        self.settings_screen_butt.clicked.connect(lambda :self.menuScreenSel(4))

        # all bellow are buttons for folder selection
        self.sonar_save_dirsel.clicked.connect(lambda : self.docBrowser(0))
        self.cs_save_dirsel.clicked.connect(lambda : self.docBrowser(1))
        self.pmd_save_dirsel.clicked.connect(lambda : self.docBrowser(2))
        self.repo_save_dirsel.clicked.connect(lambda : self.docBrowser(3))

        # Buttons for running analyzers
        self.run_sonar_button.clicked.connect(self.analyseSonar)

        # user clicked the repo submit button, repo gets downlaoded and default branch gets set
        self.git_repo_input_submit.clicked.connect(self.readRepoUrl)

        # enables user input box and folder sel button at the same time
        self.repo_save_enable_butt.toggled.connect(self._enableSave)

        # switching repos
        self.tabWidget.currentChanged.connect(lambda : self.switchRepoContext(self.tabWidget.currentIndex()))

        # triggered if user selects different branch
        self.branch_selector.currentTextChanged.connect(self.switchBranch)

        self.repo_save_dir.textChanged.connect(self._updateSaveDir)
        print("Init complete")

    # every time user types into the input field this gets updated
    def _updateSaveDir(self):
        self.active_repo['save_dir'] = self.repo_save_dir.text()

    ## when user switches saving on or off we want to track it and change stuff
    def _enableSave(self):
        toggler = self.active_repo['keep']
        self.repo_save_enable_butt.setEnabled(not toggler)
        self.repo_save_dirsel.setEnabled(not toggler)
        self.repo_save_dir.setEnabled(not toggler)
        self.active_repo['keep'] = not toggler

    ## @brief Called when user changes branches in the dropdown menu
    def switchBranch(self):
        if self.active_repo['tools'] == None:   # dont do anything if the branches arent loaded yet
            return

        new_branch = self.branch_selector.currentText()
        self.active_repo['tools'].changeBranch(new_branch)
        self.active_repo['active_branch'] = new_branch
        # update info about the last commit in branch
        commit_data = self.active_repo['tools'].getCommitData()
        info_text = self.prettyDict(commit_data)
        self.active_repo['data'] = info_text
        self.repo_info.setText(self.active_repo['data'])

    ##@brief Called when the user switches between the top tabs
    # @param repo_idx Index specifying which repo needs to be displayed
    def switchRepoContext(self, repo_idx):

        # we copy the current active repo into the correct repo memory slot
        # this might not be needed since I can just bind the memory togetger but this
        # explicit way is clearer

        self.repo_list[self.active_repo['idx']] = copy(self.active_repo)
        self.active_repo = copy(self.repo_list[repo_idx])

        # if the repo is already loaded lock repo url input and load values into widgets
        if self.active_repo['init'] :
            self.git_repo_input.setEnabled(False)
            self.git_repo_input_submit.setEnabled(False)
            self.branch_selector.addItems(self.active_repo['branches'])
            self.git_repo_input.setText(self.active_repo['url'])
            self.repo_info.setText(self.active_repo['data'])
            self.branch_selector.setCurrentText(self.active_repo['active_branch'])

        # clear uninitialized widgets
        else:
            self.git_repo_input.setEnabled(True)
            self.git_repo_input_submit.setEnabled(True)
            self.branch_selector.clear()
            self.git_repo_input.setText("")
            self.repo_info.setText("")

        # enables/disables save input as neccessary
        if self.active_repo['keep']:
            self.repo_save_dir.setEnabled(True)
        else:
            self.repo_save_dir.setEnabled(False)

        # this can only be "" if we havent loaded anything in yet
        if self.active_repo['save_dir'] != "":
            self.repo_save_dir.setText(self.active_repo['save_dir'])
        else:
            self.repo_save_dir.setText("")

    ## @brief Gets called only once for every repository
    #  Currently it is not possible to load a different repository once we load something
    def readRepoUrl(self):

        self.active_repo['url'] = self.git_repo_input.text()
        self.active_repo['save_dir'] = self.repo_save_dir.text()

        # if we specified custom save dir
        if self.active_repo['save_dir'] == '':
            self.active_repo['save_dir'] = './work/repo{}'.format(self.active_repo['idx'])
        self.active_repo['created_in'] = self.active_repo['save_dir']

        # create instance of git repo
        git_repo = grepo.GitRepo(self.active_repo['save_dir'])
        self.active_repo['tools'] = git_repo

        self.active_repo['tools'].pullRepoContents(self.active_repo['url'])

        commit_data = self.active_repo['tools'].getCommitData()
        info_text = self.prettyDict(commit_data)
        self.active_repo['data'] = info_text
        self.repo_info.setText(self.active_repo['data'])

        self.active_repo['branches'] = commit_data['branches']
        self.branch_selector.addItems(self.active_repo['branches'])

        self.branch_selector.setEnabled(True)
        self.label.setEnabled(True)

        # user should NOT be able to change url of the repo after loading it
        self.active_repo['init'] = True
        self.git_repo_input.setEnabled(False)
        self.git_repo_input_submit.setEnabled(False)

    ## analyseSonar
    #  @brief Takes parameters from GUI and runs the current(14.2.) main function with them
    #  @details Literally copypasta from current(14.2.) main and changed command line parameters to the GUI ones.
    #           NOTE: This could be done in a separate module to be used by both, main and GUI.
    def analyseSonar(self):
        # Start SonarQube and API for it
        sonar = Sonar(sonarScannerImg='sonar-maven')
        if not sonar.isSonarQubeRunning():
            sonar.startSonarQube()
        api = API()

        # Get the save dir and make the git path with it
        save_dir = self.sonar_save_dir_input.text()
        if os.path.isabs(save_dir):
            git_full_path = os.path.join(save_dir, self.active_repo['created_in'])
        else:
            git_full_path = os.path.join(os.getcwd(), self.active_repo['created_in'])
        print("active_repo['created_in']:", git_full_path)

        wait('sonarqube', sonar.isSonarQubeRunning, timeout=5)

        # Running Sonar scanner(with sanity check)
        before = set(api.projects())
        print(before)
        sonar.runSonarScanner(git_full_path, 'clean', 'verify', 'sonar:sonar')

        wait('analysis', lambda: before.symmetric_difference(api.projects()), timeout=1)

        project_key = before.symmetric_difference(api.projects()).pop()
        print('project_key:', project_key)

        wait('issues', lambda: any(api.issues(project=project_key)), timeout=1)

        # Get the issues from API, parse and save as csv
        issues = list(api.issues(project=project_key))
        issue_file = os.path.join(save_dir, self.active_repo['created_in'] + '_issues.csv')
        analysisParseList(issues, issue_file) # NOTE: NOT TESTED!   Parse and write to file

        # Get the git commit data
        commit_file = os.path.join(save_dir,  self.active_repo['created_in'] + '_commits.csv')
        # TODO: get commit data for each commit associated with an issue
        #           and write to file
        print(len(issues))
        print(issue_file)
        print(commit_file)
        api.delete_project(project_key)

        # WARNING: The cleanup() doesn't stop SonarQube yet

    ## @brief Used to switch between user views (repo, )
    #  @param index Number representing the menu screen
    def menuScreenSel(self,index):
        self.stackedWidget.setCurrentIndex(index)

    ## @brief Initializes the repo list
    def _default_repo_setup(self):
        repo_list = []
        for num in range(6):
            repo = {}
            repo['init'] = False            # repo loaded
            repo['keep'] = False            # save/not save
            repo['url'] = ""                # repo url
            repo['branches'] = ""           # all branches
            repo['active_branch'] = ""      # currently selected branch
            repo['save_dir'] = ""           # holds where to save the repo
            repo['data'] = ""               # string info about the branch
            repo['tools'] = None            # holds git repo class instance
            repo['idx'] = num               # index of the repo
            repo['created_in'] = ""         # if this and save_dir are different contents may have to be copied before exit
            repo_list.append(repo)
        active_repo = repo_list[-1]
        active_repo['idx'] = 0
        return repo_list[:-1], active_repo

    ## @briefCalls Standard document browser and sets the info field with the chosen path
    #  @param idx Index determines where the data loaded will be saved
    def docBrowser(self,idx):
        folder = str(QtWidgets.QFileDialog.getExistingDirectory(self.centralwidget, "Select Directory"))
        if idx == 0:
            self.sonar_save_dir_input.setText(folder)
        elif idx == 1:
            self.cs_save_dir_input.setText(folder)
        elif idx == 2:
            self.pmd_save_dir_input.setText(folder)
        elif idx == 3:
            self.repo_save_dir.setText(folder)
            self.active_repo['save_dir'] = folder

    ## @brief Used to print dict contents into information field in a resonably nice way
    #  @param some_dict Any dictionary to be 'prettyfied'
    #  @note could probably just be used as private method
    def prettyDict(self,some_dict):
        output = ''
        for key, item in some_dict.items():
            output += str(key) + " : " + str(item) + "\n"
        return output

    ## Runs on exit and cleans workspace
    def cleanup(self):
        self.repo_list[self.active_repo['idx']] = self.active_repo
        for item in self.repo_list:
            file_dir = item['created_in']
            if not item['keep'] and file_dir != '':
                shutil.rmtree(file_dir)
                print("Removed" + file_dir)
            elif file_dir != item['save_dir']:
                shutil.move(item['created_in'],item['save_dir'])


from collections import OrderedDict

def compress(string):
  counts = OrderedDict()
  for char in string:
    counts[char] = counts.get(char, 0) + 1

  compressed = ''.join((char + str(count)) for char, count in counts.items())

  return ''.join(compressed) if (len(compressed) < len(string)) else string

assert compress('aaaabbbcddeeeee') == 'a4b3c1d2e5'
assert compress('aabbccddee') == 'aabbccddee'
assert compress('aaa') == 'a3'
assert compress('') == ''


# -*- coding: utf-8 -*-
"""
Created on Mon Apr 27 17:24:02 2015

@author: magistr
"""

# -*- coding: utf-8 -*-
import sys
import os.path
import matplotlib.pyplot as plt

from sklearn.svm import SVR
from EnsembleClassifier import EnsembleClassifier,OMEnsembleClassifier
from EnsembleClassifier import ANNEnsembleClassifier
from regres import read_data, read_ens_coeffs
from sklearn.cross_validation import ShuffleSplit
from sklearn.utils import shuffle

from statistics import mean, median
from SelectionStrategy import NoneStrategy

from numpy import percentile, std
from collections import defaultdict

from enscalibration_3 import calibrate_ensemble
from sklearn.metrics import mean_squared_error

from math import sqrt

def rmse(actual, predicted):
    ln = min(len(predicted), len(actual))
    rmse = sqrt(mean_squared_error(actual[:ln], predicted[:ln]))
    return rmse

if __name__ == "__main__":
    path = sys.argv[1]
    path2 = sys.argv[2]

    MODEL = "S1"

    measurementsFile = os.path.join(path, "2011080100_measurements_{}_2623.txt".format(MODEL))
    noswanFile = os.path.join(path, "2011080100_noswan_{}_48x434.txt".format(MODEL))
    swanFile = os.path.join(path, "2011080100_swan_{}_48x434.txt".format(MODEL))
    hirombFile = os.path.join(path, "2011080100_hiromb_{}_60x434.txt".format(MODEL))
    coeffsFile = os.path.join(path2, "ens_coefs.txt")
#    coeffsFile = "../Data/NewEns/ens_coefs.txt"

    measurements = read_data(measurementsFile)
    noswan = read_data(noswanFile)
    swan = read_data(swanFile)
    hiromb = read_data(hirombFile)

    coefs = list(read_ens_coeffs(coeffsFile))
    classifier = OMEnsembleClassifier([hiromb, swan, noswan],
                                              coefs, measurements, error_measurement=rmse)
    classifier.prepare(1)
    #classifier = EnsembleClassifier([hiromb, swan, noswan], coefs, measurements)

   # classifier2 = classifier.copy()

    total = len(hiromb)

    #svm_model = lambda : SVR(kernel='rbf', C=1e6, gamma=0.3)

#        learn_count = 30
    lin = []
    lin_s = []
    bst = []
    ns = []
    ptas = []
    ptas_by_ens = []
    ens_by_place = []
    best2selected_pred_dist = []
    best2selected_act_dist = []

    validate_count = total - 250
    variants = 40
    cross_set = list(ShuffleSplit(total, variants, validate_count, 250))

    max_size = 100
    ts_sizes = range(1, max_size) #250) #[30, 70, 120, 145]
    for learn_count in ts_sizes:
#        coefs = list(calibrate_ensemble([hiromb, swan, noswan],
#                                            measurements, learn_count))
#        classifier = OMEnsembleClassifier([hiromb, swan, noswan],
#                                              coefs, measurements)
#        classifier.prepare(1)

        predicted = []
        spred = []
        best = []
        ens = []
        pta = []

        ens_places = [0] * len(coefs)

        best2selected_pred = []
        best2selected_act = []

#        for (training_set, validate_set) in cross_set:
        for t in range(max_size, total):
           #ts = training_set[:learn_count]
#            ts = list(range(t-learn_count, t-8))
            classifier.train(t, learn_count)
            validate_set = [t]

            pred_ens, pred_err = classifier.predict_best_ensemble(t)
            predicted.append(pred_err)

            best_ens, best_err = classifier.get_best_ensemble(t)
            best.append(best_err)

            ens.append(classifier.get_biggest_ensemble(t)[1])
            pta.append(list(map(lambda x: abs(x[0]-x[1]),
                                classifier.get_predict_to_actual_error(t))))

            ensemble_prediction, actual_errors = classifier.get_ens_ranged_by_prediction(t)
            ens_places[ensemble_prediction.index(best_ens)] += 1
            spred.append(actual_errors[1:])

            pred_pred_err, best_pred_err = classifier.get_predicted_selected_to_best_error(t)
            best2selected_pred.append(pred_pred_err - best_pred_err)
            best2selected_act.append(pred_err - best_err)

#            full2selected.append(pred_err-)

        lmn = mean(predicted)
        lin.append((lmn, min(predicted), max(predicted)))
        lin_s.append([mean(p) for p in zip(*spred)])
        bst.append((mean(best), min(best), max(best)))
        ns.append((mean(ens), min(ens), max(ens)))
        ptas.append((mean(map(mean, pta)), std(list(map(mean, pta)))))
        pta_by_ens = list(zip(*pta))
        ptas_by_ens.append((list(map(mean, pta_by_ens)),
                            list(map(std, pta_by_ens)),
                            list(map(max, pta_by_ens)),
                            list(map(min, pta_by_ens))))

        ens_by_place.append(ens_places)

        best2selected_pred_dist.append(mean(best2selected_pred))
        best2selected_act_dist.append(mean(best2selected_act))
#        lin.append((lmn, lmn-std(predicted)/2, lmn+std(predicted)/2))
#        bst.append((mean(best), mean(best)-std(best)/2, mean(best)+std(best)/2))
#        ns.append((mean(ens), mean(ens)-std(ens)/2, mean(ens)+std(ens)/2))

#        bst.append((mean(best), percentile(best, 25), percentile(best, 75)))

    [lmean, l25, l75] = list(zip(*lin))
    [bmean, b25, b75] = list(zip(*bst))
    [emean, e25, e75] = list(zip(*ns))

    mean_err_by_place = list(zip(*lin_s))

    ###Mean error by training size
    plt.figure(figsize=[10,10])
    plt.title("Mean error by training size", fontsize=15)
    pline, = plt.plot(ts_sizes, lmean, "r-", label="Predicted ensemble", linewidth=2.0)
#    plt.fill_between(ts_sizes, l25, l75, facecolor="red", alpha=0.2)
    fline, = plt.plot(ts_sizes, emean, "b-", label="Full ensemble", linewidth=2.0)
#    plt.fill_between(ts_sizes, e25, e75, facecolor="blue", alpha=0.5)
    bline, = plt.plot(ts_sizes, bmean, "g-", label="Best ensemble", linewidth=2.0)
#    plt.fill_between(ts_sizes, b25, b75, facecolor="green", alpha=0.5)

    lines = []
#    for place, i in zip(mean_err_by_place, range(2, len(mean_err_by_place))):
#        line, = plt.plot(ts_sizes, place, "-", label="Predicted ensemble on {}".format(i))
#        lines.append(line)

    plt.ylabel("Mean RMS error, cm", fontsize=15)
    plt.xlabel("Training set size, forecast", fontsize=15)
    plt.legend(handles = [pline, fline, bline] + lines, fontsize=15)
    plt.show()
    plt.close()

    ###Mean error by training size
    [pta_mean, pta_std] = list(zip(*ptas))
    plt.figure(figsize=[10,10])

    plt.title("Mean error prediction error by training size")
    mean_line, = plt.plot(ts_sizes, pta_mean, "b-", label="Mean")
    pta_lower = [m-s for m,s in zip(pta_mean, pta_std)]
    pta_upper = [m+s for m,s in zip(pta_mean, pta_std)]
    plt.fill_between(ts_sizes[2:], pta_lower[2:], pta_upper[2:],
                         facecolor="blue", alpha=0.5)

    plt.ylabel("Mean error, sm")
    plt.xlabel("Training set size, forecast")
    plt.legend(handles=[mean_line])
    plt.show()
    plt.close()

    plt.figure(figsize=[14,12])
    plt.suptitle("Error prediction error by training size for each ensemble ")
    [pta_mean, pta_std, pta_max, pta_min] = zip(*ptas_by_ens)
    for mn, st, mi, mx, i in zip(zip(*pta_mean), zip(*pta_std),
                         zip(*pta_min), zip(*pta_max), range(8)):

        pta_lower =[m-s for m,s in zip(mn, st)]
        pta_upper =[m+s for m,s in zip(mn, st)]
        plt.subplot(3,3,i+1)

        plt.title("Ensemble {}".format(i+1))
        plt.plot(ts_sizes[2:], mn[2:])
        plt.fill_between(ts_sizes[2:], pta_lower[2:], pta_upper[2:],
                         facecolor="blue", alpha=0.5)
        plt.fill_between(ts_sizes[2:], mi[2:], mx[2:], facecolor="red", alpha=0.5)

        plt.ylim(0, 6)

    plt.show()
    plt.close()

    ###Fraction of predictions
    plt.figure(figsize=[10,10])
    plt.title("Fraction of predictions")
    plt.stackplot(ts_sizes, *(list(zip(*ens_by_place))), alpha=0.5)
    plt.ylabel("Count of predictions")
    plt.xlabel("Training set size, forecast")
    plt.show()
    plt.close()

    ###Mean error by training size
#    plt.figure(figsize=[10,10])
#    plt.title("Best to selected ensemble error distance")
#    pline, = plt.plot(ts_sizes, best2selected_pred_dist, label="Predicted")
#    aline, = plt.plot(ts_sizes, best2selected_act_dist, label="Actual")
#    plt.legend(handles=[pline, aline])
#    plt.ylabel("Mean error")
#    plt.xlabel("Training set size, forecast")
#    plt.show()
#    plt.close()

#    plt.suptitle("Mean error by history length\nvalidation set size={0}".format(validate_count),
#                        fontsize = 15)
#    for (errors_by_points_linear, errors_by_points_svr), i in zip(errors_by_ts, ts_sizes):
#        #plt.subplot(2,2, ts_sizes.index(i))
#        plt.title("Training set size={0}".format(i),
#                      fontsize=15)
#
#        l_line, = plt.plot(range(1, max_points), errors_by_points_linear, "-o", label="Linear regression")
#        #svr_line, = plt.plot(range(1, max_points), errors_by_points_svr, "-*", label="SVR")
#
#        #plt.legend(handles=[l_line, svr_line])
#
#        plt.ylim(3.25, 3.75)
#
#        plt.xlabel("Points", fontsize=12)
#        plt.ylabel("Mean error", fontsize=12)
#    plt.show()
#        ens_errors.append(mean(ens_list))
#        best_errors.append(mean(best_list))

#    fig = plt.figure(figsize=[7, 7])
#    plt.title("Error distribution for operative mode")
#
#    diffun = mean
#
#    plt.axhline(y=diffun(ml_list), linewidth = 1, color = '0.25',
#             linestyle = "--", label=median(ml_list))
#
#    plt.annotate("{0:.2f}".format(diffun(ml_list)),
#                 xy=(3, diffun(ml_list)), xytext=(3.33, diffun(ml_list)+0.1))
#
#    plt.axhline(y=diffun(ens_list), linewidth = 1, color = '0.25',
#             linestyle = "--", label=diffun(ml_list))
#
#    plt.annotate("{0:.2f}".format(diffun(ens_list)),
#                 xy=(3, diffun(ens_list)), xytext=(3-0.1, 0.1))
#
#    res = plt.boxplot(errors_by_time[:3], whis = 'range', showmeans = True)
#    plt.xticks([1,2,3], ["ml", "ens", "best"], fontsize = 12)
#
#    plt.xlabel("Ensemble selection approach", fontsize = 15)
#    plt.ylabel("Error", fontsize=15)
#    plt.show()
#    plt.close()

#    plt.figure(figsize=(10, 10))
#
#    plt.title("Validation set size={}".format(validate_count), fontsize = 15)
#
#    ml_line, = plt.plot(range(max_learn_count-1), ml_errors, label="Predicted")
#    ens_line, = plt.plot(range(max_learn_count-1), ens_errors, label="Ensemble")
#    best_lb, = plt.plot(range(max_learn_count-1), best_errors, label="Best")
#
#    plt.legend(handles=[ml_line, ens_line, best_lb], fontsize = 12)
#
#    plt.ylabel("Mean error", fontsize = 12)
#    plt.xlabel("Training set size", fontsize = 12)
#
#    plt.show()
#    plt.close()


#!/usr/bin/env python

from __future__ import print_function

import sys
import os
import argparse
import netCDF4 as nc
import numpy as np
import pandas as pd
import re
import datetime
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

"""
What this script does:

Plot time series of sea ice area in the Southern Ocean.

Example:

"""

NORTHERN_EXTENT_OF_SOUTHERN_OCEAN = -50

def time_index_to_date(time_var, time_idx):
    """
    Return a time point as a date

    We use periods here because pandas doesn't support datetime objects as low
    as 0001-01-01.
    """

    m = re.search('\d{4}-\d{2}-\d{2}', time_var.units)
    start_date = datetime.datetime.strptime(m.group(0), '%Y-%m-%d')
    time_point = time_var[time_idx]

    days = None
    if 'hours since' in time_var.units:
        days = int(time_point / 24.0)
    elif 'days since' in time_var.units:
        days = int(time_point)
    else:
        assert(False)

    return (start_date + datetime.timedelta(days=days))

def time_to_period(time_var, time_idx):
    """
    Return a time point as a pandas Period.

    We use periods here because pandas doesn't support datetime objects as low
    as 0001-01-01.
    """

    return pd.Period(time_index_to_date(time_var, time_idx), 'D')

def get_southern_ocean_mask(mask, lons, lats):
    """
    Return a masked array that masks the whole globe except the Southern Ocean.

    Input is the global bathymetry mask and the grid.
    """

    def find_nearest_index(array, value):
        return (np.abs(array - value)).argmin()

    # Initialise to all masked.
    ret_mask = np.ones_like(mask, dtype=bool)

    south_pole_lat_idx = find_nearest_index(lats[:,0], -90)
    north_ext_so_idx = find_nearest_index(lats[:,0],
                                          NORTHERN_EXTENT_OF_SOUTHERN_OCEAN)

    ret_mask[south_pole_lat_idx:north_ext_so_idx, :] = \
        mask[south_pole_lat_idx:north_ext_so_idx, :];

    return ret_mask

def calc_antarctic_ice_area(aice, area, lons, lats):
    """
    Calculate total ice area.
    """

    mask = get_southern_ocean_mask(aice[:, :].mask, lons, lats)

    # so_ stands for Southern Ocean.
    so_area = np.ma.masked_array(area, mask=mask)
    so_aice = np.ma.masked_array(aice, mask=mask)

    return np.sum(so_area * so_aice)

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument('model_files', nargs='+',
                        help='The CICE output data files.')
    parser.add_argument('--output_file', default='./',
                        help='will be written.')

    args = parser.parse_args()

    # Ice time series to calculate.
    ts = pd.DataFrame()

    # Go through input files one at a time building the timeseries as we go.
    for model_file in args.model_files:
        with nc.Dataset(model_file) as f:

            print('.', end='')
            sys.stdout.flush()

            time_var = f.variables['time']
            assert(time_var.shape == (1,))
            aice = f.variables['aice'][0, :, :]
            area = f.variables['tarea'][:, :]
            lons = f.variables['TLON'][:, :]
            lats = f.variables['TLAT'][:, :]

            period = time_to_period(time_var, 0)
            ice_area = calc_antarctic_ice_area(aice, area, lons, lats)

            new_ts = pd.DataFrame({'Area' : [ice_area]}, [period])
            ts = ts.append(new_ts)

    print('.')
    sys.stdout.flush()

    ts = ts.sort_index()

    # Make plots for the months of Sept and March
    mar_mask = map(lambda x : x.month == 3, ts.index)
    sept_mask = map(lambda x : x.month == 9, ts.index)
    mar_ts = pd.DataFrame({'March' : ts['Area'][mar_mask]})
    sept_ts = pd.DataFrame({'Sept' : ts['Area'][sept_mask]})
    annual_ts = pd.DataFrame({'Rolling mean' : ts['Area']})
    annual_ts = pd.rolling_mean(annual_ts, window=12)[11:]
    ax_mar = mar_ts.plot()
    ax_sept = sept_ts.plot(ax=ax_mar)
    annual_ts.plot(ax=ax_sept)
    plt.xlabel('Time (years)')
    plt.ylabel('m^2')
    plt.title('Antarctic Ice Area')
    fig = matplotlib.pyplot.gcf()
    fig.set_size_inches(9,4.5)
    plt.savefig(args.output_file)
    plt.close()

if __name__ == '__main__':
    sys.exit(main())


import cv2
import numpy as np
import time

cap = cv2.VideoCapture(0)

if (cap.isOpened() == False):
    print("Unable to read camera feed")

is_End = False

# pts1 = np.float32([[90,65],[0,230],[570,75],[640,240]])

# pts2 = np.float32([[10,10],[10,290],[290,10],[290,290]])

# M = cv2.getPerspectiveTransform(pts1, pts2)

pre_frame = cv2.imread('pre_frame.png')
pre_frame = cv2.cvtColor(pre_frame, cv2.COLOR_BGR2GRAY)
while(not is_End):
    is_Change = False

    while(not is_Change):
            frame_list = []

            is_Stable = False
            while(not is_Stable):
                ret, frame = cap.read()
                cv2.circle(frame, (90,65), 2, (255,0,0),-1)
                cv2.circle(frame, (0,230), 2, (255,0,0),-1)
                cv2.circle(frame, (570,75), 2, (255,0,0),-1)
                cv2.circle(frame, (640,240), 2, (255,0,0),-1)

                gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

                # gray_frame = cv2.warpPerspective(gray_frame, M, (300,300))

                if ret == True:
                    cv2.imshow('omok',gray_frame)
                    # time.sleep(1)

                    if cv2.waitKey(1) & 0xFF == ord('q'):
                        break
                    if len(frame_list):
                        frame_list = np.append(frame_list,np.array([gray_frame]),axis=0)
                    else:
                        frame_list = np.array([gray_frame])

                    if len(frame_list) == 5 :
                        if np.max(np.std(frame_list,axis=0)) < 50:
                            is_Stable = True
                            # cv2.imwrite('pre_frame.png',frame_list[-1])
                            print('Stable!')
                        else:
                            print(np.max(np.std(frame_list,axis=0)))
                            frame_list = frame_list[1:]
                            print('Not Stable!')

                else:
                    break

            if np.mean(abs(pre_frame - frame_list[-1])) > 10:
                print(np.mean(abs(pre_frame - frame_list[-1])))
                is_Change = True
                check_frame = abs(pre_frame - frame_list[-1])
                cv2.imshow('check_frame',check_frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
                time.sleep(5)
                print('Change!')
            else:
                print('Not Change!')

    cv2.imwrite('pre_frame.png',frame_list[-1])
    is_End = True
    print('End!')

cap.release()
cv2.destroyAllWindows()


from logging import LoggerAdapter

class GameLogger(LoggerAdapter):
    def process(self, msg, kwargs):
        new_msg = [msg]
        if 'game_id' in self.extra:
            new_msg.insert(0, '[%s]' % self.extra['game_id'])

        new_msg = ' '.join(new_msg)
        return new_msg, kwargs

def make_logger(base_logger, game_id=None):
    logger_extra = {}
    if game_id is not None:
        logger_extra['game_id'] = game_id

    return GameLogger(base_logger, logger_extra)


#!/usr/bin/env python
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.

"""
Simple Bot to reply to Telegram messages.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Echobot example, repeats messages.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""

import logging
import datetime
import sqlite3

from telegram import Update, ForceReply
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext

# Enable logging
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)

logger = logging.getLogger(__name__)

# Define a few command handlers. These usually take the two arguments update and
# context.
def start(update: Update, _: CallbackContext) -> None:
    """Send a message when the command /start is issued."""
    user = update.effective_user
    update.message.reply_markdown_v2(
        f'Hi {user.mention_markdown_v2()}\!',
        reply_markup=ForceReply(selective=True),
    )

def help_command(update: Update, _: CallbackContext) -> None:
    """Send a message when the command /help is issued."""
    update.message.reply_text('Este es el comado de ayuda')

def echo(update: Update, _: CallbackContext) -> None:
    """Echo the user message."""
    update.message.reply_text(update.message.text)

def pole(update: Update, _: CallbackContext) -> None:
    """Pole para AP"""
    weekno = datetime.datetime.today().weekday()
    now = datetime.datetime.now()
    hour = now.hour

    db = sqlite3.connect("data.db")
    cursor = db.cursor()

    #sacamos las fecha de consulta
    fecha_ant = "'" + str(now.year) + '-' + str(now.month) + '-' + str(now.day) + "'"
    fecha_act = datetime.datetime(now.year, now.month, (now.day+1))

    # if weekno < 7:
    if weekno < 5 and now.hour>=5:
        if(update.message.text.lower()=="pole"):
            user = update.effective_user
            #Validamos que no se haya hecho la pole
            datos = cursor.execute("SELECT * FROM poles WHERE pole='pole' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
            if len(datos.fetchall())==0:
                registros = ('pole',user.mention_markdown_v2(),3.5,now)
                datos = cursor.execute("INSERT INTO poles(pole,user,points,date) VALUES(?,?,?,?)",registros)
                db.commit()
                update.message.reply_markdown_v2(
                    f'Erda bien y tal, {user.mention_markdown_v2()}\ ha hecho la pole',
                )
        elif (update.message.text.lower()=="plata"):
            user = update.effective_user
            # Validamos que se haya hecho la pole
            datos = cursor.execute("SELECT * FROM poles WHERE pole='pole' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
            if len(datos.fetchall())>0:
                # Validamos que no se haya hecho una plata
                datos = cursor.execute("SELECT * FROM poles WHERE pole='plata' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
                if len(datos.fetchall())==0:
                    #Validamos que si este usuario ya hizo alguna position, no puede hacer plata
                    datos = cursor.execute("SELECT * FROM poles WHERE user='"+str(user.mention_markdown_v2())+"' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
                    if len(datos.fetchall())==0:
                        registros = ('plata',user.mention_markdown_v2(),2,now)
                        datos = cursor.execute("INSERT INTO poles(pole,user,points,date) VALUES(?,?,?,?)",registros)
                        db.commit()
                        update.message.reply_markdown_v2(
                            f'muy bien, {user.mention_markdown_v2()}\ ha hecho la plata',
                        )
        elif (update.message.text.lower()=="bronce"):
            user = update.effective_user
            # Validamos que se haya hecho la plata
            datos = cursor.execute("SELECT * FROM poles WHERE pole='plata' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
            if len(datos.fetchall())>0:
                # Validamos que no se haya hecho una plata
                datos = cursor.execute("SELECT * FROM poles WHERE pole='bronce' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
                if len(datos.fetchall())==0:
                    #Validamos que si este usuario ya hizo alguna position, no puede hacer plata
                    datos = cursor.execute("SELECT * FROM poles WHERE user='"+str(user.mention_markdown_v2())+"' AND date >=  '" +str(now.year) + "-" + str(now.month).zfill(2) + "-" + str(now.day).zfill(2) + "'")
                    if len(datos.fetchall())==0:
                        registros = ('bronce',user.mention_markdown_v2(),0.5,now)
                        datos = cursor.execute("INSERT INTO poles(pole,user,points,date) VALUES(?,?,?,?)",registros)
                        db.commit()
                        update.message.reply_markdown_v2(
                            f'Algo es algo, {user.mention_markdown_v2()}\ ha conseguido el bronce',
                        )
        elif (update.message.text.lower()=="fail"):
            user = update.effective_user
            registros = ('fail',user.mention_markdown_v2(),0,now)
            datos = cursor.execute("INSERT INTO poles(pole,user,points,date) VALUES(?,?,?,?)",registros)
            db.commit()
            update.message.reply_markdown_v2(
                f'Al menos lo intento, {user.mention_markdown_v2()}\ ha conseguido un Fail',
            )
    elif (update.message.text.lower()=="poleprueba" or update.message.text.lower()=="plataprueba" or update.message.text.lower()=="bronceprueba" or update.message.text.lower()=="fail"):
        user = update.effective_user
        update.message.reply_markdown_v2(
            f'Deja de molestarme {user.mention_markdown_v2()} que estas no son horas de estar haciendo {update.message.text.lower()}',
        )

def main() -> None:
    """Start the bot."""
    # Create the Updater and pass it your bot's token.
    updater = Updater("1730057456:AAHQ99r1lHoiEmKdl2NpVcza17hipO-lI_A")

    # Get the dispatcher to register handlers
    dispatcher = updater.dispatcher

    # on different commands - answer in Telegram
    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CommandHandler("help", help_command))

    # on non command i.e message - echo the message on Telegram
    dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, pole))
    dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, echo))

    # Start the Bot
    updater.start_polling()

    # Run the bot until you press Ctrl-C or the process receives SIGINT,
    # SIGTERM or SIGABRT. This should be used most of the time, since
    # start_polling() is non-blocking and will stop the bot gracefully.
    updater.idle()

if __name__ == '__main__':
    main()


# Noah Jackson
# Testing MIT Data

import wfdb
import pandas as pd
import numpy as np
import os
from decimal import Decimal

# TODO: Parse MIT sample data
def printData(recordNumber):
    # build path to specific record
    filePath = 'mit-bih-arrhythmia-database-1.0.0/' + str(recordNumber)

    df_columns = ["record_name", "n_sig", "fs", "counter_freq", "base_counter", "sig_len", "base_time", "base_date",
                  "comments", "sig_name", "p_signal", "d_signal", "e_p_signal", "file_name", "fmt", "samps_per_frame",
                  "skew", "byte_offset", "adc_gain", "baseline", "units", "adc_res", "adc_zero", "init_value",
                  "checksum", "block_size", "has_annotations"]

    formatted_patient_DF = pd.DataFrame(index=np.arange(1), columns=df_columns)

    # get specific record from MIT data set
    record = wfdb.rdrecord(filePath)

    # get all attributes of record object and store in attributes
    attributes = vars(record)
    # print(attributes)

    # initialize record value for later use
    recordValue = 0

    # will hold time series data for patient signal and mv
    p_signal_time = []
    signal_name = ""

    # iterate through attributes
    for attr in attributes:
        # example of selecting specific data segment from record
        # if attr == "checksum":

        if attr == "record_name":
            recordValue = getattr(record, attr)
            formatted_patient_DF['record_name'].values[0] = getattr(record, attr)

        if attr == "n_sig":
            formatted_patient_DF['n_sig'].values[0] = getattr(record, attr)

        if attr == "fs":
            formatted_patient_DF['fs'].values[0] = getattr(record, attr)

        if attr == "counter_freq":
            formatted_patient_DF['counter_freq'].values[0] = getattr(record, attr)

        if attr == "base_counter":
            formatted_patient_DF['base_counter'].values[0] = getattr(record, attr)

        if attr == "sig_len":
            formatted_patient_DF['sig_len'].values[0] = getattr(record, attr)

        if attr == "base_time":
            formatted_patient_DF['base_time'].values[0] = getattr(record, attr)

        if attr == "base_date":
            formatted_patient_DF['base_date'].values[0] = getattr(record, attr)

        if attr == "comments":
            formatted_patient_DF['comments'].values[0] = getattr(record, attr)

        if attr == "sig_name":
            formatted_patient_DF['sig_name'].values[0] = getattr(record, attr)
            signal_name = getattr(record, attr)

        if attr == "p_signal":
            formatted_patient_DF['p_signal'].values[0] = getattr(record, attr)
            p_signal_time = getattr(record, attr )

        if attr == "d_signal":
            formatted_patient_DF['d_signal'].values[0] = getattr(record, attr)

        if attr == "e_p_signal":
            formatted_patient_DF['e_p_signal'].values[0] = getattr(record, attr)

        if attr == "file_name":
            formatted_patient_DF['file_name'].values[0] = getattr(record, attr)

        if attr == "fmt":
            formatted_patient_DF['fmt'].values[0] = getattr(record, attr)

        if attr == "samps_per_frame":
            formatted_patient_DF['samps_per_frame'].values[0] = getattr(record, attr)

        if attr == "skew":
            formatted_patient_DF['skew'].values[0] = getattr(record, attr)

        if attr == "byte_offset":
            formatted_patient_DF['byte_offset'].values[0] = getattr(record, attr)

        if attr == "adc_gain":
            formatted_patient_DF['adc_gain'].values[0] = getattr(record, attr)

        if attr == "baseline":
            formatted_patient_DF['baseline'].values[0] = getattr(record, attr)

        if attr == "units":
            formatted_patient_DF['units'].values[0] = getattr(record, attr)

        if attr == "adc_res":
            formatted_patient_DF['adc_res'].values[0] = getattr(record, attr)

        if attr == "adc_zero":
            formatted_patient_DF['adc_zero'].values[0] = getattr(record, attr)

        if attr == "init_value":
            formatted_patient_DF['init_value'].values[0] = getattr(record, attr)

        if attr == "checksum":
            formatted_patient_DF['checksum'].values[0] = getattr(record, attr)

        if attr == "block_size":
            formatted_patient_DF['block_size'].values[0] = getattr(record, attr)

        # print each attribute name and its associated value for the record
        # print("\n%s: %s" % (attr, getattr(record, attr)))

    extractTimeData(p_signal_time, signal_name, recordValue)
    writePatientCSV(formatted_patient_DF, recordValue, 'All_Attributes_')

# will dynamically build list of columns for signals of patient record
def buildTimeDFCol(signalNames):
    df_columns = ["time"]
    count = 1

    for element in signalNames:
        df_columns.append(element)
        count += 1

    df_columns.append("signal_record_name")
    df_columns.append("annotation")
    return df_columns

def extractTimeData(p_signal_time, signal_name, record_value):
    df_columns = buildTimeDFCol(signal_name)
    formatted_pt_time_DF = pd.DataFrame(index=np.arange(650000), columns=df_columns)
    time = 0.0
    tempTime = 0.0
    count = 0

    for element in p_signal_time:
        formatted_pt_time_DF["signal_record_name"].values[count] = record_value
        formatted_pt_time_DF["time"].values[count] = time

        formatted_pt_time_DF[df_columns[1]].values[count] = element[0]

        formatted_pt_time_DF[df_columns[2]].values[count] = float(element[1])
        tempTime += 0.0027777777777777777777777777
        time = round(tempTime, 3)
        count += 1
    writePatientCSV(formatted_pt_time_DF, record_value, "TimeData_")

def writePatientCSV(patientData_DF, rec_num, fileName):
    # getting current working directory where patient data will be written
    curWorDir = os.getcwd()

    # filename
    ptFormattedFile = curWorDir + "/" + "Formatted_Patient_" + fileName + str(rec_num) + ".csv"

    # write formatted patient data to new csv file
    patientData_DF.to_csv(ptFormattedFile, sep=',', encoding='utf-8', index=False)

if __name__ == '__main__':
    rec_num = input("Please enter record number: ")
    print("You entered record number: " + rec_num)

    printData(rec_num)


import gym
import time
import numpy as np

from abp import HRAAdaptive
from abp.utils import clear_summary_path
from abp.explanations import PDX
from tensorboardX import SummaryWriter
from gym.envs.registration import register
from abp.openai.envs.four_towers_multi_unit_pysc2.FourTowerSequential import FourTowerSequentialMultiUnit

from absl import app
from absl import flags
from collections import namedtuple
import datetime
import time
import sys
import numpy as np
import pandas as pd
import csv
import json
import imutil

def run_task(evaluation_config, network_config, reinforce_config):
    import absl
    absl.flags.FLAGS(sys.argv[:1])
    env = FourTowerSequentialMultiUnit()

    # env = gym.make(evaluation_config.env)
    max_episode_steps = 100
    state = env.reset()
    # print(state)
    choices = [0,1,2,3]
    pdx_explanation = PDX()

    reward_types = ['damageToZealot', 'damageToZergling', 'damageToRoach', 'damageToStalker', 'damageToMarine', 'damageToHydralisk']

    agent = HRAAdaptive(name = "FourTowerSequential",
                        choices = choices,
                        reward_types = reward_types,
                        network_config = network_config,
                        reinforce_config = reinforce_config)

    training_summaries_path = evaluation_config.summaries_path + "/train"
    clear_summary_path(training_summaries_path)
    train_summary_writer = SummaryWriter(training_summaries_path)

    test_summaries_path = evaluation_config.summaries_path + "/test"
    clear_summary_path(test_summaries_path)
    test_summary_writer = SummaryWriter(test_summaries_path)

    totalDamageToZealot = 0
    totalDamageToZergling = 0
    totalDamageToRoach = 0
    totalDamageToStalker = 0
    totalDamageToMarine = 0
    totalDamageToHydralisk = 0

    # Training Episodes
    for episode in range(evaluation_config.training_episodes):
        state = env.reset()
        total_reward = 0
        done = False
        dead = False
        deciding = True
        running = True
        steps = 0
        rewards = []

        initial_state = np.array(state)

        while deciding:
            steps += 1
            action, q_values = agent.predict(state)
            state, reward, done, dead, info = env.step(action)

            while running:
                action = 4
                state, reward, done, dead, info = env.step(action)
                if done:
                    break

            if not dead:
                rewards = {'damageToZealot': env.decomposed_rewards[len(env.decomposed_rewards) - 1][0], 'damageToZergling': env.decomposed_rewards[len(env.decomposed_rewards) - 1][1], 'damageToRoach': env.decomposed_rewards[len(env.decomposed_rewards) - 1][2], 'damageToStalker': env.decomposed_rewards[len(env.decomposed_rewards) - 1][3], 'damageToMarine': env.decomposed_rewards[len(env.decomposed_rewards) - 1][4], 'damageToHydralisk': env.decomposed_rewards[len(env.decomposed_rewards) - 1][5]}

            else:
                rewards = {'damageToZealot': env.decomposed_rewards[len(env.decomposed_rewards) - 2][0], 'damageToZergling': env.decomposed_rewards[len(env.decomposed_rewards) - 2][1], 'damageToRoach': env.decomposed_rewards[len(env.decomposed_rewards) - 2][2], 'damageToStalker': env.decomposed_rewards[len(env.decomposed_rewards) - 2][3], 'damageToMarine': env.decomposed_rewards[len(env.decomposed_rewards) - 2][4], 'damageToHydralisk': env.decomposed_rewards[len(env.decomposed_rewards) - 2][5]}

            for reward_type in rewards.keys():
                agent.reward(reward_type, rewards[reward_type])

            total_reward += rewards['damageToZealot'] + rewards['damageToZergling'] + rewards['damageToRoach'] + rewards['damageToStalker'] + rewards['damageToMarine'] + rewards['damageToHydralisk']

            if dead:
                break

        totalDamageToZealot += rewards['damageToZealot']
        totalDamageToZergling += rewards['damageToZergling']
        totalDamageToRoach += rewards['damageToRoach']
        totalDamageToStalker += rewards['damageToStalker']
        totalDamageToMarine += rewards['damageToMarine']
        totalDamageToHydralisk += rewards['damageToHydralisk']

        print("Damage to Zealot: {}".format(totalDamageToZealot))
        print("Damage to Zergling: {}".format(totalDamageToZergling))
        print("Damage to Roach: {}".format(totalDamageToRoach))
        print("Damage to Stalker: {}".format(totalDamageToStalker))
        print("Damage to Marine: {}".format(totalDamageToMarine))
        print("Damage to Hydralisk: {}".format(totalDamageToHydralisk))

        agent.end_episode(state)
        test_summary_writer.add_scalar(tag="Train/Episode Reward", scalar_value=total_reward,
                                       global_step=episode + 1)
        train_summary_writer.add_scalar(tag="Train/Steps to collect all Fruits", scalar_value=steps + 1,
                                        global_step=episode + 1)

        print("EPISODE REWARD {}".format(total_reward))
        print("EPISODE {}".format(episode))

    agent.disable_learning()

    # Test Episodes
    for episode in range(evaluation_config.test_episodes):
        state = env.reset()
        total_reward = 0
        done = False
        steps = 0
        deciding = True
        running = True

        while deciding:
            steps += 1
            action, q_values = agent.predict(state)
            print(action)
            print(q_values)

            if evaluation_config.render:
                # env.render()
                pdx_explanation.render_all_pdx(action, 4, q_values, ['Top_Left', 'Top_Right', 'Bottom_Left', 'Bottom_Right'], ['damageToZealot', 'damageToZergling', 'damageToRoach', 'damageToStalker', 'damageToMarine', 'damageToHydralisk'])
                time.sleep(evaluation_config.sleep)
                # This renders an image of the game and saves to test.jpg
                # imutil.show(self.last_timestep.observation['rgb_screen'], filename="test.jpg")

            state, reward, done, dead, info = env.step(action)

            while running:
                action = 4
                state, reward, done, dead, info = env.step(action)
                if done:
                    break

            if dead:
                break

        agent.end_episode(state)

        test_summary_writer.add_scalar(tag="Test/Episode Reward", scalar_value=total_reward,
                                       global_step=episode + 1)
        test_summary_writer.add_scalar(tag="Test/Steps to collect all Fruits", scalar_value=steps + 1,
                                       global_step=episode + 1)


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

from odoo import models, fields, api, _
import requests
import json
from odoo.exceptions import Warning, UserError
import logging
_logger = logging.getLogger(__name__)
from datetime import datetime

class PolizasReport(models.TransientModel):
    _name = 'polizas.report'

    start_date = fields.Date("Fecha inicio")
    end_date = fields.Date("Fecha fin")
    journal_ids = fields.Many2many('account.journal',string='Diarios', default=lambda self: self.env['account.journal'].search([]))

    @api.multi
    def action_print_polizas_report(self):
        ctx = self._context.copy()
        domain = [('journal_id', 'in',self.journal_ids.ids)]
        if self.start_date:
            domain.append(('date','>=',self.start_date))
        if self.end_date:
            domain.append(('date','<=',self.end_date))

        journal_entries = self.env['account.move'].search(domain)
        company = self.env.user.company_id
        archivo_cer = company.archivo_cer
        archivo_key = company.archivo_key
        request_params = {
                'company': {
                      'rfc': company.rfc,
                      'api_key': company.proveedor_timbrado,
                      'modo_prueba': company.modo_prueba,
                },
                'informacion':{
                      'proceso': 'polizas',
                      'fecha_inicio': datetime.strftime(self.start_date, '%Y-%m-%d %H:%M:%S') or '', #self.start_date or '',
                      'fecha_fin': datetime.strftime(self.end_date, '%Y-%m-%d %H:%M:%S') or '', #self.end_date or '',
                },
                'certificados': {
                      'archivo_cer': archivo_cer.decode("utf-8"),
                      'archivo_key': archivo_key.decode("utf-8"),
                      'contrasena': company.contrasena,
                },}
        polizas = []
        for move in journal_entries:

            mv_vals = {
                'nombre' : move.name,
                'reference' : move.ref or '',
                'diario' : move.journal_id.name,
                'fecha' : datetime.strftime(move.date, '%Y-%m-%d %H:%M:%S'), #move.date,
                }
            transaccion = []
            for line in move.line_ids:
                if len(line.account_cfdi_ids) >= 1:
                    cfdi_line = line.account_cfdi_ids[0]
                    transaccion.append({
                       'cuenta' : line.account_id.name or '',
                       'codigo' : line.account_id.code or '',
                       'cliente' : line.partner_id.name or '',
                       'label' : line.name or '',
                       'debe' : line.debit,
                       'haber' : line.credit,
                       'compnal':{
                           'uuid' : cfdi_line.uuid,
                           'rfc' : cfdi_line.rfc_cliente,
                           'monto' : cfdi_line.monto,
                           'moneda' : cfdi_line.moneda,
                           'tipcamb' : cfdi_line.tipocamb,
                           },
                       })
                else:
                    transaccion.append({
                       'cuenta' : line.account_id.name or '',
                       'codigo' : line.account_id.code or '',
                       'cliente' : line.partner_id.name or '',
                       'label' : line.name or '',
                       'debe' : line.debit,
                       'haber' : line.credit,
                       })
            mv_vals.update({'transaccion' : transaccion})
            polizas.append(mv_vals)
        request_params.update({'polizas': polizas})

        url=''
        company = self.env.user.company_id
        if company.proveedor_timbrado == 'multifactura':
            url = '%s' % ('http://facturacion.itadmin.com.mx/api/contabilidad')
        elif company.proveedor_timbrado == 'gecoerp':
            if company.modo_prueba:
                url = '%s' % ('https://itadmin.gecoerp.com/invoice/?handler=OdooHandler33')
            else:
                url = '%s' % ('https://itadmin.gecoerp.com/invoice/?handler=OdooHandler33')
        if not url:
            raise Warning("Seleccione el proveedor de timbrado en la configuración de la compañía.")

        response = requests.post(url,auth=None,verify=False, data=json.dumps(request_params),headers={"Content-type": "application/json"})
        _logger.info('something ... %s', response.text)

        json_response = response.json()
        estado_factura = json_response.get('estado_conta','')
        if estado_factura == 'problemas_contabilidad':
            raise UserError(_(json_response['problemas_message']))
        if json_response.get('conta_xml'):

            #_logger.info("xml %s", json_response['conta_xml'])
            #_logger.info("zip %s", json_response['conta_zip'])

            #return base64.b64decode(json_response['conta_xml'])
            try:
                form_id = self.env['ir.model.data'].get_object_reference('contabilidad_cfdi', 'reporte_conta_xml_zip_download_wizard_download_form_view_itadmin')[1]
            except ValueError:
                form_id = False
            ctx.update({'default_xml_data': json_response['conta_xml'], 'default_zip_data': json_response.get('conta_zip', None)})
            return {
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'conta.xml.zip.download',
                'views': [(form_id, 'form')],
                'view_id': form_id,
                'target': 'new',
                'context': ctx,
            }
        return True