Advertisement
Guest User

Untitled

a guest
Mar 18th, 2019
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.29 KB | None | 0 0
  1. import requests as requester
  2. import os
  3. import xml.etree.ElementTree as et
  4. import base64
  5.  
  6. from Principal.configuracion_lectura import lectura,conf_path
  7. from Principal.validators import Validator
  8. from Principal.os_identifier import config_path, path_splitter
  9.  
  10.  
  11. # testing on dev
  12. # from dependencies.configuracion_lectura import lectura
  13. # from dependencies.validators import Validator
  14. # from dependencies.os_identifier import config_path,path_splitter
  15.  
  16. # conf_path = config_path()
  17.  
  18.  
  19. class DataHandler(object):
  20.  
  21. """
  22. this object is going to manage the data and validate the data
  23. based on the established parameters in the forms, also it will be able to concoct
  24. all values and to return a dictionary of dictionaries which it will held the data
  25. and this is the one to be modified and the post request is going to be generated
  26. periodically based on this.
  27. the different parameters required for the object are:
  28.  
  29. tables: a dictionary containing the following data:
  30. :key: names of the table in the database
  31. :value: the fields of the given database
  32. """
  33.  
  34. def __init__(self, tables, load_related_models=False):
  35. self.tables = tables
  36. self.load_related_models = load_related_models
  37. self.validator = Validator()
  38. self.general_stacker = {}
  39. self.tables_columns = {}
  40. self.tables_columns_config = {}
  41. self.set_configuration()
  42. self.validators = {'string': self.validator.check_str,
  43. 'float': self.validator.check_float,
  44. 'integer': self.validator.check_int,
  45. 'date': self.validator.check_date,
  46. 'datetime': self.validator.check_datetime,
  47. 'bool': self.validator.check_bool,
  48. 'email':self.validator.validate_email,
  49. 'alpha':self.validator.check_alphanumeric
  50. }
  51. self.get_all_data()
  52.  
  53. def set_configuration(self):
  54. """
  55. Sets all the required values in order to start the connection
  56. :return: None
  57. """
  58. configuration_reader = lectura()
  59.  
  60. self.main_url = configuration_reader.url
  61. self.user_id = configuration_reader.user_id
  62. self.user = configuration_reader.user
  63. self.password = configuration_reader.api_pass
  64. self.owner = configuration_reader.owner
  65. self.end_points = {
  66. 'base_server': self.main_url,
  67. 'login': "%s/site/login" % self.main_url,
  68. 'get_data': "%s/get/getData" % self.main_url,
  69. 'post_data': "%s/insert/insertData" % self.main_url,
  70. 'delete_data': "%s/delete/deleteData" % self.main_url,
  71. 'get_fields': '%s/site/getTableStructure' % self.main_url,
  72. }
  73. authorization = "%s:%s" % (self.owner, self.password)
  74. self.headers = {"Content-Type": "text/xml", "Authorization": base64.b64encode(bytes(authorization,
  75. encoding='utf-8'))}
  76. connected = self.verify_connection()
  77. if not connected:
  78. print(connected)
  79. raise ConnectionError('the connection you\'re trying to establish is not available')
  80. for table in self.tables:
  81. conf_data = self.get_tables_fields(table)
  82. if isinstance(conf_data, list):
  83. self.tables_columns[table] = [column['name'] for column in conf_data if not isinstance(conf_data,str)]
  84. self.tables_columns_config[table] = conf_data
  85. else:
  86. raise ValueError('there was an error with the tables you\'re trying to read:%s' % conf_data)
  87. else:
  88. check_missing_models = [(model, error) for model, error in self.tables_columns.items()
  89. if isinstance(error, str) or error is None]
  90. if len(check_missing_models) > 0:
  91. raise AttributeError("Please verify the given models: %s" % check_missing_models.__str__()) # this will be displayed for the moment.
  92.  
  93. def get_tables_fields(self, table):
  94. """
  95. Gets the fields and data type of the tables provided
  96. :return: list of dictionaries
  97. """
  98. if not self.verify_connection():
  99. return None
  100. url = self.end_points['get_fields']
  101. returning_fields = []
  102. if self.user_id is None or self.user_id == '':
  103. raise AttributeError('The user doesn\'t exist') # this will be displayed for the moment.
  104. pass_encode = base64.b64encode(self.password.encode('ascii'))
  105. password = ""
  106. for x in pass_encode:
  107. password = password + chr(x)
  108.  
  109. servicio = et.Element("serviceName_req", name="get")
  110. estructura = et.SubElement(servicio, "ws_structure")
  111. parametros = et.SubElement(estructura, "params")
  112.  
  113. mod = et.SubElement(parametros, "model").text = table
  114. atr = et.SubElement(parametros, "attributes")
  115. s_atr = et.SubElement(atr, "attribute").text = ""
  116. rel = et.SubElement(parametros, "relations").text = 'false'
  117.  
  118. autorizacion = et.SubElement(servicio, "authorization")
  119. et.SubElement(autorizacion, "owner_code").text = self.owner
  120. et.SubElement(autorizacion, "id_user").text = self.user_id
  121. et.SubElement(autorizacion, "language").text = "es"
  122.  
  123. tree = et.ElementTree(servicio)
  124. tree.write(conf_path+"table_fields.xml", xml_declaration=True, encoding='utf-8', method="xml")
  125.  
  126. f = open(conf_path+'table_fields.xml', 'r')
  127. xml_str = f.read()
  128. f.close()
  129. response = requester.post(url, data=xml_str, headers=self.headers)
  130. # print (response.status_code)
  131. with open(conf_path+"response_table_fields.xml", "wt") as s:
  132. s.write(response.content.decode())
  133. s.close()
  134.  
  135. parser = et.XMLParser(encoding="ISO-8859-1")
  136. tree_resp = et.parse(conf_path+"response_table_fields.xml", parser=parser)
  137. root_resp = tree_resp.getroot()
  138. status = root_resp[1].text
  139. if status == 'true':
  140. field_count = len([r.tag for x in root_resp[3] for r in x if r.tag == "label"])
  141. fields = self.data_formatter([(r.tag, r.text) for x in root_resp[3] for r in x], 'label', field_count)
  142. for record in fields:
  143. field = {}
  144. # key = [record.pop(record.index(x)) for x in record if 'name' in x][1]
  145. for values in record:
  146. field[values[0]] = values[1]
  147. else:
  148. returning_fields.append(field)
  149. else:
  150. # final_field_count = len(returning_fields)
  151. # if final_field_count != field_count:
  152. # print(field_count, final_field_count, table, returning_fields)
  153. # exit()
  154. return returning_fields
  155.  
  156. else:
  157. return "the model doesn't exists or authentication failed."
  158.  
  159. def validate_user_and_get_data(self, table):
  160. """
  161. makes the connection with the server and validates the user, also gets the data from the server
  162. :param table:
  163. :return:
  164. """
  165. user_id = self.user_id
  166. pass_encode = base64.b64encode(self.password.encode('ascii'))
  167.  
  168. password = ""
  169. for x in pass_encode:
  170. password = password + chr(x)
  171.  
  172. servicio = et.Element("serviceName_req", name="get")
  173. estructura = et.SubElement(servicio, "ws_structure")
  174. parametros = et.SubElement(estructura, "params")
  175.  
  176. mod = et.SubElement(parametros, "model").text = table
  177. atr = et.SubElement(parametros, "attributes")
  178. s_atr = et.SubElement(atr, "attribute").text = ""
  179. rel = et.SubElement(parametros, "relations").text = self.load_related_models
  180.  
  181. autorizacion = et.SubElement(servicio, "authorization")
  182. et.SubElement(autorizacion, "owner_code").text = self.owner
  183. # et.SubElement(autorizacion, "id_user").text = "1"
  184. et.SubElement(autorizacion, "id_user").text = user_id
  185. et.SubElement(autorizacion, "language").text = "es"
  186.  
  187. tree = et.ElementTree(servicio)
  188. tree.write(conf_path+"ServiceGetBasic.xml", xml_declaration=True, encoding='utf-8', method="xml")
  189.  
  190. f = open(conf_path+'ServiceGetBasic.xml', 'r')
  191. xml_str = f.read()
  192. f.close()
  193. response = requester.post(self.end_points['get_data'], data=xml_str, headers=self.headers)
  194. # print (response.status_code)
  195. with open(conf_path+"responseGetBasic.xml", "wt") as s:
  196. s.write(response.content.decode())
  197. s.close()
  198.  
  199. parser = et.XMLParser(encoding="ISO-8859-1")
  200. tree_resp = et.parse(conf_path+"responseGetBasic.xml", parser=parser)
  201. root_resp = tree_resp.getroot()
  202.  
  203. if len(root_resp) != 4:
  204. return (False, "")
  205. else:
  206. transaccion = root_resp[0]
  207. estatus = root_resp[1]
  208. mensaje = root_resp[2]
  209. if estatus.text == 'true':
  210. return True, mensaje.text
  211. else:
  212. if mensaje.text == None:
  213. mensaje.text = "ERROR de autenticacion"
  214. return False, mensaje.text
  215.  
  216. def verify_connection(self):
  217. """
  218. checks if the connection to the database server is alive
  219. :return: bool
  220. """
  221. url = self.end_points['base_server']
  222. self.active = False
  223. try:
  224. response = requester.get(url, headers=self.headers, timeout=10)
  225. if response.status_code == 200:
  226. self.active = True
  227. except Exception as e:
  228. pass
  229. finally:
  230. return self.active
  231.  
  232. def get_all_data(self):
  233. """
  234. gets all the data from the database and loads it in the dictionary
  235. in the same order for all the existent tables, in case of an error with one
  236. of the databases it will return the error in the place of the data
  237. :return: dict
  238. """
  239. if self.verify_connection():
  240. for table in self.tables:
  241. data = self.get_data(table)
  242. self.general_stacker[table] = data
  243. else:
  244. raise ConnectionError('The server is not connected, please try again.')
  245.  
  246. def get_data(self, table):
  247. """
  248. Gets the data from the given table
  249. :param table: table name to get the data from.
  250. :return: dict.
  251.  
  252. """
  253. # getter = xml_get()
  254. status, data = self.validate_user_and_get_data(table)
  255. if status and 'no arrojo' not in data:
  256. # print(data,table)
  257. finished_data = self.read_xml(table)
  258. return finished_data
  259. else:
  260. return status, data
  261.  
  262. def read_xml(self, table):
  263. """
  264. reads the xml file and give back all the data in a list of dictionaries
  265. :param table:
  266. :return:
  267. """
  268. parsed_data = []
  269. parser = et.XMLParser(encoding="ISO-8859-1")
  270. tree_resp = et.parse(conf_path+"responseGetBasic.xml", parser=parser)
  271. global root_resp
  272. root_resp = tree_resp.getroot()
  273.  
  274. record_amount = int(root_resp[3][0].text)
  275. records = self.data_formatter([(r.tag,r.text) for x in root_resp[3][1:] for r in x],
  276. splitter_label='representing_column',num_records=record_amount)
  277. columns = [x[0] for x in records[0]]
  278. equality = self.equality_evaluator(table,gathered_columns=columns)
  279. if not equality[0]:
  280. raise AttributeError("La tabla %s tiene el error: %s" % (table, equality[1]))
  281.  
  282. for row in records:
  283. register = {}
  284. columns = [x[0] for x in row]
  285. keys = [x for x in self.tables_columns[table]]
  286. for index in range(len(columns)):
  287. register[columns[index]] = row[index][1]
  288. else:
  289. parsed_data.append(register)
  290. else:
  291. return parsed_data
  292.  
  293. def equality_evaluator(self, table, gathered_columns):
  294. """
  295. evaluates the equality of the columns
  296. :param table:
  297. :param gathered_columns:
  298. :return:
  299. """
  300. equality = False
  301. keys = [x for x in self.tables_columns[table]]
  302. for key in keys:
  303. if key not in gathered_columns:
  304. return equality, "the column %s doesn't exist in the database." % key
  305. else:
  306. if len(keys) != len(gathered_columns):
  307. return equality, "the provided table %s doesn't have the same number of columns in the " \
  308. "database."
  309. return True, "No errors"
  310.  
  311. def data_formatter(self, data, splitter_label, num_records=0):
  312. """
  313. rearrange the given data so it could be processed better
  314. :param data: non-formatted data
  315. :param splitter_label: label to use as reference to split the values in
  316. :param num_records: number of identified rows
  317. :return: list of lists
  318. """
  319. s = 'Splitter'
  320. formatted_data = []
  321. for val_index in range(len(data)):
  322. if splitter_label in data[val_index]:
  323. data[val_index] = s, val_index
  324. # print(data)
  325. if s in data[0] and splitter_label != "label":
  326. del data[0]
  327.  
  328. splitting_indexes = [val[1] for val in data if s in val and 0 not in val]
  329. last_index = 0
  330. for index in splitting_indexes:
  331. formatted_data.append(data[last_index:index])
  332. last_index = index
  333. else:
  334. if num_records > 0 and num_records != len(formatted_data):
  335. formatted_data.append(data[last_index:])
  336. for item in range(len(formatted_data)):
  337. formatted_data[item] = [x for x in formatted_data[item] if s not in x]
  338. else:
  339. if num_records == len(formatted_data):
  340. return formatted_data
  341. return None
  342.  
  343. def post_record(self, table, record, operation, mode='insert'):
  344. """
  345. takes a record which belongs to the given table and inserts is in the remote
  346. database
  347. :param table: name of the table in the main dictionary
  348. :param record: data to be inserted into table
  349. :return: list of dictionaries
  350. """
  351. inserter = xml_post()
  352. validated = self.validate_table_data(table, data=record)
  353.  
  354. if len(validated) > 1 and validated[0]:
  355. # local operations
  356. if mode == 'update':
  357. updated,errors = self.update_table(table,record)
  358. if not updated:
  359. return errors
  360. elif mode == 'delete':
  361. deleted, errors = self.remove_record_table(table,record)
  362.  
  363. if not deleted:
  364. return errors
  365. else:
  366. record_local = record.copy()
  367. if 'id' not in record.keys():
  368. record_local['id'] = self.get_id(table)
  369. added, errors = self.add_record_table(table, record_local)
  370. if not added:
  371. return errors
  372.  
  373. # remote operations
  374. record['modelo'] = table
  375. record = [record] if not isinstance(record,list) else record
  376. status, msg = inserter.valida_credencial(usuario=self.user, id_usuario=self.user_id,
  377. listaRecords=record, service=operation)
  378. if not status:
  379. return msg
  380. return True
  381. return validated[1]
  382.  
  383. def get_id(self, table):
  384. """
  385. genera un nuevo ID para la ruta
  386. :return: str
  387. """
  388. new_id = "1"
  389. print(self.general_stacker[table])
  390. if self.general_stacker[table] == [] or isinstance(self.general_stacker[table], tuple):
  391. return new_id
  392. # print(self.db_object.db[self.model][-1],self.db_object.db[self.model][-1].keys(),
  393. # 'id' in self.db_object.db[self.model][-1].keys())
  394. ids = [int(obj['id']) for obj in self.general_stacker[table][:]]
  395. new_id = str(max(ids) + 1)
  396. return new_id
  397.  
  398. def add_record_table(self, table, record):
  399. """
  400. adds the record locally to the table in order
  401. :param table: table to append the value
  402. :param record: record which it should be unique with the id
  403. :return:tuple: bool, error_msg
  404. """
  405. result = False
  406. error_msg = None
  407. id_exists = [True for exist in self.general_stacker[table][:] if isinstance(exist,dict) and
  408. exist['id'] == record['id']]
  409. if id_exists == [] :
  410. result = True
  411. if isinstance(self.general_stacker[table],tuple):
  412. self.general_stacker[table] = []
  413. self.general_stacker[table].append(record)
  414.  
  415. else:
  416. error_msg = "the record you're trying to append already exists in the database"
  417. return result, error_msg
  418.  
  419. def update_table(self, table, record):
  420. """
  421. updates the given record if found locally
  422. :param table: table
  423. :param record: dictionary, record to update
  424. :return: tuple bool and errors
  425. """
  426. result = False
  427. error = None
  428. rows_to_update = self.lookup_records(table, id=record['id'], mode='single')
  429. if rows_to_update != []:
  430. self.general_stacker[table][rows_to_update[1]] = record
  431. # print(self.general_stacker[table],'\n')
  432. result = True
  433. else:
  434. error = "Error. No record was found with the specified value."
  435. return result, error
  436.  
  437. def remove_record_table(self, table, record):
  438. """
  439. deletes the given record if found locally
  440. :param table: table
  441. :param record: dictionary, record to update
  442. :return: tuple bool and errors
  443. """
  444. result = False
  445. error = None
  446. rows_to_delete = self.lookup_records(table, id=record['id'], mode='single')
  447. if rows_to_delete != []:
  448. self.general_stacker[table].pop(rows_to_delete[1])
  449. result = True
  450. else:
  451. error = "Error. No record was found with the specified value."
  452. return result, error
  453.  
  454. def lookup_records(self, table, **kwargs):
  455. """
  456. gets the records in the general_stacker that matches with the given conditions
  457. for now it's going to work with just the ID
  458. :param table: string
  459. :param kwargs: dict
  460. :return: list of tuples
  461. """
  462. table_columns = self.tables_columns[table]
  463. look_up_filters = [key for key in kwargs.keys() if key in table_columns]
  464. results = [(dicty, self.general_stacker[table].index(dicty)) for dicty in self.general_stacker[table]
  465. for lookup in look_up_filters
  466. if dicty[lookup] == kwargs[lookup]]
  467.  
  468. if 'mode' in kwargs.keys() and kwargs['mode'] == 'single':
  469. return results[0] if results != [] else []
  470. return results
  471.  
  472. def validate_table_data(self, table, data=None):
  473. """
  474. validates the data in the given specified table is correct as required
  475. in case of any error, it will return the error
  476. the correctness will be validated based on the field the table takes. for example:
  477. if there's a data missing which is required, it will return the error or if the data structure is not right
  478. :return: tuple
  479. """
  480. if data is None:
  481. return "ERROR. No Data was passed. Try again"
  482. error_msgs = []
  483. validated = False
  484. # accepted_dtypes_column = [(dtype['name'], dtype['type']) for dtype in self.tables_columns_config[table]]
  485. passed_columns = [key for key in data.keys()]
  486. full_validation = []
  487.  
  488. for key in passed_columns:
  489. if key not in self.tables_columns[table]:
  490. return "ERROR, The following doesn't exist in the predefined columns %s" % key
  491. dtype = self.get_field_dtype(key,table)
  492. data_validated = self.validate_data(data_type=dtype, data=data[key])
  493. full_validation.append([key, data[key], data_validated])
  494. else:
  495. # print(full_validation)
  496. val = [result[2][0] for result in full_validation]
  497. # print(val,full_validation,self.tables_columns_config[table])
  498. if False in val and val.count(False) >= 1:
  499. bad_items = [item for item in full_validation if not item[2][0]]
  500. for item in bad_items:
  501. error_msgs.append("Error. The data %s is not appropriated to the field %s because %s" %
  502. (item[1], item[0], item[2][1]))
  503. else:
  504. validated = True
  505. return validated, error_msgs
  506.  
  507. def get_field_dtype(self, field, table):
  508. """
  509. gets the data type of a given field
  510. :param field:
  511. :return: str: data type of the field.
  512. """
  513. dtype = None
  514. for dicty in self.tables_columns_config[table]:
  515. if dicty['name'] == field:
  516. dtype = dicty['type']
  517. break
  518. return dtype
  519.  
  520. def validate_data(self, data_type, data):
  521. """
  522. validates the data with the existing supported data types
  523. verify the module validators for more information.
  524. :param data_type: str: name of the data type
  525. :param data: obj to be evaluated
  526. :return: bool
  527. """
  528. if data_type is None:
  529. return False
  530. validation_result = self.validators[data_type](value=data)
  531. if not validation_result[0] and data_type =='string':
  532. validation_result = self.validators['email'](data)
  533. if not validation_result[0]:
  534. validation_result = self.validators['alpha'](data)
  535. return validation_result
  536.  
  537.  
  538. class xml_post():
  539. # I need modify valida_Credencial method for work with several services
  540. def valida_credencial(self, usuario, id_usuario, listaRecords, service='insert'):
  541.  
  542. # variables de conexion tomadas del json de configuracion
  543. # id de usuario harcodeado hasta que funcione API
  544. cfg = lectura()
  545. g_dominio = cfg.url
  546. g_owner = cfg.owner
  547. g_api_pass = cfg.api_pass
  548.  
  549. servicio = et.Element('serviceName_req', name=service)
  550. estructura = et.SubElement(servicio, 'ws_structure')
  551. parametros = et.SubElement(estructura, 'params')
  552.  
  553. for records in listaRecords:
  554. for k, v in records.items():
  555. if k == 'modelo':
  556. mod = et.SubElement(parametros, 'model', name=v)
  557. atr = et.SubElement(mod, 'attributes')
  558.  
  559. for records in listaRecords:
  560. for k, v in records.items():
  561. if k != 'modelo':
  562. if k == 'id' and v == 0:
  563. s_atr = et.SubElement(atr, k).text = None
  564. else:
  565. s_atr = et.SubElement(atr, k).text = v
  566.  
  567. autorizacion = et.SubElement(servicio, 'authorization')
  568. et.SubElement(autorizacion, 'owner_code').text = g_owner
  569. et.SubElement(autorizacion, 'id_user').text = id_usuario
  570. et.SubElement(autorizacion, 'language').text = 'es'
  571.  
  572. tree = et.ElementTree(servicio)
  573. tree.write(conf_path+'ServicePostBasic.xml', xml_declaration=True, encoding='utf-8',
  574. method='xml')
  575.  
  576. f = open(conf_path + 'ServicePostBasic.xml', 'r')
  577. xml_str = f.read()
  578. f.close()
  579.  
  580. api = g_owner + ':' + g_api_pass
  581. cabeceras = {'Content-Type': 'text/xml', 'Authorization': base64.b64encode(api.encode())}
  582. url = g_dominio + '/insert/insertData' if service == 'insert' else g_dominio + '/delete/deleteData'
  583.  
  584. response = requester.post(url, data=xml_str, headers=cabeceras)
  585. # print (response.status_code)
  586. with open(conf_path + 'ResponsePostBasic.xml', 'wt') as s:
  587. s.write(response.content.decode())
  588. s.close()
  589.  
  590. parser = et.XMLParser(encoding='ISO-8859-1') # a evaluar, por codificacion.
  591. tree_resp = et.parse(conf_path + 'ResponsePostBasic.xml', parser=parser)
  592. root_resp = tree_resp.getroot()
  593.  
  594. if len(root_resp) != 4:
  595. return (False, '')
  596. else:
  597. transaccion = root_resp[0]
  598. estatus = root_resp[1]
  599. mensaje = root_resp[2]
  600. if estatus.text == 'true':
  601. return (True, mensaje.text)
  602. else:
  603. if mensaje.text == None:
  604. mensaje.text = 'ERROR de autenticacion'
  605. return (False, mensaje.text)
  606.  
  607.  
  608. def main():
  609. # api_tester = DataHandler(owner="materer", user='systemcore', user_password="Mate1275",
  610. # database="MATERER",database_password="MATERER2017",
  611. # tables=["articulos_productos",
  612. # "articulos_configuracion",
  613. # 'articulos',
  614. # 'articulos_tipo',
  615. # 'articulos_categorias',
  616. # 'alicuotas',
  617. # 'monedas',
  618. # 'unidades',
  619. # 'configuracion_articulos',
  620. # ])
  621. api_tester = DataHandler(tables=[
  622. 'empresa_contable','ejercicios_contables'
  623.  
  624. ])
  625. print(api_tester.general_stacker)
  626.  
  627. if __name__ == '__main__':
  628. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement