jtt-utils 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.1
2
+ Name: jtt-utils
3
+ Version: 1.0.0
4
+ Summary: jtt common tools
5
+ Home-page: UNKNOWN
6
+ Author: candyabc
7
+ Author-email: hfcandyabc@163.com
8
+ License: maxwin
9
+ Platform: any
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: BSD License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Programming Language :: Python :: 3.5
18
+ Classifier: Programming Language :: Python :: 3.6
19
+
20
+ jtt common tools
21
+
@@ -0,0 +1,72 @@
1
+ ## D:\work\mwwork\JTT\jtt-tm-util
2
+ #### jtt 常用工具
3
+ *
4
+
5
+ ### 设计
6
+
7
+
8
+ ### 设定
9
+ *
10
+
11
+ ###产生分发包
12
+ ``
13
+ python setup.py sdist
14
+ twine upload dist/*
15
+ ``
16
+ ### 运行
17
+ * python setup.py sdist build
18
+ * python setup.py install
19
+
20
+
21
+ ### 修改记录
22
+ * 2020/03/23
23
+ ```text
24
+ v 0.0.4
25
+ 修改consul reader 增加read_service 的key参数,向上兼容
26
+
27
+ ```
28
+ * 2020/05/27
29
+ ```text
30
+ v 0.0.5
31
+ 修改consul reader 增加service_as_url向上兼容
32
+
33
+ ```
34
+
35
+ * 2020/06/11
36
+ ```text
37
+ v 0.0.6
38
+
39
+ 修改sync_basedata 增加read employee
40
+ ```
41
+
42
+ * 2020/06/17
43
+ ```text
44
+ v 0.0.7
45
+
46
+ 修改sync_basedata 的bug
47
+ ```
48
+
49
+ * 2023/03/16
50
+ ```text
51
+ v 0.1.0
52
+
53
+ fix update line_config bug。
54
+ ```
55
+ * 2023/03/17
56
+ ```text
57
+ v 0.1.1
58
+
59
+ kong register add host param
60
+ ```
61
+ * 2023/04/20
62
+ ```text
63
+ v 0.1.2
64
+ sync basedata 同步支持 key_type='set' 方式
65
+ ```
66
+
67
+ * 2023/04/20
68
+ ```text
69
+ v 0.1.3
70
+ fix : consul read datebase driver 的错误
71
+ add read_dbdriver func
72
+ ```
@@ -0,0 +1,12 @@
1
+ __version__ ='1.0.0'
2
+ __author__ = 'candyabc'
3
+ __copyright__ = 'candyabc'
4
+ __email__ = 'hfcandyabc@163.com'
5
+ __license__ = 'maxwin'
6
+
7
+
8
+ from .sync_basedata import data_manager
9
+
10
+ from .consul_handle import consul_reader
11
+ from .kong import Kong
12
+ # __all__=("JttTaskManager","JttTask")
@@ -0,0 +1,245 @@
1
+ from consul import Consul
2
+
3
+ import sys
4
+
5
+ import collections
6
+
7
+ from .log import logger
8
+
9
+ CService =collections.namedtuple('CService','ip port agent_ip serviceid')
10
+ CRedisService = collections.namedtuple('CRedisService','ip port db')
11
+ CDatabaseService = collections.namedtuple('CDatabaseService','dbdriver ip port dbname user pwd')
12
+ # CMongoService = collections.namedtuple('CMongoService','host port username password authSource')
13
+ def cservice_to_address(service):
14
+ return '%s:%s' % (service.ip,service.port)
15
+
16
+ class ConsulAgent:
17
+ def __init__(self,node_name,ip,services=None):
18
+ self.node_name = node_name
19
+ self.ip = ip
20
+ self._services = services
21
+
22
+ def combine_address(self,port):
23
+ return '%s:%s' %(self.ip,port)
24
+
25
+ def combine_service_id(self,name,port):
26
+ return '%s:%s:%s' %(self.node_name,name,port)
27
+
28
+ class ConsulReader():
29
+ def __init__(self):
30
+ self._consul =None
31
+ self._agent =None
32
+
33
+ @property
34
+ def consul(self):
35
+ if self._consul is None:
36
+ self._consul =Consul()
37
+ return self._consul
38
+
39
+ @property
40
+ def agent(self):
41
+ if self._agent is None:
42
+ try:
43
+ agent_self = self.consul.agent.self()
44
+ self._agent = ConsulAgent(agent_self['Member'].get('Name'),
45
+ agent_self['Member'].get('Addr'),
46
+ self.consul.catalog.services()[1])
47
+
48
+ except Exception as e:
49
+ logger.error('读取consul 服务失败,error:%s' % (e))
50
+
51
+ sys.exit(-1)
52
+
53
+ return self._agent
54
+
55
+ def read_key(self,key,raise_error=False):
56
+ v =self.consul.kv.get(key)[1]
57
+ if v is None:
58
+ if raise_error:
59
+ raise Exception('consul: not has key %s' % key)
60
+ else:
61
+ sys.exit('consul: not has key %s' % key)
62
+ return v['Value'].decode()
63
+
64
+ def read_key_default(self,key,default):
65
+ try:
66
+ return self.read_key(key,True)
67
+ except:
68
+ return default
69
+
70
+ def put_key(self,key,value,**args):
71
+ self.consul.kv.put(key,value,**args)
72
+
73
+ def read_services(self,service_name,tag,raise_error=False,key =None,**args):
74
+ try:
75
+ srvs = self.consul.catalog.service(service_name, tag=tag,**args)[1]
76
+
77
+ if key is not None:
78
+ port = self.read_key('%s_%s_%s' %(service_name,tag,key),raise_error=raise_error)
79
+ else:
80
+ port =None
81
+ re =[]
82
+ for srv in srvs or []:
83
+ re.append(CService(ip=srv['ServiceAddress'],
84
+ port=port or srv['ServicePort'],
85
+ agent_ip=srv['Address'],
86
+ serviceid = srv['ServiceID']))
87
+ return re
88
+ except Exception as e:
89
+ logger.error('consul:%s,%s error:%s' % (service_name,tag,str(e)))
90
+ self._consul =None
91
+ if raise_error:
92
+ raise Exception('consul:%s,%s, error:%s' % (service_name,tag,str(e)))
93
+ else:
94
+ sys.exit('consul:%s,%s, error:%s' % (service_name,tag,str(e)))
95
+
96
+ def read_service(self,service_name,tag,raise_error=False,key =None,**args):
97
+ srvs = self.read_services(service_name,tag,raise_error,key,**args)
98
+ if len(srvs) <= 0:
99
+ logger.error('consul:%s,%s error:config is not exists' % (service_name,tag))
100
+ if raise_error:
101
+ raise Exception('consul:%s,%s error:config is not exists' % (service_name,tag))
102
+ else:
103
+ sys.exit('consul:%s,%s error:config is not exists' % (service_name,tag))
104
+ return srvs[0]
105
+
106
+ def read_service_as_address(self,service_name,tag,raise_error=False,key =None,**args):
107
+ srv = self.read_service(service_name,tag,raise_error,key,**args)
108
+ return cservice_to_address(srv)
109
+
110
+ def read_redis(self,tag,as_slave=False):
111
+ try:
112
+ if as_slave:
113
+ agent_self = self.consul.agent.self()
114
+ member = agent_self['Member']
115
+ node_name = member.get('NodeName')
116
+ srv = self.read_service('redis', tag, raise_error=True, near=node_name)
117
+ else:
118
+ srv = self.read_service('redis',tag,raise_error=True)
119
+
120
+ try:
121
+ db = self.read_key('redis_%s_db' % tag)
122
+ except:
123
+ db =0
124
+
125
+ return CRedisService(srv.ip,srv.port,db)
126
+
127
+ except Exception as e:
128
+ logger.error('consul:redis error:%s' % str(e))
129
+ sys.exit('consul:redis error:%s' % str(e))
130
+
131
+ def redis_as_url(self,tag,**args):
132
+ credis = self.read_redis(tag,**args)
133
+ return 'redis://%s:%s/%s' % (credis.ip,credis.port,credis.db)
134
+
135
+ def service_as_url(self,service_name,tag,raise_error=False,key =None,protocol ='http',**args):
136
+ service = self.read_service(service_name,tag,raise_error,key,**args)
137
+ return '%s://%s' % (protocol,cservice_to_address(service))
138
+
139
+ def kafka_servers(self,tag=None):
140
+ srvs =self.read_services('kafka',tag=tag)
141
+ if len(srvs)<=0:
142
+ logger.error('consul not set kafka')
143
+ sys.exit('consul not set kafka')
144
+ addresses = [cservice_to_address(srv) for srv in srvs]
145
+ return ','.join(addresses)
146
+
147
+ def read_dbdriver(self,dbname,**args):
148
+ return self.read_key('%s_dbdriver' % dbname,**args)
149
+
150
+ def read_database(self,dbname,dbapi =None):
151
+ dbsrv =self.read_service('database',dbname)
152
+ driver_key = self.read_key('%s_dbdriver' % dbname)
153
+ if dbapi ==None:
154
+ if driver_key =='mssql':
155
+ dbapi ='pymssql'
156
+ elif driver_key =='mysql':
157
+ dbapi ='pymysql'
158
+ else:
159
+ raise Exception(f'not set dbapi driver for {driver_key}')
160
+
161
+
162
+ # if dbapi is None:
163
+ # db_driver = 'mssql+pymssql' if driver_key=='mssql' else 'mysql+mysqldb'
164
+ # else:
165
+ db_driver = '%s+%s' %(driver_key,dbapi)
166
+
167
+ return CDatabaseService(db_driver,
168
+ dbsrv.ip,
169
+ dbsrv.port,
170
+ self.read_key('%s_db_name'% dbname),
171
+ self.read_key('%s_login_user' % dbname),
172
+ self.read_key('%s_login_pw' % dbname))
173
+
174
+ #dbdriver ip port dbname user pwd
175
+ def read_database_keys(self,dbname):
176
+ return {'dbdriver' : self.read_key('%s_dbdriver' % dbname),
177
+ 'dbname':self.read_key('%s_db_name' % dbname),
178
+ 'user': self.read_key('%s_login_user' % dbname),
179
+ 'pwd': self.read_key('%s_login_pw' % dbname)}
180
+
181
+ def database_as_url(self,dbname,**args):
182
+ dbsrv = self.read_database(dbname,**args)
183
+ return "{dbdriver}://{us}:{pw}@{host}:{port}/{name}".format(
184
+ dbdriver =dbsrv.dbdriver,us=dbsrv.user,pw=dbsrv.pwd,
185
+ host=dbsrv.ip,port=dbsrv.port,name=dbsrv.dbname)
186
+
187
+ def database_as_json(self,dbname):
188
+ dbsrv = self.read_database(dbname)
189
+ return {'host':dbsrv.ip,
190
+ 'port':dbsrv.port,
191
+ 'db':dbsrv.dbname,
192
+ 'user':dbsrv.user,
193
+ 'password':dbsrv.pwd}
194
+
195
+ def read_mongo(self,dbname):
196
+ dbsrv = self.read_service('mongo', dbname)
197
+ return {'host':dbsrv.ip,
198
+ 'port':dbsrv.port,
199
+ 'username':self.read_key('mongo_%s_user' % dbname),
200
+ 'password':self.read_key('mongo_%s_pw' % dbname),
201
+ 'authSource': dbname}
202
+
203
+ def mongo_as_url(self,dbname):
204
+ dbconfig = self.read_mongo(dbname)
205
+ return 'mongodb://{username}:{password}@{host}:{port}/{authSource}'.format(**dbconfig)
206
+
207
+ def register_service(self,name,port,host=None,tags =None,check=None,token=None,service_id=None):
208
+ if host is None:
209
+ host = self.agent.ip
210
+ try:
211
+ if service_id is None:
212
+ service_id = self.agent.combine_service_id( name, port )
213
+
214
+ ret= self.consul.agent.service.register(name, service_id=service_id, address=host,
215
+ port=port, tags=tags, check=check, token=token)
216
+ if ret:
217
+ logger.info('註冊%s(%s) 到consul成功!' % (name, service_id))
218
+ else:
219
+ logger.error('注册 %s(%s) 服务失败' % (name, service_id))
220
+ except Exception as e:
221
+ logger.error('注册 %s(%s) 服务失败,error:%s' % (name,service_id,str(e)))
222
+
223
+ sys.exit('注册 %s(%s)服务失败,error:%s' % (name,service_id, e))
224
+
225
+ def deregister_service(self,name,port,service_id=None):
226
+ try:
227
+ if service_id is None:
228
+ service_id=self.agent.combine_service_id( name, port )
229
+
230
+ ret= self.consul.agent.service.deregister(service_id)
231
+
232
+ if ret:
233
+ logger.info('删除%s(%s)成功!' % (name, service_id))
234
+ else:
235
+ logger.error('删除%s(%s)服务失败!' % (name, service_id))
236
+ except Exception as e:
237
+ logger.error('删除 %s(%s)服务失败,error:%s' % (name,service_id, str(e)))
238
+
239
+ sys.exit('删除 %s(%s)服务失败,error:%s' % (name,service_id, e))
240
+
241
+ def catalog_register(self):
242
+ self.consul.catalog.register()
243
+ consul_reader = ConsulReader()
244
+
245
+
@@ -0,0 +1,177 @@
1
+ from .consul_handle import consul_reader
2
+ from .log import logger
3
+ import requests
4
+ import sys
5
+
6
+
7
+ class Kong:
8
+ def __init__(self):
9
+
10
+ self.admin_uri = consul_reader.service_as_url('kong',tag='admin')
11
+ srv = consul_reader.read_service('kong',tag='kong')
12
+ self.ip = srv.ip
13
+ self.port = srv.port
14
+
15
+ def register_upstream_service(self,upstream_name,port, base_path,auth=None,host =None,**args):
16
+ if host is None:
17
+ address = consul_reader.agent.combine_address(port)
18
+ else:
19
+ address ='%s:%s' %(host,port)
20
+ self.register_upstream(upstream_name,address,**args)
21
+ self.register_service(upstream_name,base_path,auth)
22
+
23
+ def register_service(self,upstream_name,base_path, auth=None):
24
+ '''
25
+ :param kong_admin: 用于注册服务
26
+ :param service_host: 本地服务的host,用于设定upstream_url
27
+ :param service: 服务api,如:rightmanage/v1.0
28
+ :param kong_uris: 空的host uris,为空时值为service,如:rightmanage/v1.0
29
+ :param auth: 认证类型,可为str或list,如:'jwt',或:['jwt','key']
30
+ :param health: 如果service_host 是upstreams,则可以注册health
31
+ :return:
32
+ '''
33
+ ''''
34
+ # 注册 服务API
35
+ resp = requests.post('http://{kong}/apis/'.format(kong=KONG),
36
+ json={'name': 'rightmanage_v1.0',
37
+ 'uris': '/rightmanage/v1.0',
38
+ 'upstream_url': 'http://{server}/rightmanage/v1.0'.format(server=SERVER)})
39
+ print('注册 服务API', resp.text)
40
+
41
+ resp = requests.post('http://{kong}/apis/rightmanage_v1.0/plugins'.format(kong=KONG),
42
+ json={"name": "jwt"})
43
+ print('注册服务API JWT', resp.text)
44
+ '''
45
+ api_url = '{kong}/apis/'.format(kong=self.admin_uri)
46
+ # 如果kong_uris有值,则需要用它注册
47
+ api_name = base_path.replace('/','_').replace('.','_').strip('_')
48
+
49
+
50
+ upstream_url = 'http://{upstream_name}{base_path}'.format(upstream_name=upstream_name,
51
+ base_path=base_path)
52
+ try:
53
+ # 删除旧的api
54
+ resp = requests.delete('{kong}/apis/{api_name}'.format(kong=self.admin_uri,
55
+ api_name=api_name))
56
+ logger.info('del api(%s) %s,%s '% (api_name,resp.status_code,resp.text))
57
+ resp = requests.post(api_url,
58
+ json={'name': api_name,
59
+ 'uris': base_path,
60
+ 'upstream_url': upstream_url,
61
+ 'preserve_host': True})
62
+ logger.info('注册%s服务 to kong 成功,%s '% (base_path, resp.text))
63
+ except Exception as e:
64
+ logger.error('注册 %s服务失败,error:%s' % (base_path, e))
65
+
66
+ sys.exit(-1)
67
+ if auth:
68
+ if isinstance(auth, str):
69
+ auth =[auth]
70
+
71
+ if 'jwt' in auth:
72
+ auth_data = {"name": "jwt",
73
+ "config.uri_param_names": "jwt",
74
+ "config.claims_to_verify": "exp",
75
+ "config.key_claim_name": "iss",
76
+ "config.secret_is_base64": "false",
77
+ "config.cookie_names": "sessionid"
78
+ }
79
+
80
+ elif 'key' in auth:
81
+ auth_data = {"name": "key-auth",
82
+ "config.key_names": "apikey"
83
+ }
84
+
85
+
86
+ else:
87
+ raise Exception('不支持的auth(%s)' % auth)
88
+
89
+ resp = requests.post('{kong}/apis/{api_name}/plugins'.format(kong=self.admin_uri,
90
+ api_name=api_name),
91
+ json=auth_data)
92
+ if resp.status_code != 201:
93
+ # 在0.12.1之前的kong 不支持config.cookie_names
94
+ if 'jwt' in auth:
95
+ auth_data.pop('config.cookie_names')
96
+ resp = requests.post('{kong}/apis/{api_name}/plugins'.format(kong=self.admin_uri,
97
+ api_name=api_name),
98
+ json=auth_data)
99
+ if resp.status_code == 201:
100
+ logger.info('注册%s服务API %s 成功,%s' % (base_path,auth, resp.text))
101
+ else:
102
+ logger.error('注册%s服务API %s 失败,%s,%s' % (base_path,auth, resp.status_code, resp.text))
103
+
104
+ def register_upstream(self,upstream_name,service_address,health =None,weight=100):
105
+ '''
106
+
107
+ :param upstream_name: monitor-srv
108
+ :param service_host: ip:port
109
+ :param weight: 100
110
+ :param health: 如: ordermng/v1.0/health, realtime-srv/v1.0/health
111
+ :return:
112
+ '''
113
+ ''''
114
+ # create an upstream
115
+ $ curl -X POST http://kong:8001/upstreams \
116
+ --data "name=address.v1.service"
117
+
118
+ # add two targets to the upstream
119
+ $ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
120
+ --data "target=192.168.101.75:80"
121
+ --data "weight=100"
122
+ $ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
123
+ --data "target=192.168.100.76:80"
124
+ --data "weight=50"
125
+
126
+ # create a Service targeting the Blue upstream
127
+ $ curl -X POST http://kong:8001/apis/ \
128
+ --data "name=service-api" \
129
+ --data "uris=/aa"
130
+ --data "upstream_url=http://address.v1.service"
131
+ '''
132
+ upstream_api_url = '{kong}/upstreams/'.format(kong=self.admin_uri)
133
+ # add upstream
134
+ try:
135
+ '''
136
+ healthchecks.active.http_path - 在向目标发出HTTP GET请求时应该使用的路径。默认值是“/”。
137
+ healthchecks.active.timeout - 用于探测的HTTP GET请求的连接超时限制。默认值是1秒。
138
+ healthchecks.active.concurrency - 在主动健康检查中并发检查的目标数量。
139
+ 你还需要为运行的探针指定间隔的值:
140
+
141
+ healthchecks.active.healthy.interval - 健康目标的主动健康检查间隔时间(以秒为单位)。0的值表明不执行对健康目标的主动探测。
142
+ healthchecks.active.unhealthy.interval - 对不健康目标的主动健康检查间隔时间(以秒为单位)。0值表示不应该执行不健康目标的主动探测。
143
+ 这允许您调整主动健康检查的行为,无论您是否希望探测健康和不健康的目标在相同的时间间隔内运行,或者一个比另一个更频繁。
144
+
145
+ 最后,您需要配置Kong应该如何解释探头,通过设置健康计数器上的各种阈值,一旦到达,就会触发状态变化。计数器阈值字段是:
146
+
147
+ healthchecks.active.healthy.successes - 在主动探测中成功的数量(由healthchecks.active.healthy.http_statuses定义)来确认目标的健康
148
+ healthchecks.active.unhealthy.tcp_failures - 在主动探测中TCP故障的数量,以确认目标是不健康的。
149
+ healthchecks.active.unhealthy.timeouts - 在主动探测中超时的数量,以确认目标是不健康的。
150
+ healthchecks.active.unhealthy.http_failures - 在主动探测中出现的HTTP故障数量(由healthchecks.active.healthy.http_statuses定义)来确认目标是不健康的。
151
+ '''
152
+ upstream = {'name': upstream_name,
153
+ 'healthchecks.active.http_path': health or '/',
154
+ 'healthchecks.active.healthy.interval': 0 if health is None else 5,
155
+ 'healthchecks.active.healthy.successes': 1,
156
+ 'healthchecks.active.unhealthy.interval': 5,
157
+ 'healthchecks.active.unhealthy.tcp_failures': 1,
158
+ 'healthchecks.active.unhealthy.timeouts': 3,
159
+ 'healthchecks.active.unhealthy.http_failures': 1
160
+ }
161
+ resp = requests.post(upstream_api_url, json=upstream)
162
+ if resp.status_code == 409:
163
+ resp = requests.patch(upstream_api_url + upstream_name, json=upstream)
164
+ logger.info('add %s upstream to kong 成功,%s,%s' % (upstream_name,resp.status_code,resp.text))
165
+ except Exception as e:
166
+ logger.error('add %s upstream失败,error:%s' % (upstream_name, e))
167
+ sys.exit(-1)
168
+ targets_api_url = '{kong}/upstreams/{upstream_name}/targets'.format(kong=self.admin_uri,
169
+ upstream_name=upstream_name)
170
+ try:
171
+ resp = requests.post(targets_api_url,
172
+ json={'target': service_address, 'weight': weight})
173
+ logger.info('add %s target to kong 成功,%s,%s' % (upstream_name,resp.status_code,resp.text))
174
+ except Exception as e:
175
+ logger.error('add target for %s失败,error:%s' % (upstream_name, e))
176
+
177
+ sys.exit(-1)
@@ -0,0 +1,3 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger('jtt_util')
@@ -0,0 +1,266 @@
1
+ import asyncio
2
+ import json
3
+ from datetime import datetime
4
+ from collections import defaultdict
5
+ from .log import logger
6
+
7
+
8
+ DEFAULT_NAMES=[('vehicle','carid,dvrid1,fleetid,vtid'),
9
+ 'accredituser']
10
+
11
+ UPDATE_ASSOCIATIONS={'vehicle':['vehicle.','device.']}
12
+
13
+ NullObj ={}
14
+
15
+ def data_isnull(data):
16
+ return data ==NullObj
17
+
18
+ async def get_last_modifytime(rds_r):
19
+ # 有可能新旧同步程序同时运行,需取最新变更值
20
+ async def get_last_time(rds_key):
21
+ try:
22
+ last_mdy_time =await rds_r.hget(rds_key, 'lastmodifytime')
23
+ except Exception as e:
24
+ logger.error('redis error:%s'%str(e))
25
+ last_mdy_time = None
26
+ last_mdy_time = last_mdy_time.decode()\
27
+ if last_mdy_time is not None else '20010101010101010101'
28
+ return datetime.strptime(last_mdy_time, '%Y%m%d%H%M%S%f')
29
+
30
+ return await get_last_time('sync_record')
31
+
32
+ async def read_data_as_dict(rds,rds_key,fields=None):
33
+ if fields is None:
34
+ data = await rds.hgetall(rds_key)
35
+ if data:
36
+ return {k.decode(): v.decode() for k, v in data.items()}
37
+ else:
38
+ data = await rds.hmget(rds_key, *fields)
39
+ if data:
40
+ obj = {k: v.decode() for (k, v) in zip(fields, data) if v is not None}
41
+ return obj
42
+ return NullObj
43
+
44
+ class SyncBasedataManage():
45
+ def __init__(self):
46
+ self._tasks =[]
47
+ self._redis =None
48
+ self.mappings ={}
49
+ self.names =[]
50
+ self.cache =defaultdict(dict)
51
+ # self.sync_task =None
52
+
53
+ # self.clear_all()
54
+
55
+ def update_associations(self,key):
56
+ re =[key]
57
+ conditions = UPDATE_ASSOCIATIONS.get(key)
58
+ if conditions:
59
+ for condition in conditions:
60
+ re+=list(filter(lambda name:name.startswith(condition),self.names))
61
+
62
+ return re
63
+
64
+ def config(self,redis,names=DEFAULT_NAMES,loop=None):
65
+ def make_func(name):
66
+ def _method(self,key):
67
+ return self.get_bdobj(name,key )
68
+
69
+ return _method
70
+
71
+ self._redis =redis
72
+ for cname in names:
73
+ if type(cname)==tuple:
74
+ name, fields = cname
75
+ self.mappings[name]=fields.split(',')
76
+
77
+ else:
78
+ name =cname
79
+ self.names.append(name)
80
+ _method_name = 'get_%s' % name.replace('.', '_')
81
+ if not hasattr(self.__class__,_method_name):
82
+ _method = make_func(name)
83
+ setattr(self.__class__, _method_name, _method)
84
+ # self.names =names
85
+ if loop is not None:
86
+ self._tasks.append(loop.create_task(self.sync_data()))
87
+
88
+ @property
89
+ def rds(self):
90
+ assert self._redis is not None, '%s :please config redis first.' % self.__class__.__name__
91
+ return self._redis
92
+
93
+ def clear_all(self):
94
+ self.cache.clear()
95
+
96
+
97
+ async def sync_data(self):
98
+ last_modifytime =await get_last_modifytime(self.rds)
99
+ logger.info('sync_data get last modifytime:%s' % last_modifytime)
100
+ # last_modifytime =last_modifytime+timedelta(microseconds=-1)
101
+ # 避免频繁重启时,频繁访问redis
102
+ await asyncio.sleep(5)
103
+
104
+ while True:
105
+ try:
106
+ logger.debug('SyncBasedataManage.sync_data')
107
+ last_modifytime_local =await get_last_modifytime(self.rds)
108
+ if last_modifytime_local > last_modifytime:
109
+ updatedatas = await self.rds.zrangebyscore('bd_updatekeys',int(last_modifytime.timestamp()), float('inf'))
110
+ for ud in updatedatas:
111
+ ud_key = ud.decode().split('_{')[0] ##可能有'bdupd.line_config.%s_{"time": %s的情况,导致split到错误位置
112
+ if ud_key.startswith('bdupd.'):
113
+ [name,key] = ud_key.split('.')[-2:]
114
+ if name =='basedata' and key =='recreateall':
115
+ logger.info('ready recreatecall')
116
+ handled = self.custom_reload()
117
+ if not handled:
118
+ self.clear_all()
119
+
120
+ else:
121
+ if name =='line_config': # line.config的 update name 是line_config 所以要调整一下
122
+ name = 'line.config'
123
+ elif name =='vehiclegroup_vehicles':
124
+ name ='vehiclegroup.vehicles'
125
+ logger.info(f'notify update {name} {key}')
126
+ bd_names = self.update_associations(name)
127
+ for bd_name in bd_names:
128
+ handled =self.custom_reload_item(bd_name,key)
129
+ if not handled :
130
+ self.remove_bdobj(bd_name,key)
131
+
132
+ last_modifytime = last_modifytime_local
133
+ except Exception as e:
134
+ logger.error('sync_basedata error:%s' % str(e))
135
+ finally:
136
+ await asyncio.sleep(20)
137
+ def custom_reload(self):
138
+ return False
139
+
140
+ def custom_reload_item(self,name,key):
141
+ return False
142
+
143
+ async def get_bdobj(self,name,key,key_type='hash'):
144
+ assert name in self.names,'not set config %s' % name
145
+ key = str(key)
146
+ obj =(self.cache.get(name) or {}).get(key)
147
+ # obj = self.cache[name].get(key)
148
+ if obj is None:
149
+ logger.info(f"get bdobj from redis {name},{key}")
150
+ rds_key = 'bd:%s:%s' % (name, key)
151
+ fields =self.mappings.get(name)
152
+ obj =None
153
+ if key_type =='hash':
154
+ obj = await read_data_as_dict(self.rds,rds_key,fields)
155
+ elif key_type =='string':
156
+ data = await self.rds.get(rds_key)
157
+ if data is not None:
158
+ try:
159
+ obj = json.loads(data.decode())
160
+ except Exception as e:
161
+ logger.error('key:%s load error:%s' %(rds_key,str(e)))
162
+ elif key_type =='set':
163
+ data = await self.rds.smembers(rds_key)
164
+ if data is not None:
165
+ try:
166
+ obj =[item.decode() for item in data]
167
+ except Exception as e:
168
+ logger.error('key:%s load error:%s' % (rds_key, str(e)))
169
+ else:
170
+ logger.error('key:%s not support key_type:%s' %(rds_key,key_type))
171
+ if data_isnull(obj):
172
+ logger.warning('the %s(%s) is not exist!' % (name, key))
173
+ obj =None
174
+ # if self.cache.get(name) is None:
175
+ # self.cache[name] = {}
176
+ self.add_bdobj(name,key,obj)
177
+ return obj
178
+ return obj
179
+
180
+
181
+ def add_bdobj(self,name,key,obj):
182
+ self.cache[name][key] = obj
183
+
184
+ def remove_bdobj(self,name,key):
185
+ if self.cache.get(name):
186
+ try:
187
+ logger.info(f'remove {name} {key}')
188
+ self.cache[name].pop(key)
189
+ except:
190
+ pass
191
+
192
+
193
+ async def event_config(self,carid,evcode):
194
+ config = await self.get_vehicle_config(carid)
195
+ if config is not None:
196
+ data = config.get('event_params_%s' % evcode)
197
+ if data:
198
+ return json.loads(data)
199
+ return NullObj
200
+
201
+ async def get_e_stop(self,imsi):
202
+ return await self.get_bdobj('estop',imsi,'string')
203
+
204
+ async def get_vehicletype(self,id):
205
+ return await self.get_bdobj('vehicletype',id,'string')
206
+
207
+ class MapSyncManager(SyncBasedataManage):
208
+ def __init__(self,has_map_dvrid=False):
209
+ super().__init__()
210
+ self.has_map_dvrid =has_map_dvrid
211
+ self.dvrid_carid_map ={}
212
+ # self.has_dvrid = has_map_dvrid
213
+
214
+
215
+ def add_bdobj(self,name,key,obj):
216
+ super().add_bdobj(name,key,obj)
217
+ if self.has_map_dvrid and name =='vehicle' and obj is not None and obj.get('dvrid1') is not None:
218
+ self.dvrid_carid_map[obj['dvrid1']]=key
219
+
220
+ def remove_bdobj(self,name,key):
221
+ if self.has_map_dvrid and name == 'vehicle':
222
+ obj = self.cache[name].get(key)
223
+ if obj is not None and obj.get('dvrid1') is not None:
224
+ try:
225
+ self.dvrid_carid_map.pop(obj['dvrid1'])
226
+ except:
227
+ pass
228
+
229
+ super().remove_bdobj(name, key)
230
+
231
+ def clear_all(self):
232
+ super().clear_all()
233
+ self.dvrid_carid_map.clear()
234
+
235
+ def custom_reload(self):
236
+
237
+ vt_keys = self.cache['vehicle'].keys()
238
+
239
+ self.clear_all()
240
+
241
+ for key in vt_keys:
242
+ asyncio.ensure_future(self.get_bdobj('vehicle',key))
243
+
244
+ return True
245
+
246
+ def custom_reload_item(self,name,key):
247
+ # logger.info(f'custom_reload_item{name},{key}')
248
+ self.remove_bdobj(name,key)
249
+ if name =='vehicle' and self.has_map_dvrid:
250
+ asyncio.ensure_future(self.get_bdobj(name,key))
251
+ return True
252
+
253
+
254
+ # managers={'default':SyncBasedataManage(),
255
+ # 'map_dvrid1':MapSyncManager(True)}
256
+ # def get_manager():
257
+ # import os
258
+ # mode = os.getenv('SYNC_MANAGER','default')
259
+ # if mode =='default':
260
+ # return SyncBasedataManage()
261
+ # elif mode =='map_dvrid1':
262
+ # return MapSyncManager(True)
263
+
264
+ data_manager=MapSyncManager()
265
+
266
+
@@ -0,0 +1,23 @@
1
+ from datetime import datetime
2
+ def linux_timestamp():
3
+ return int(datetime.now().timestamp())
4
+
5
+ def timestamp_ms():
6
+ return datetime.now().timestamp()*1000
7
+
8
+ def time_to_microsec_str(d=None):
9
+ if d is None:
10
+ d = datetime.now()
11
+ return d.strftime('%Y%m%d%H%M%S%f')
12
+
13
+ def time_to_sql_str(d=None):
14
+ if d is None:
15
+ d = datetime.now()
16
+
17
+ return d.strftime('%Y-%m-%d %H:%M:%S')
18
+
19
+ def date_to_sql_str(d=None):
20
+ if d is None:
21
+ d = datetime.now()
22
+
23
+ return d.strftime('%Y-%m-%d')
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.1
2
+ Name: jtt-utils
3
+ Version: 1.0.0
4
+ Summary: jtt common tools
5
+ Home-page: UNKNOWN
6
+ Author: candyabc
7
+ Author-email: hfcandyabc@163.com
8
+ License: maxwin
9
+ Platform: any
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: BSD License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Programming Language :: Python :: 3.5
18
+ Classifier: Programming Language :: Python :: 3.6
19
+
20
+ jtt common tools
21
+
@@ -0,0 +1,14 @@
1
+ README.md
2
+ setup.cfg
3
+ setup.py
4
+ jtt_tm_utils/__init__.py
5
+ jtt_tm_utils/consul_handle.py
6
+ jtt_tm_utils/kong.py
7
+ jtt_tm_utils/log.py
8
+ jtt_tm_utils/sync_basedata.py
9
+ jtt_tm_utils/timeutil.py
10
+ jtt_utils.egg-info/PKG-INFO
11
+ jtt_utils.egg-info/SOURCES.txt
12
+ jtt_utils.egg-info/dependency_links.txt
13
+ jtt_utils.egg-info/not-zip-safe
14
+ jtt_utils.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ jtt_tm_utils
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import re
5
+ import os
6
+
7
+
8
+ from setuptools import setup, find_packages
9
+
10
+
11
+ def fpath(name):
12
+ return os.path.join(os.path.dirname(__file__), name)
13
+
14
+
15
+ def read(fname):
16
+ return open(fpath(fname)).read()
17
+
18
+
19
+ # def desc():
20
+ # info = read('README.rst')
21
+ # try:
22
+ # return info + '\n\n' + read('doc/changelog.rst')
23
+ # except IOError:
24
+ # return info
25
+
26
+ # grep flask_admin/__init__.py since python 3.x cannot import it before using 2to3
27
+ file_text = read(fpath('jtt_tm_utils/__init__.py'))
28
+
29
+
30
+ def grep(attrname):
31
+ pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
32
+ strval, = re.findall(pattern, file_text)
33
+ return strval
34
+
35
+
36
+ # entry_points = """
37
+ # [console_scripts]
38
+ # pycli = mw_uml_generator.cli:cli
39
+ # """
40
+ install_requires = [
41
+ ]
42
+
43
+ setup(
44
+ name='jtt-utils',
45
+ version=grep('__version__'),
46
+ url='',
47
+ license=grep('__license__'),
48
+ author=grep('__author__'),
49
+ author_email=grep('__email__'),
50
+ description='jtt common tools',
51
+ long_description='jtt common tools',
52
+ packages=find_packages(),
53
+ # entry_points=entry_points,
54
+ include_package_data=True,
55
+ zip_safe=False,
56
+ platforms='any',
57
+ install_requires=install_requires,
58
+
59
+ tests_require=[
60
+ ],
61
+ classifiers=[
62
+ 'Development Status :: 4 - Beta',
63
+ 'Environment :: Web Environment',
64
+ 'Intended Audience :: Developers',
65
+ 'License :: OSI Approved :: BSD License',
66
+ 'Operating System :: OS Independent',
67
+ 'Programming Language :: Python',
68
+ 'Topic :: Software Development :: Libraries :: Python Modules',
69
+ 'Programming Language :: Python :: 3.5',
70
+ 'Programming Language :: Python :: 3.6',
71
+ ]
72
+ )
73
+
74
+