コード例 #1
0
ファイル: RSA.py プロジェクト: jason40418/iot_project
 def __init__(self, usage: str, ip: str, rsa_key: KeyRSA, expire: str):
     self.__usage = usage
     self.__ip = ip
     self.__rsa_key = rsa_key
     self.__create = datetime.now()
     self.__expire = self.__create + timedelta(seconds=int(expire))
     self.__status = 0
     self.__cfg = Config()
     self.__dbmgr = DBMgr()
     self.__rsa_id = self.__insert_to_db()
コード例 #2
0
ファイル: RSA.py プロジェクト: jason40418/iot_project
 def __init__(self, rsa_id: str, usage: str, ip: str, rsa_key: KeyRSA,
              create: datetime, expire: datetime, status: int):
     self.__rsa_id = rsa_id
     self.__usage = usage
     self.__ip = ip
     self.__rsa_key = rsa_key
     self.__create = create
     self.__expire = expire
     self.__status = status
     self.__cfg = Config()
     self.__dbmgr = DBMgr()
コード例 #3
0
class SensorDataHelper():
    dbmgr = DBMgr()

    @staticmethod
    def get_sensor_from_db():
        sql = "SELECT * FROM `iot`.`sensor`"
        args = {}

        while True:
            status, row, record = SensorDataHelper.dbmgr.query(sql, args)
            if status: break

        return record
コード例 #4
0
class AccessHelper():
    dbmgr = DBMgr()

    @staticmethod
    def get_by_name(account):
        sql = " SELECT * \
                FROM `iot`.`access` \
                WHERE `access`.`account` = %(account)s \
                ORDER BY `access`.`id` DESC"

        args = {'account': account}

        while True:
            status, row, data = AccessHelper.dbmgr.query(sql, args)
            if status: break

        if row == 0:
            return False, {}
        elif row == 1:
            return True, data[0]
        # 為系統發生例外狀況
        else:
            return True, data[0]

    def get_all():
        sql = " SELECT * \
                FROM `iot`.`access` \
                ORDER BY `access`.`id` DESC"

        args = {}

        while True:
            status, row, data = AccessHelper.dbmgr.query(sql, args)
            if status: break

        return status, row, data

    @staticmethod
    def get_records_by_name(account):
        sql = " SELECT * \
                FROM `iot`.`access_record` \
                WHERE `access_record`.`account` = %(account)s \
                ORDER BY `access_record`.`id` DESC"

        args = {'account': account}

        while True:
            status, row, data = AccessHelper.dbmgr.query(sql, args)
            if status: break

        return status, row, data

    @staticmethod
    def entry(account):
        sql = "INSERT INTO `iot`.`access`(`access`.`account`) VALUES(%(account)s)"
        args = {'account': account}

        while True:
            status, row, result_id = AccessHelper.dbmgr.insert(sql,
                                                               args,
                                                               multiple=False)
            if status:
                break

    @staticmethod
    def delete(account):
        sql = "DELETE FROM `iot`.`access` WHERE `access`.`account` = %(account)s"
        args = {'account': account}

        while True:
            status, row, result_id = AccessHelper.dbmgr.delete(sql, args)
            if status: break

    @staticmethod
    def add_record(account, entry, exit):
        sql = " INSERT INTO `iot`.`access_record`(`access_record`.`account`, `access_record`.`entry`, `access_record`.`exit`) \
                VALUES(%(account)s, %(entry)s, %(exit)s)"

        args = {
            'account': account,
            'entry': entry,
            'exit': exit,
        }

        while True:
            status, row, result_id = AccessHelper.dbmgr.insert(sql,
                                                               args,
                                                               multiple=False)
            if status:
                break

    @staticmethod
    def exit(account, entry_dt, trigger=None):
        entry_dt_str = entry_dt.isoformat(' ')

        if trigger == 'system':
            years = entry_dt.year
            months = entry_dt.month
            days = entry_dt.day
            exit_dt = datetime.datetime(years, months, days, 23, 59, 59)
            exit_dt_str = exit_dt.isoformat(' ')

        else:
            exit_dt_str = datetime.datetime.today().isoformat(' ')

        AccessHelper.add_record(account, entry_dt_str, exit_dt_str)
        AccessHelper.delete(account)
コード例 #5
0
class MemberHelper():
    dbmgr = DBMgr()

    @staticmethod
    def check_duplicate_by_account(account):
        sql = "SELECT * FROM `iot`.`member` WHERE `member`.`account` = %(account)s"
        args = {'account': account}

        status, row, result = MemberHelper.dbmgr.query(sql, args)

        if not status:
            result = {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 503
            }
            return False, result, 503
        elif row != 0:
            result = {
                'error_type': "AccountDuplocatedError",
                'error_msg': "此帳號資料庫已經存在,請勿重複註冊",
                'error_code': 400
            }
            return False, result, 400
        else:
            result = {
                'type': "AccountPassVerify",
                'msg': "此帳號為新使用者,可以使用",
                'code': 200
            }
            return True, result, 200

    @staticmethod
    def create(member):
        # 取得資料表欄位
        table_col = MemberHelper.dbmgr.get_db_column('iot', 'member')
        sql = MemberHelper.dbmgr.insert_sql('member', table_col, False)
        args = member.get_all_parameter()
        status, row, result = MemberHelper.dbmgr.insert(sql, args)

        # 看新增狀態
        if status:
            return True, {
                'type': "AccountRegisterSuccess",
                'msg': "帳號新增至資料庫成功",
                'code': 200
            }, 200
        else:
            return False, {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 500
            }, 500

    @staticmethod
    def get(account):
        sql = "SELECT * FROM `iot`.`member` WHERE `account`=%(account)s"
        args = {'account': account}
        status, row, result = MemberHelper.dbmgr.query(sql, args)

        if not status:
            return False, {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 500
            }, 500
        # 發生未預期錯誤(帳號超過一個例外狀況)
        elif row > 1:
            return False, {
                'error_type': "AccountMoreThanOneException",
                'error_msg': "資料庫存在該帳號之資料超過一筆異常",
                'error_code': 500
            }, 500
        # 找不到帳號
        elif row == 0:
            return False, {
                'error_type': "AccountVerifyError",
                'error_msg': "帳號不存在或密碼錯誤,請確認後重新輸入",
                'error_code': 400
            }, 400
        else:
            m = Member(result[0]['id'], result[0]['account'],
                       result[0]['name'], result[0]['email'],
                       result[0]['password'], result[0]['identity'],
                       result[0]['create'], result[0]['modify'])
            return True, m, 200

    @staticmethod
    def update(member):
        sql = " UPDATE `iot`.`member` \
                SET `member`.`name`=%(name)s, `member`.`email`=%(email)s, \
                    `member`.`password`=%(password)s, `member`.`modify`=%(modify)s \
                WHERE `member`.`account`=%(account)s"

        args = member.get_all_parameter()
        status, row, result = MemberHelper.dbmgr.update(sql, args)

        if not status:
            return False, {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 500
            }, 500
        # 發生未預期錯誤(帳號超過一個例外狀況)
        elif row > 1:
            return False, {
                'error_type': "AccountMoreThanOneException",
                'error_msg': "資料庫存在該帳號之資料超過一筆異常",
                'error_code': 500
            }, 500
        # 找不到帳號
        elif row == 0:
            return False, {
                'error_type': "AccountVerifyError",
                'error_msg': "帳號不存在或密碼錯誤,請確認後重新輸入",
                'error_code': 400
            }, 400
        else:
            return True, {
                'type': "AccountUpdateSuccess",
                'msg': "帳號更新成功",
                'code': 200
            }, 200

    @staticmethod
    def verify(account, password):
        sql = "SELECT `password`, `identity` FROM `iot`.`member` WHERE `account`=%(account)s"
        args = {'account': account}
        status, row, result = MemberHelper.dbmgr.query(sql, args)

        if not status:
            return False, {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 500
            }, 500
        # 發生未預期錯誤(帳號超過一個例外狀況)
        elif row > 1:
            return False, {
                'error_type': "AccountMoreThanOneException",
                'error_msg': "資料庫存在該帳號之資料超過一筆異常",
                'error_code': 500
            }, 500
        # 找不到帳號
        elif row == 0:
            return False, {
                'error_type': "AccountVerifyError",
                'error_msg': "帳號不存在或密碼錯誤,請確認後重新輸入",
                'error_code': 400
            }, 400
        # 自資料庫取回該帳號之加密密碼成功
        else:
            # 編碼成utf-8以進行檢查
            password = password.encode("utf-8")
            hash_password = result[0]["password"].encode("utf-8")

            # 嘗試進行bcrypt金鑰檢查
            if (bcrypt.checkpw(password, hash_password)):
                return True, {
                    'type': "AccountVerifySucess",
                    'msg': "帳號密碼驗證成功",
                    'code': 200
                }, 200
            else:
                return False, {
                    'error_type': "AccountVerifyError",
                    'error_msg': "帳號不存在或密碼錯誤,請確認後重新輸入",
                    'error_code': 400
                }, 400
コード例 #6
0
class RSAHelper():
    dbmgr = DBMgr()

    @staticmethod
    def check_key_vaild(key_id, request_ip, usage):
        '''
        驗證金鑰有效性
        '''
        curr_datetime = datetime.now()
        sql = "SELECT * FROM `iot`.`rsa` WHERE `rsa`.`id` = %(id)s"
        args = {'id': key_id}
        status, row, result = RSAHelper.dbmgr.query(sql, args)

        # Case 1: 資料庫檢索失敗
        if not status:
            result = {
                'error_type': "DatabaseError",
                'error_msg': "[" + str(result[0]) + "]" + str(result[1]),
                'error_code': 503
            }
        # Case 2: 金鑰超過一把(伺服器500發生未預期錯誤)
        elif row > 1:
            result = {
                'error_type': "KeyMoreThanOne",
                'error_msg': "系統發生未預期錯誤,金鑰超過一把",
                'error_code': 500
            }
        # Case 3: 金鑰不存在
        elif row == 0:
            result = {
                'error_type': "KeyNotExist",
                'error_msg': "請求之金鑰不存在",
                'error_code': 400
            }
        # Case 4: 金鑰與請求IP不相符(正確只會有一筆,直接取用list第一筆dict資料)
        elif result[0]['ip'] != request_ip:
            result = {
                'error_type': "KeyNotMatchRequestIP",
                'error_msg': "請求之金鑰與IP位置不相符",
                'error_code': 400
            }
        # Case 5: 金鑰請求與用途不相符
        elif result[0]['usage'] != usage:
            result = {
                'error_type': "KeyNotMatchUsage",
                'error_msg': "請求之金鑰與金鑰用途不相符",
                'error_code': 400
            }
        # Case 6: 金鑰已經過期
        elif result[0]['expire'] < curr_datetime:
            result = {
                'error_type': "KeyExpired",
                'error_msg': "請求之金鑰已經失效",
                'error_code': 400
            }
        # Case 7: 金鑰已經被使用
        elif result[0]['status'] == 1:
            result = {
                'error_type': "KeyHasBeenUsed",
                'error_msg': "請求之金鑰已經被使用",
                'error_code': 400
            }
        # Case 8: 金鑰驗證成功
        else:
            # 將金鑰重新封裝
            rsa_key = KeyRSA(result[0]['public_key'], result[0]['private_key'],
                             result[0]['bits'])
            # 建立一個RSA物件回傳使用
            rsa = RSA(result[0]['id'], result[0]['usage'], result[0]['ip'],
                      rsa_key, result[0]['create'], result[0]['expire'],
                      result[0]['status'])

            return True, row, rsa

        return False, row, result
コード例 #7
0
ファイル: RSA.py プロジェクト: jason40418/iot_project
class RSA(metaclass=MultipleMeta):
    def __init__(self, rsa_id: str, usage: str, ip: str, rsa_key: KeyRSA,
                 create: datetime, expire: datetime, status: int):
        self.__rsa_id = rsa_id
        self.__usage = usage
        self.__ip = ip
        self.__rsa_key = rsa_key
        self.__create = create
        self.__expire = expire
        self.__status = status
        self.__cfg = Config()
        self.__dbmgr = DBMgr()

    def __init__(self, usage: str, ip: str, rsa_key: KeyRSA, expire: str):
        self.__usage = usage
        self.__ip = ip
        self.__rsa_key = rsa_key
        self.__create = datetime.now()
        self.__expire = self.__create + timedelta(seconds=int(expire))
        self.__status = 0
        self.__cfg = Config()
        self.__dbmgr = DBMgr()
        self.__rsa_id = self.__insert_to_db()

    def __gen_rsa_key_id(self):
        # 取得現在日期
        time = strftime("%Y%m%d%H%M%S", localtime())

        # 產生三位數隨機亂碼
        rand = random.randint(0, 999)
        # 不足位數前方補0
        rand = "%03d" % rand

        return (time + rand)

    def __insert_to_db(self):
        # 如果設定檔指定不留下金鑰歷史紀錄
        if not strtobool(
                self.__cfg.getValue('history_record', 'rsa_key_history')):
            sql = "DELETE FROM `iot`.`rsa` WHERE `ip` = %(ip)s AND `usage` = %(usage)s AND `bits` = %(bits)s"
            args = {
                'ip': self.__ip,
                'usage': self.__usage,
                'bits': self.__rsa_key.get_bits()
            }
            status, row, result = self.__dbmgr.delete(sql, args)
            # TODO: 檢查刪除有無異常(通常只會有連線異常)

        while 1:
            # 產生一組隨機號碼
            key_id = self.__gen_rsa_key_id()

            # 檢查是否有重複
            if not self.__check_duplicate(key_id):
                break

        self.__rsa_id = key_id
        column = self.__dbmgr.get_db_column('iot', 'rsa')
        sql = self.__dbmgr.insert_sql('rsa', column, True)
        args = self.get_parameter()
        result, row, error_msg = self.__dbmgr.insert(sql, args)

        return key_id

    def __check_duplicate(self, key_id):
        if (self.__dbmgr.conn()):
            try:
                with self.__dbmgr.cursor() as cursor:
                    sql = "SELECT * FROM `iot`.`rsa` WHERE `id` = %(id)s"
                    args = {'id': key_id}

                    num_of_rows = int(cursor.execute(sql, args))
                    result = cursor.fetchall()
                    self.__dbmgr.commit()

            except self.__dbmgr.mysql_error() as e:
                print('[@RSA] 【{}】{!r}'.format(e.args[0], e.args[1]))

        else:
            print("[@RSA] Fails to connect to MySQL Server!!")

        self.__dbmgr.close()

        return True if num_of_rows != 0 else False

    def get_parameter(self):
        return {
            'usage': self.__usage,
            'ip': self.__ip,
            'bits': self.__rsa_key.get_bits(),
            'public_key': self.__rsa_key.get_public_key(),
            'private_key': self.__rsa_key.get_private_key(),
            'create': self.__create,
            'expire': self.__expire,
            'status': 0,
            'id': self.__rsa_id
        }

    def __remove_all_expire_keys(self):
        sql = "DELETE FROM  `iot`.`rsa` WHERE `iot`.`expire` < '%(date_time)s';"
        args = {'date_time': datetime.datetime.now()}
        status, row, result = self.__dbmgr.delete(sql, args)

    def get_rsa_key_id(self):
        return self.__rsa_id
コード例 #8
0
class SensorHelper():
    dbmgr = DBMgr()
    SENSOR_LIST = SensorDataHelper.get_sensor_from_db()

    # TODO: 若資料庫出錯是否要印出或做其他處置
    @staticmethod
    def get_new_record_id(promotor='system'):
        sql = "INSERT INTO `iot`.`record`(`promotor`) VALUES(%(promotor)s)"
        args = {'promotor': promotor}
        while True:
            status, row, result_id = SensorHelper.dbmgr.insert(sql, args)
            if status:
                break
        return result_id

    @staticmethod
    def insert_sensor_data(data, record_id):
        sql = "INSERT INTO `iot`.`data`(`record_id`, `item`, `value`) VALUES(%(record_id)s, %(sensor)s, %(value)s)"
        args = list()
        # 進來的資料為{sensor: value}
        for k, v in data.items():
            args.append({'sensor': k, 'value': v, 'record_id': record_id})
        while True:
            status, row, result_id = SensorHelper.dbmgr.insert(sql,
                                                               args,
                                                               multiple=True)
            if status:
                break
            else:
                print(result_id)
        print("[@SensorHelper] Insert all this record data into db finish.")

    @staticmethod
    def update_fail_list(data_list, record_id):
        sql = "UPDATE `iot`.`record` SET `fail_list`=%(fail_list)s WHERE `record`.`id`=%(id)s"
        args = {
            'fail_list': SensorHelper.dbmgr.list_to_string(data_list),
            'id': record_id
        }
        while True:
            status, row, result = SensorHelper.dbmgr.update(sql, args)
            if status:
                break
        print("[@SensorHelper] Update fail sensor list finish.")

    @staticmethod
    def get_latest_data():
        sql = "SELECT * FROM `iot`.`record` ORDER BY `record`.`id` DESC LIMIT 1"
        args = {}

        while True:
            status, row, record = SensorHelper.dbmgr.query(sql,
                                                           args,
                                                           fetch='one')
            if status: break

        # 資料庫無任何資料
        if row == 0:
            return False, {}
        else:
            # 取得資料庫內所有該次監測的資料
            record_id = record['id']
            sql = "SELECT * FROM `iot`.`data` WHERE `data`.`record_id`=%(id)s"
            args = {'id': record_id}

            data_dict = dict()
            while True:
                status, row, data = SensorHelper.dbmgr.query(sql, args)
                if status: break

            for datum in data:
                data_dict.update({
                    datum['item']:
                    None if datum['value'] is None else round(
                        float(datum['value']), 1)
                })

            # 組回原本格式
            result = {
                'id': record_id,
                'datetime': record['datetime'].strftime('%Y-%m-%d %H:%M:%S'),
                'fail': SensorHelper.dbmgr.string_to_list(record['fail_list']),
                'data': data_dict
            }
            return True, result

    @staticmethod
    def get_sensor_his_data(sensor, day=30, is_resample=True, remain=-1):
        # 今天日期
        today = datetime.date.today()
        # 最多獲取資料天數
        limit_day = today - datetime.timedelta(day)
        # 取得符合監測記錄
        sql = "SELECT * FROM `iot`.`record` WHERE `record`.`datetime` >= %(date)s ORDER BY `record`.`id` ASC LIMIT 1"
        args = {'date': limit_day.strftime("%Y-%m-%d")}
        status, row, record = SensorHelper.dbmgr.query(sql, args, fetch='one')
        day_info = {
            'start': limit_day.strftime("%Y-%m-%d"),
            'end': today.strftime("%Y-%m-%d")
        }
        #
        if status and row != 0:
            sql = " SELECT  `record`.`datetime`, `data`.`value` \
                    FROM    `iot`.`record`, `iot`.`data` \
                    WHERE   `record`.`id` = `data`.`record_id` AND `data`.`record_id` >= %(record_id)s AND `data`.`item` = %(sensor)s"

            args = {'record_id': record['id'], 'sensor': sensor}
            status, row, data = SensorHelper.dbmgr.query(sql, args)

            # 成功取回至少一筆資料
            if status and row != 0:
                for datum in data:
                    try:
                        datum['value'] = round(float(datum['value']), 3)
                    except:
                        datum['value'] = None

                df, df_h = SensorHelper.data_convert_to_dataframe(
                    data, is_resample=is_resample)

                # 判斷最後df要保留多少長度資料
                if remain > 0 and len(df.index) >= remain:
                    df = df[0:remain]

                return True, df, df_h, row, day_info
            # 失敗或沒有取得資料
            else:
                return False, pd.DataFrame(), pd.DataFrame(), 0, day_info
        # 資料庫沒有任何紀錄
        else:
            return False, pd.DataFrame(), pd.DataFrame(), 0, day_info

    @staticmethod
    def data_convert_to_dataframe(data,
                                  resample='60Min',
                                  sort=False,
                                  is_resample=True):
        # TODO: 檢查參數是否型態與格式正確
        df = pd.DataFrame(data)
        df.set_index("datetime", inplace=True)
        df = df.dropna()

        # 新增統計高低數值欄位
        df['high'] = df['value']
        df['low'] = df['value']

        if is_resample:
            # 重新進行取樣
            df = df.resample(resample).agg({
                'high': 'max',
                'low': 'min',
                'value': 'mean'
            })
            df = df.asfreq(resample)

            if len(df.index) != 0:
                # 以小時進行合併
                s_low = df['low'].groupby(df['low'].index.hour).min()
                s_high = df['high'].groupby(df['high'].index.hour).max()
                s_mean = df['value'].groupby(df['value'].index.hour).mean()
                df_h = pd.concat([s_low, s_high, s_mean], axis=1)
            else:
                df_h = df
        else:
            if len(df.index) != 0:
                # 以分鐘進行合併
                s_low = df['low'].groupby(df['low'].index.minute).min()
                s_high = df['high'].groupby(df['high'].index.minute).max()
                s_mean = df['value'].groupby(df['value'].index.minute).mean()
                df_h = pd.concat([s_low, s_high, s_mean], axis=1)
            else:
                df_h = df

        # 取小數點至第二位,並且重設index
        df = df.round(2).reset_index()
        df['datetime'] = df['datetime'].apply(
            lambda x: x.strftime("%Y-%m-%d %H:%M:%S"))
        df = df.sort_values(by=["datetime"], ascending=[sort])
        df = df.dropna()

        df_h = df_h.round(2).reset_index()
        df_h = df_h.dropna()

        return df, df_h

    @staticmethod
    def get_statist_data(sensor, period, header):
        '''
        不能傳入年,要使用要用relative
        '''
        # TODO: 若傳入資料格式錯誤之異常處理(包含key值)
        time_idx = ['hours', 'minutes', 'seconds']
        result = list()
        for item in period:
            # 判斷是否要抓取到時間部分
            get_all = False
            if 'all' in item.keys():
                get_all = True
            elif list(set(time_idx) & set(item.keys())):
                today = datetime.datetime.now()
                limit_day = (
                    today -
                    datetime.timedelta(**item)).strftime("%Y-%m-%d %H:%M:%S")
                today = today.strftime("%Y-%m-%d %H:%M:%S")
            else:
                today = datetime.date.today()
                limit_day = (today -
                             datetime.timedelta(**item)).strftime("%Y-%m-%d")
                today = today.strftime("%Y-%m-%d")

            # 使用SQL指令取回資料庫筆數和平均數
            # 取得符合監測記錄
            if get_all:
                sql = "SELECT * FROM `iot`.`record` ORDER BY `record`.`id` ASC LIMIT 1"
            else:
                sql = "SELECT * FROM `iot`.`record` WHERE `record`.`datetime` >= %(date)s ORDER BY `record`.`id` ASC LIMIT 1"
            args = {'date': limit_day}
            status, row, record = SensorHelper.dbmgr.query(sql,
                                                           args,
                                                           fetch='one')
            # 將該筆之日期資訊儲存
            temp_result = {'start': limit_day, 'end': today}

            if status and row != 0:
                sql = " SELECT      SUM(`data`.`value`) as `sum`, COUNT(`data`.`value`) as `count`, SUM(`data`.`value`)/COUNT(`data`.`value`) as `avg` \
                        FROM        `iot`.`record`, `iot`.`data` \
                        WHERE       `record`.`id` = `data`.`record_id` AND `data`.`record_id` >= %(record_id)s AND `data`.`item` >= %(sensor)s\
                        GROUP BY    `data`.`item`"

                args = {'record_id': record['id'], 'sensor': sensor}
                status, row, data = SensorHelper.dbmgr.query(sql,
                                                             args,
                                                             fetch='one')
                print('row', row)

                # 成功取回至少一筆資料
                if status and row != 0:
                    temp_result.update({
                        'sum':
                        round(data['sum'], 2) if data['sum'] != None else 0.00,
                        'count':
                        int(data['count']),
                        'avg':
                        round(data['avg'], 2) if data['avg'] != None else 0.00
                    })
                # 失敗或沒有取得資料
                else:
                    temp_result.update({'sum': 0.00, 'count': 0, 'avg': 0.00})
            # 資料庫沒有任何紀錄
            else:
                temp_result.update({'sum': 0.00, 'count': 0, 'avg': 0.00})

            result.append(temp_result)

        return header, result
コード例 #9
0
class MemberPreferenceHelper():
    # TODO: 新增預設檔案之前可能需要先檢查是否存在
    dbmgr = DBMgr()

    DEFAULT_ITEM = [
        item for item in SensorHelper.SENSOR_LIST if item['default']
    ]

    @staticmethod
    def add_default_value(account):
        # TODO: 需要改成OO寫法
        sql = " INSERT INTO `iot`.`member_preference`(`account`, `item`, `min`, `max`) \
                VALUES(%(account)s, %(item)s, %(min)s, %(max)s)"

        args = list()

        for item in MemberPreferenceHelper.DEFAULT_ITEM:
            args.append({
                'account': account,
                'min': item['min'],
                'max': item['max'],
                'item': item['id']
            })

        while True:
            status, row, result_id = MemberPreferenceHelper.dbmgr.insert(
                sql, args, multiple=True)
            if status:
                break

    @staticmethod
    def get_by_account(account):
        sql = "SELECT * FROM `iot`.`member_preference` WHERE `member_preference`.`account` = %(account)s"
        args = {'account': account}

        item_list = list()

        while True:
            status, row, result = MemberPreferenceHelper.dbmgr.query(sql, args)
            if status: break

        for i in result:
            item_list.append(i['item'])

        return result, item_list

    @staticmethod
    def calc_avg_pref_value():
        """ Use for to calculate the averger value of preference in this room
        """
        # 取得目前在環境中的人員數量
        status, row, data = AccessHelper.get_all()

        people = [i['account'] for i in data] if status else list()
        result = dict()

        for item in MemberPreferenceHelper.DEFAULT_ITEM:
            if not status or row <= 0:
                result.update(
                    {item['id']: {
                         'min': item['min'],
                         'max': item['max'],
                     }})
            else:
                sql = " SELECT  * \
                        FROM    `iot`.`member_preference` \
                        WHERE   `member_preference`.`item`=%(item)s AND \
                                `member_preference`.`account` in %(people)s"

                args = {'item': item['id'], 'people': tuple(people)}

                while True:
                    status, row, data = MemberPreferenceHelper.dbmgr.query(
                        sql, args)
                    if status: break

                # 判斷是否有抓取到該項目偏好設定檔案
                if row == 0:
                    result.update({
                        item['id']: {
                            'min': item['min'],
                            'max': item['max'],
                        }
                    })
                else:
                    min_value = float(sum(d['min'] for d in data)) / len(data)
                    max_value = float(sum(d['max'] for d in data)) / len(data)
                    result.update({
                        item['id']: {
                            'min': round(min_value, 2),
                            'max': round(max_value, 2),
                        }
                    })

        return result, people, len(people)

    @staticmethod
    def edit(args):
        sql = " UPDATE  `iot`.`member_preference` \
                SET     `member_preference`.`min` = %(min)s, \
                        `member_preference`.`max` = %(max)s \
                WHERE   `member_preference`.`item` = %(item)s AND \
                        `member_preference`.`account` = %(account)s"

        while True:
            status, row, result = MemberPreferenceHelper.dbmgr.update(
                sql, args, multiple=True)
            if status: break

        return row