Skip to content

Commit

Permalink
Merge 03f3f6f into 0743d9a
Browse files Browse the repository at this point in the history
  • Loading branch information
itaa committed Jul 23, 2019
2 parents 0743d9a + 03f3f6f commit 095f8d8
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 13 deletions.
6 changes: 6 additions & 0 deletions README.rst
Expand Up @@ -123,6 +123,12 @@ fishbase 能干什么?
最近更新
==========

2019.7.17 v1.1.15
------------------

- 添加可选参数,定义日志文件格式 `#240 <https://github.com/chinapnr/fishbase/issues/240>`_
- 根据银行卡、身份证获取详细信息的方法 `#243 <https://github.com/chinapnr/fishbase/issues/243>`_

2019.6.25 v1.1.14
------------------

Expand Down
5 changes: 5 additions & 0 deletions docs/change_log.rst
@@ -1,5 +1,10 @@
更新记录
===========================
2019.7.17 v1.1.15
---------------------------
* `#240 <https://github.com/chinapnr/fishbase/issues/240>`_, logging, edit function :meth:`set_log_file`, optimize;
* `#243 <https://github.com/chinapnr/fishbase/issues/243>`_, data, add function :meth:`CardBin.get_card_detail`, :meth:`IdCard.get_number_detail` doc and unittest;

2019.6.25 v1.1.14
---------------------------
* `#238 <https://github.com/chinapnr/fishbase/issues/238>`_, common, edit function :meth:`RMBConversion.an2cn`, :meth:`RMBConversion.cn2an` optimize;
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Expand Up @@ -58,9 +58,9 @@
# built documents.
#
# The short X.Y version.
version = '1.1.13'
version = '1.1.15'
# The full version, including alpha/beta/rc tags.
release = '1.1.13'
release = '1.1.15'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
3 changes: 3 additions & 0 deletions docs/fish_data.rst
Expand Up @@ -6,11 +6,14 @@
fish_data.CardBin.check_bankcard
fish_data.CardBin.get_bank_info
fish_data.CardBin.get_cardbin_info
fish_data.CardBin.get_card_detail
fish_data.CardBin.get_bank_name_by_code
fish_data.IdCard.get_checkcode
fish_data.IdCard.check_number
fish_data.IdCard.get_zone_info
fish_data.IdCard.get_areanote_info
fish_data.IdCard.get_province_info
fish_data.IdCard.get_number_detail

.. automodule:: fish_data
:members:
2 changes: 1 addition & 1 deletion fishbase/__init__.py
Expand Up @@ -27,4 +27,4 @@
from .fish_project import *
from .fish_random import *

__version__ = '1.1.14' # type: str
__version__ = '1.1.15' # type: str
117 changes: 117 additions & 0 deletions fishbase/fish_data.py
Expand Up @@ -350,6 +350,43 @@ def get_province_info(cls):
{})
return values

# 2019.07.17 create by Hu Jun, add in v1.1.15, github issue #243
@classmethod
def get_number_detail(cls, id_num):
"""
根据身份证号获取性别、省份、出生年月日信息
:param:
* id_num: (string) 要查询的银行卡号
:returns:
* flag: (bool) 是否查询成功
* info: (dict) 性别信息等
举例如下::
from fishbase.fish_data import *
print('--- fish_data get_number_detail demo ---')
print(IdCard.get_id_num_detail('130522198407316471'))
print('---')
输出结果::
--- fish_data get_number_detail demo ---
(True, {'province': '130000', 'gender': '男', 'birth_date': '19840731'})
---
"""
if len(str(id_num)) != 18:
return False, {}
province = id_num[:2] + '0' * 4
gender = '男' if int(id_num[-2]) % 2 == 1 else '女'
birth_date = id_num[6:14]

return True, {'province': province,
'gender': gender,
'birth_date': birth_date}


# 2019.1.6 create by David Yi, #188 用 class CardBin 方法实现
class CardBin(object):
Expand Down Expand Up @@ -555,3 +592,83 @@ def get_cardbin_info(cls, bank, card_type):
{"bank": bank, "card_type": card_type})

return values

# 2019.07.17 create by Hu Jun, add in v1.1.15, github issue #243
@classmethod
@lru_cache()
def get_card_detail(cls, card_num):
"""
根据银行卡卡号,获取银行名称和银行卡类型
:param:
* card_num: (string) 银行卡号
:returns:
* flag: (bool) 是否查询成功的标识
* info: (dict) 银行名称和银行卡类型字典
举例如下::
from fishbase.fish_data import *
print('--- fish_data get_card_detail demo ---')
result = CardBin.get_card_detail('6212836989522229131')
print(result)
print('---')
输出结果::
--- fish_data get_card_detail demo ---
(True, {'bank_name': '中国银行', 'card_type': 'DC'})
---
"""
# 根据 card_bin 以及卡号长度查询 bank_code 和 card_type
# card_bin 的长度从 10 - 3
for bin_len in list(range(10, 2, -1)):
value = sqlite_query('fish_data.sqlite',
'select bankcode,cardtype from cn_cardbin where bin=:bin '
'and length=:length',
{"bin": card_num[:bin_len], "length": len(card_num)})
if value:
bank_name = cls.get_bank_name_by_code(value[0][0])
return True, {'bank_name': bank_name,
'card_type': value[0][-1]}
return False, {}

# 2019.07.17 create by Hu Jun, add in v1.1.15, github issue #243
@classmethod
@lru_cache()
def get_bank_name_by_code(cls, bank_code):
"""
根据银行代号,获取银行名称
:param:
* bank_code: (string) 银行代号
:returns:
* info: (dict) 银行名称和银行卡类型字典
举例如下::
from fishbase.fish_data import *
print('--- fish_data get_bank_name_by_code demo ---')
result = CardBin.get_bank_name_by_code('ABC')
print(result)
print('---')
输出结果::
--- fish_data get_bank_name_by_code demo ---
中国农业银行
---
"""
value = sqlite_query('fish_data.sqlite',
'select bankname from cn_bank where bankcode=:bank_code ',
{"bank_code": bank_code})
return value[0][0]
54 changes: 44 additions & 10 deletions fishbase/fish_logger.py
Expand Up @@ -26,17 +26,32 @@
# edit from https://www.jianshu.com/p/d615bf01e37b
class SafeFileHandler(FileHandler):

def __init__(self, filename, mode='a', encoding=None, delay=0):
def __init__(self, filename, mode='a', encoding=None, delay=0,
file_name_format='%project_name-%log-%date'):
"""
Use the specified filename for streamed logging
"""
if codecs is None:
encoding = None
FileHandler.__init__(self, filename, mode, encoding, delay)

# 日志文件路径
self.file_path = os.path.split(filename)[0]
# 日志文件名称
file_name = os.path.split(filename)[1]

temp_file_name = file_name.split('.')
if len(temp_file_name) == 1:
self.project_name = temp_file_name[0]
self.log_suffix = 'log'
else:
self.project_name, self.log_suffix = temp_file_name[0], temp_file_name[1]

self.mode = mode
self.encoding = encoding
self.suffix = "%Y-%m-%d"
self.suffix_time = ""
self.file_name_format = file_name_format

def emit(self, record):
"""
Expand All @@ -62,8 +77,11 @@ def check_base_filename(self, record):
"""
time_tuple = time.localtime()

if self.file_name_format:
pass

if self.suffix_time != time.strftime(self.suffix, time_tuple) or not os.path.exists(
self.baseFilename + '.' + self.suffix_time):
self._get_format_filename()):
return 1
else:
return 0
Expand All @@ -79,36 +97,46 @@ def build_base_filename(self):
self.stream = None

# remove old suffix
if self.suffix_time != "":
index = self.baseFilename.find("." + self.suffix_time)
if index == -1:
index = self.baseFilename.rfind(".")
self.baseFilename = self.baseFilename[:index]
# if self.suffix_time != "":
# index = self.baseFilename.find("." + self.suffix_time)
# if index == -1:
# index = self.baseFilename.rfind(".")
# self.baseFilename = self.baseFilename[:index]

# add new suffix
current_time_tuple = time.localtime()
self.suffix_time = time.strftime(self.suffix, current_time_tuple)
self.baseFilename = self.baseFilename + "." + self.suffix_time
self.baseFilename = self._get_format_filename()

self.mode = 'a'
if not self.delay:
self.stream = self._open()

def _get_format_filename(self):
split_list = self.file_name_format.split('-')
name_mapping = {'%log': self.log_suffix,
'%project_name': self.project_name,
'%date': self.suffix_time}
new_file_name = '.'.join([name_mapping.get(i) for i in split_list])
return os.path.join(self.file_path, new_file_name)


# 设置日志记录,按照每天一个文件,记录包括 info 以及以上级别的内容
# 输入: local_file 日志文件名
# 2018.2.6 edit by David Yi
# 2018.2.11 edit, log 相关代码优化简化; #11010
# 2018.2.13 edit, remove thread watch
# 2018.4.23 edit,#19023 增加 docstring
def set_log_file(local_file=None):
# 2019.7.16 v1.1.15 #240 edit by Hu Jun
def set_log_file(local_file=None, file_name_format='%project_name-%log-%date'):

"""
设置日志记录,按照每天一个文件,记录包括 info 以及以上级别的内容;
日志格式采取日志文件名直接加上日期,比如 fish_test.log.2018-05-27
:param:
* local_fie: (string) 日志文件名
* file_name_format: (string) 日志文件名格式
:return: 无
举例如下::
Expand Down Expand Up @@ -136,9 +164,15 @@ def set_log_file(local_file=None):
if local_file is not None:
default_log_file = local_file

support_split = ['%project_name', '%log', '%date']

file_format_split = file_name_format.split('-')
if set(file_format_split) != set(support_split):
raise ValueError('file_name_format error, please check and try again!')

# time rotating file handler
# _tfh = TimedRotatingFileHandler(default_log_file, when="midnight")
_tfh = SafeFileHandler(filename=default_log_file)
_tfh = SafeFileHandler(filename=default_log_file, file_name_format=file_name_format)
_tfh.setLevel(logging.INFO)
_tfh.setFormatter(_formatter)

Expand Down
30 changes: 30 additions & 0 deletions test/test_data.py
Expand Up @@ -153,3 +153,33 @@ def test_get_note_by_province(self):
def test_get_province_info(self):
values = IdCard.get_province_info()
assert len(values) > 0

# 2019.07.17 edit by Hu Jun
def test_get_card_detail(self):
values = CardBin.get_card_detail('6212836989522229131')
assert values[0]
assert values[1].get('bank_name') == '中国银行'
assert values[1].get('card_type') == 'DC'

# 2019.07.17 edit by Hu Jun
def test_get_card_detail_01(self):
values = CardBin.get_card_detail('123762515738129')
assert not values[0]

# 2019.07.17 edit by Hu Jun
def test_get_bank_name_by_code(self):
result = CardBin.get_bank_name_by_code('ABC')
assert result == '中国农业银行'

# 2019.07.17 edit by Hu Jun
def test_get_number_detail(self):
values = IdCard.get_number_detail('130522198407316471')
assert values[0]
assert values[1].get('province') == '130000'
assert values[1].get('gender') == '男'
assert values[1].get('birth_date') == '19840731'

# 2019.07.17 edit by Hu Jun
def test_get_number_detail_01(self):
values = IdCard.get_number_detail('130522198407316')
assert not values[0]
57 changes: 57 additions & 0 deletions test/test_logger.py
@@ -0,0 +1,57 @@
# coding=utf-8
# fish_logging.py 单元测试
# 2018.7.17 create by Hu Jun

import os
import time
import shutil
import pytest
from fishbase.fish_logger import set_log_file, logger as log
# 定义当前路径
current_path = os.path.dirname(os.path.abspath(__file__))


class TestFishLogging(object):
log_filename = ''
suffix = "%Y-%m-%d"
suffix_time = ''
log_path = ''

def setup_class(self):
# 定义配置文件名
self.log_path = os.path.join(current_path, 'log_path')
if not os.path.exists(self.log_path):
os.mkdir(self.log_path)

self.log_filename = os.path.join(self.log_path, 'unittest.log')

current_time_tuple = time.localtime()
self.suffix_time = time.strftime(self.suffix, current_time_tuple)

def teardown_class(self):
try:
# 关闭日志文件句柄
for h in log.handlers:
h.close()
shutil.rmtree(self.log_path)
except Exception as _:
pass

def test_format1(self):
set_log_file(self.log_filename)
log.info('test_format1')
assert 'unittest.log.{}'.format(self.suffix_time) in os.listdir(self.log_path)

def test_format2(self):
set_log_file(self.log_filename, file_name_format='%project_name-%date-%log')
log.info('test_format2')
assert 'unittest.{}.log'.format(self.suffix_time) in os.listdir(self.log_path)

def test_format3(self):
set_log_file(self.log_filename, file_name_format='%date-%project_name-%log')
log.info('test_format3')
assert 'unittest.log.{}'.format(self.suffix_time) in os.listdir(self.log_path)

def test_format4(self):
with pytest.raises(ValueError):
set_log_file(self.log_filename, file_name_format='%date-%project_name-%log1')

0 comments on commit 095f8d8

Please sign in to comment.