bigdata-corp-api 0.16__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,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Murabei Data Science
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: bigdata-corp-api
3
+ Version: 0.16
4
+ Summary: Python API to facilitate interaction with BigDataCorp end-points
5
+ License: BSD-3-Clause License
6
+ License-File: LICENSE
7
+ Author: André Andrade Baceti
8
+ Author-email: a.baceti@murabei.com
9
+ Requires-Python: >=3.6
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Dist: requests
13
+ Description-Content-Type: text/markdown
14
+
15
+ # bigdata-corp-api
16
+ Python API to facilitate interaction with BigDataCorp end-points
17
+
@@ -0,0 +1,2 @@
1
+ # bigdata-corp-api
2
+ Python API to facilitate interaction with BigDataCorp end-points
@@ -0,0 +1,72 @@
1
+ [project]
2
+ name = "bigdata-corp-api"
3
+ version = "0.16"
4
+ description = "Python API to facilitate interaction with BigDataCorp end-points"
5
+ readme = "README.md"
6
+ requires-python = ">=3.6"
7
+ license = { text = "BSD-3-Clause License" }
8
+ authors = [
9
+ {name = "André Andrade Baceti", email = "a.baceti@murabei.com"}
10
+ ]
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "Operating System :: OS Independent",
14
+ ]
15
+ dependencies = [
16
+ "requests"
17
+ ]
18
+
19
+ [tool.poetry]
20
+ packages = [{include = "bigdatacorp_api", from = "src"}]
21
+
22
+ [build-system]
23
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
24
+ build-backend = "poetry.core.masonry.api"
25
+
26
+ [tool.ruff]
27
+ line-length = 79 # Quantidade de caracteres que o linter irá considerar na linha
28
+ indent-width = 4
29
+
30
+ # Pastas onde o linter não atuara. Por padrão ele só lê arquivos python e ignora tudo dentro do .gitignore
31
+ exclude = [
32
+ "secrets/",
33
+ "__init__.py",
34
+ "setup_template.py",
35
+ "setup.py"
36
+ ]
37
+
38
+ # Lista de linters que serão aplicados. o ruff da suporte a vários e eles podem ser adicionados aqui.
39
+ [tool.ruff.lint]
40
+ select = [
41
+ "F", # Pyflakes - Procura bugs básicos no codigo
42
+ "E", # pycodestyle - Pep8
43
+ "W", # pycodestyle - Pep8
44
+ "D", # pydocstyle - Docstring
45
+ "I", # Isort - Imports
46
+ "PD", # pandas-vet - linter de pandas (não deixa usar inplace nem df como nome de variavel)
47
+ "N", # pep8-naming - nomes seguindo pep8
48
+ "S", # flake8-bandit - linter de vulnerabilidade (nmao deixa usar assert)
49
+ ]
50
+
51
+ # Os códigos de erros colocados aqui são ignorados pelo linter. O erro abaixo é em relação ao assert. Está comentado por padrão.
52
+ ignore = [
53
+ # "S101" # Use of `assert` detected
54
+ "E402", "N806", "I001"
55
+ ]
56
+
57
+ # ativa um modo plus do ruff
58
+ preview = true
59
+
60
+ [tool.ruff.format]
61
+ indent-style = "space"
62
+
63
+ [tool.ruff.lint.pydocstyle]
64
+ convention = "google" # seleciona as docstrings do google como padrão
65
+
66
+ [tool.ruff.lint.isort]
67
+ no-sections = true
68
+
69
+ [dependency-groups]
70
+ docs = [
71
+ "mkdocs (>=1.6.1,<2.0.0)"
72
+ ]
File without changes
@@ -0,0 +1,734 @@
1
+ """BigDataCorp Python API."""
2
+ import os
3
+ import time
4
+ import json
5
+ import time
6
+ import datetime
7
+ import requests
8
+ from bigdatacorp_api.exceptions import (
9
+ BigDataCorpAPIException, BigDataCorpAPIInvalidDocumentException,
10
+ BigDataCorpAPIMinorDocumentException,
11
+ BigDataCorpAPIInvalidDatabaseException,
12
+ BigDataCorpAPIMaxRetryException,
13
+ BigDataCorpAPIInvalidInputException,
14
+ BigDataCorpAPILoginProblemException,
15
+ BigDataCorpAPIProblemAPIException,
16
+ BigDataCorpAPIOnDemandQueriesException,
17
+ BigDataCorpAPIMonitoringAPIException,
18
+ BigDataCorpAPIUnmappedErrorException,
19
+ BigDataCorpAPIEmptyEnrichedProcessException)
20
+
21
+
22
+ class BigDataCorpAPI:
23
+ CPF_DATABASES = [
24
+ "government_debtors",
25
+ "election_candidate_data",
26
+ "circles_college_class",
27
+ "circles_coworkers",
28
+ "circles_household",
29
+ "circles_relatives",
30
+ "circles_lawsuit_parties",
31
+ "circles_partners",
32
+ "circles_neighbors",
33
+ "circles_building",
34
+ "basic_data",
35
+ "occupation_data",
36
+ "electoral_donors",
37
+ "related_people_emails",
38
+ "addresses_extended",
39
+ "related_people_addresses",
40
+ "media_profile_and_exposure",
41
+ "company_group_employed",
42
+ "company_group_family_ownership",
43
+ "company_group_sued",
44
+ "company_group_ownership",
45
+ "historical_basic_data",
46
+ "financial_data",
47
+ "demographic_data",
48
+ "licenses_and_authorizations",
49
+ "life_stages",
50
+ "collections",
51
+ "electoral_providers",
52
+ "indebtedness_question",
53
+ "processes",
54
+ "first_level_relatives_lawsuit_data",
55
+ "business_relationships",
56
+ "related_people",
57
+ "phones_extended",
58
+ "related_people_phones",
59
+ "vehicles",
60
+ "registration_data"]
61
+
62
+ CNPJ_DATABASES = [
63
+ "partner_murabei_credit_score_company",
64
+ "government_debtors",
65
+ "syndicate_agreements",
66
+ "investment_fund_data",
67
+ 'electoral_donors',
68
+ 'owners_electoral_donors',
69
+ 'company_evolution',
70
+ 'activity_indicators',
71
+ 'interests_and_behaviors',
72
+ 'licenses_and_authorizations',
73
+ "financial_market",
74
+ 'electoral_providers',
75
+ 'industrial_property',
76
+ 'employees_industrial_property',
77
+ 'owners_industrial_property',
78
+ 'domains',
79
+ 'domains_extended',
80
+ 'emails_extended',
81
+ 'related_people_emails',
82
+ 'addresses_extended',
83
+ 'related_people_addresses',
84
+ 'phones_extended',
85
+ 'related_people_phones',
86
+ 'basic_data',
87
+ 'history_basic_data',
88
+ 'media_profile_and_exposure',
89
+ 'kyc_dtec_flex_news',
90
+ 'kyc',
91
+ 'economic_group_kyc',
92
+ 'employees_kyc',
93
+ 'owners_kyc',
94
+ 'online_ads',
95
+ 'marketplace_data',
96
+ 'apps_networks_and_platforms',
97
+ 'collections',
98
+ 'owners_lawsuits',
99
+ 'processes',
100
+ 'reputations_and_reviews',
101
+ "social_conscience",
102
+ "awards_and_certifications",
103
+ 'circles_employees',
104
+ 'circles_legal_representatives',
105
+ 'circles_first_level_owners',
106
+ 'economic_group_full_extended',
107
+ 'economic_group_first_level_extended',
108
+ 'economic_group_second_level_extended',
109
+ 'economic_group_third_level_extended',
110
+ 'company_group_household_activity',
111
+ 'company_group_rfcontact',
112
+ 'company_group_household',
113
+ 'company_group_tradename',
114
+ 'company_group_tradename_city',
115
+ 'company_group_building',
116
+ 'company_group_documentroot',
117
+ 'company_group_officialname',
118
+ 'company_group_legal_representative',
119
+ 'company_group_owners',
120
+ 'company_group_household_owners_surname',
121
+ 'relationships',
122
+ 'economic_group_relationships',
123
+ "registration_data"]
124
+
125
+ MARKETPLACE_DATABASES = [
126
+ "partner_murabei_credit_score_company"
127
+ ]
128
+
129
+ PROCESS_DATABASES = [
130
+ 'basic_data',
131
+ 'cade_processes_data'
132
+ ]
133
+
134
+ def __init__(self, bigdata_auth_token: str):
135
+ """
136
+ __init__.
137
+
138
+ Args:
139
+ bigdata_auth_token [str]: Authentication token for BigData API.
140
+ """
141
+ self._bigdata_auth_token = bigdata_auth_token
142
+
143
+ def list_cpf_dataset(self) -> list:
144
+ """
145
+ Return avaiable BigData CPF Datasets.
146
+
147
+ Args:
148
+ No Args
149
+ Kwargs:
150
+ No Kwargs
151
+ Return:
152
+ Return a list with avaiable datasets.
153
+ """
154
+ return self.CPF_DATABASES
155
+
156
+ def list_cnpj_dataset(self) -> list:
157
+ """
158
+ Return avaiable BigData CNPJ Datasets.
159
+
160
+ Args:
161
+ No Args
162
+ Kwargs:
163
+ No Kwargs
164
+ Return:
165
+ Return a list with avaiable datasets.
166
+ """
167
+ return self.CNPJ_DATABASES
168
+
169
+ def list_process_dataset(self) -> list:
170
+ """
171
+ Return avaiable BigData process Datasets.
172
+
173
+ Args:
174
+ No Args
175
+ Kwargs:
176
+ No Kwargs
177
+ Return:
178
+ Return a list with avaiable datasets.
179
+ """
180
+ return self.PROCESS_DATABASES
181
+
182
+ def get_cpf_dataset(self, cpf: str, dataset: str) -> dict:
183
+ """
184
+ Call BigData API to fecth a database for a CPF.
185
+
186
+ Retry for 5 times sleeping 1 second when errors are raised.
187
+
188
+ Args:
189
+ cpf [str]: Person's CPF.
190
+ dataset [str]: Dataset on BigData that user should be fetched.
191
+ Return [dict]:
192
+ Information avaiable on BigData.
193
+ Raise:
194
+ BigDataCorpAPIException: Raise if errors in API occour.
195
+ """
196
+ if dataset not in self.CPF_DATABASES:
197
+ msg = (
198
+ "dataset [{dataset}] not avaiable on bigboost for CPF, "
199
+ "avaiable datasets:\n{datasets}").format(
200
+ dataset=dataset, datasets=", ".join(self.CPF_DATABASES))
201
+ raise BigDataCorpAPIException(msg)
202
+
203
+ url = "https://bigboost.bigdatacorp.com.br/peoplev2"
204
+ payload = {
205
+ "Datasets": dataset,
206
+ "q": "doc{" + cpf + "}",
207
+ "Limit": 1}
208
+ headers = {
209
+ "accept": "application/json",
210
+ "content-type": "application/json",
211
+ "AccessToken": self._bigdata_auth_token}
212
+
213
+ error_msgs = []
214
+ for i in range(5):
215
+ try:
216
+ response = requests.post(url, json=payload, headers=headers)
217
+ response.raise_for_status()
218
+ response_json = response.json()
219
+ status_data = response_json['Status']
220
+
221
+ # Treat minor validation error
222
+ birth_validation = status_data.get('date_of_birth_validation')
223
+ if birth_validation is not None:
224
+ raise BigDataCorpAPIMinorDocumentException(
225
+ message="this cpf belongs to a minor",
226
+ payload=birth_validation[0])
227
+
228
+ login_entry = status_data.get("login")
229
+ if login_entry is not None:
230
+ login_return = login_entry[0]
231
+ if login_return["Code"] == -101:
232
+ msg = "BigBoost user has expired"
233
+ raise BigDataCorpAPILoginProblemException(msg)
234
+
235
+ # Check if the CPF has a match
236
+ status = status_data[dataset][0]
237
+ if status['Code'] == 0:
238
+ return response.json()
239
+
240
+ elif status['Code'] >= -202 and status['Code'] <= -100:
241
+ raise BigDataCorpAPIInvalidInputException(
242
+ message="error related to input data",
243
+ payload={
244
+ 'bigdata_status': status,
245
+ 'cpf': cpf,
246
+ 'dataset': dataset})
247
+
248
+ elif status['Code'] >= -1002 and status['Code'] <= -1000:
249
+ raise BigDataCorpAPILoginProblemException(
250
+ message="error related to login problem",
251
+ payload={
252
+ 'bigdata_status': status,
253
+ 'cpf': cpf,
254
+ 'dataset': dataset
255
+ })
256
+ elif status['Code'] >= -2999 and status['Code'] <= -2000:
257
+ raise BigDataCorpAPIProblemAPIException(
258
+ message="error related to internal problems in APIs "
259
+ "or services",
260
+ payload={
261
+ 'bigdata_status': status,
262
+ 'cpf': cpf,
263
+ 'dataset': dataset
264
+ })
265
+ elif status['Code'] >= -1999 and status['Code'] <= -1200:
266
+ raise BigDataCorpAPIOnDemandQueriesException(
267
+ message="error related to on-demand queries",
268
+ payload={
269
+ 'bigdata_status': status,
270
+ 'cpf': cpf,
271
+ 'dataset': dataset
272
+ })
273
+ elif status['Code'] <= -3000:
274
+ raise BigDataCorpAPIMonitoringAPIException(
275
+ message="error related to problems in the Monitoring "
276
+ "API or Asynchronous Calls",
277
+ payload={
278
+ 'bigdata_status': status,
279
+ 'cpf': cpf,
280
+ 'dataset': dataset
281
+ })
282
+ else:
283
+ raise BigDataCorpAPIUnmappedErrorException(
284
+ message="unmapped error",
285
+ payload={
286
+ 'bigdata_status': status,
287
+ 'cpf': cpf,
288
+ 'dataset': dataset
289
+ })
290
+
291
+ # Raise if document is invalid
292
+ except BigDataCorpAPIException as e:
293
+ raise e
294
+
295
+ # Raise if document is invalid
296
+ except BigDataCorpAPILoginProblemException as e:
297
+ raise e
298
+
299
+ # Raise if document is from an under age (age < 18)
300
+ except BigDataCorpAPIMinorDocumentException as e:
301
+ raise e
302
+
303
+ except Exception as e:
304
+ error_msgs.append(str(e))
305
+ print("!!Error fetching BigData API:", str(e))
306
+
307
+ msg = (
308
+ "Untreated error on API with max 5 retries:{}\n".format(
309
+ "\n".join(error_msgs)))
310
+ raise BigDataCorpAPIMaxRetryException(
311
+ message=msg, payload={"errors": error_msgs})
312
+
313
+ def get_cnpj_dataset(self, cnpj: str, dataset: str) -> dict:
314
+ """
315
+ Call BigData API to fecth a database for a CNPJ.
316
+
317
+ Retry for 5 times sleeping 1 second when errors are raised.
318
+
319
+ Args:
320
+ cnpj [str]: Company CNPJ.
321
+ dataset [str]: Dataset on BigData that user should be fetched.
322
+ Return [dict]:
323
+ Information avaiable on BigData.
324
+ Raise:
325
+ BigDataCorpAPIException: Raise if errors in API occour.
326
+ """
327
+ if dataset not in self.CNPJ_DATABASES:
328
+ msg = (
329
+ "dataset [{dataset}] not avaiable on bigboost for CNPJ, "
330
+ "avaiable datasets:\n{datasets}").format(
331
+ dataset=dataset, datasets=", ".join(self.CNPJ_DATABASES))
332
+ raise BigDataCorpAPIException(msg)
333
+
334
+ if dataset in self.MARKETPLACE_DATABASES:
335
+ url = "https://plataforma.bigdatacorp.com.br/marketplace"
336
+ else:
337
+ url = "https://bigboost.bigdatacorp.com.br/companies"
338
+
339
+ payload = {
340
+ "Datasets": dataset,
341
+ "q": "doc{" + cnpj + "}",
342
+ "Limit": 1}
343
+ headers = {
344
+ "accept": "application/json",
345
+ "content-type": "application/json",
346
+ "AccessToken": self._bigdata_auth_token}
347
+
348
+ error_msgs = []
349
+ for i in range(5):
350
+ try:
351
+ response = requests.post(url, json=payload, headers=headers)
352
+ response.raise_for_status()
353
+ response_json = response.json()
354
+ status_data = response_json['Status']
355
+
356
+ login_entry = status_data.get("login")
357
+ if login_entry is not None:
358
+ login_return = login_entry[0]
359
+ if login_return["Code"] == -101:
360
+ msg = "BigBoost user has expired"
361
+ raise BigDataCorpAPILoginProblemException(msg)
362
+
363
+ # Check if the CNPJ has a match
364
+ status = status_data[dataset][0]
365
+ if status['Code'] == 0:
366
+ return response.json()
367
+ elif status['Code'] >= -202 and status['Code'] <= -100:
368
+ raise BigDataCorpAPIInvalidInputException(
369
+ message="error related to input data",
370
+ payload={
371
+ 'bigdata_status': status,
372
+ 'cnpj': cnpj,
373
+ 'dataset': dataset
374
+ })
375
+ elif status['Code'] >= -1002 and status['Code'] <= -1000:
376
+ raise BigDataCorpAPILoginProblemException(
377
+ message="error related to login problem",
378
+ payload={
379
+ 'bigdata_status': status,
380
+ 'cnpj': cnpj,
381
+ 'dataset': dataset
382
+ })
383
+ elif status['Code'] >= -2999 and status['Code'] <= -2000:
384
+ raise BigDataCorpAPIProblemAPIException(
385
+ message="error related to internal problems in APIs "
386
+ "or services",
387
+ payload={
388
+ 'bigdata_status': status,
389
+ 'cnpj': cnpj,
390
+ 'dataset': dataset
391
+ })
392
+ elif status['Code'] >= -1999 and status['Code'] <= -1200:
393
+ raise BigDataCorpAPIOnDemandQueriesException(
394
+ message="error related to on-demand queries",
395
+ payload={
396
+ 'bigdata_status': status,
397
+ 'cnpj': cnpj,
398
+ 'dataset': dataset
399
+ })
400
+ elif status['Code'] <= -3000:
401
+ raise BigDataCorpAPIMonitoringAPIException(
402
+ message="error related to problems in the Monitoring "
403
+ "API or Asynchronous Calls",
404
+ payload={
405
+ 'bigdata_status': status,
406
+ 'cnpj': cnpj,
407
+ 'dataset': dataset
408
+ })
409
+ else:
410
+ raise BigDataCorpAPIUnmappedErrorException(
411
+ message="unmapped error",
412
+ payload={
413
+ 'bigdata_status': status,
414
+ 'cnpj': cnpj,
415
+ 'dataset': dataset
416
+ })
417
+
418
+ # Raise if document is invalid
419
+ except BigDataCorpAPIException as e:
420
+ raise e
421
+
422
+ except Exception as e:
423
+ error_msgs.append(str(e))
424
+ print("!!Error fetching BigData API:", str(e))
425
+
426
+ msg = (
427
+ "Untreated error on API with max 5 retries:{}\n".format(
428
+ "\n".join(error_msgs)))
429
+ raise BigDataCorpAPIMaxRetryException(
430
+ message=msg, payload={"errors": error_msgs})
431
+
432
+
433
+ def get_process_dataset(self, process: str, dataset: str) -> dict:
434
+ """Call BigData API to fecth a database for a process.
435
+
436
+ Retry for 5 times sleeping 1 second when errors are raised.
437
+
438
+ Args:
439
+ process [str]: process number.
440
+ dataset [str]: Dataset on BigData that user should be fetched.
441
+ Return [dict]:
442
+ Information avaiable on BigData.
443
+ Raise:
444
+ BigDataCorpAPIException: Raise if errors in API occour.
445
+ """
446
+ if dataset not in self.PROCESS_DATABASES:
447
+ msg = (
448
+ "dataset [{dataset}] not avaiable on bigboost for process, "
449
+ "avaiable datasets:\n{datasets}").format(
450
+ dataset=dataset, datasets=", ".join(self.PROCESS_DATABASES))
451
+ raise BigDataCorpAPIException(msg)
452
+
453
+ url = "https://plataforma.bigdatacorp.com.br/processos"
454
+
455
+ payload = {
456
+ "Datasets": dataset,
457
+ "q": "processnumber{" + process + "}",
458
+ "Limit": 1}
459
+ headers = {
460
+ "accept": "application/json",
461
+ "content-type": "application/json",
462
+ "AccessToken": self._bigdata_auth_token}
463
+
464
+ error_msgs = []
465
+ for i in range(5):
466
+ try:
467
+ response = requests.post(url, json=payload, headers=headers)
468
+ response.raise_for_status()
469
+ response_json = response.json()
470
+ status_data = response_json['Status']
471
+ result_data = response_json.\
472
+ get('Result', [{}])[0].\
473
+ get('BasicData', {})
474
+
475
+ login_entry = status_data.get("login")
476
+ if login_entry is not None:
477
+ login_return = login_entry[0]
478
+ if login_return["Code"] == -101:
479
+ msg = "BigBoost user has expired"
480
+ raise BigDataCorpAPILoginProblemException(msg)
481
+
482
+ # Check if the process has a match
483
+ status = status_data[dataset][0]
484
+ if status['Code'] == 0 and result_data:
485
+ return response.json()
486
+ elif not result_data:
487
+ raise BigDataCorpAPIEmptyEnrichedProcessException(
488
+ message="no process data returned",
489
+ payload={
490
+ 'bigdata_status': status,
491
+ 'process_number': process,
492
+ 'dataset': dataset
493
+ })
494
+ elif status['Code'] >= -202 and status['Code'] <= -100:
495
+ raise BigDataCorpAPIInvalidInputException(
496
+ message="error related to input data",
497
+ payload={
498
+ 'bigdata_status': status,
499
+ 'process_number': process,
500
+ 'dataset': dataset
501
+ })
502
+ elif status['Code'] >= -1002 and status['Code'] <= -1000:
503
+ raise BigDataCorpAPILoginProblemException(
504
+ message="error related to login problem",
505
+ payload={
506
+ 'bigdata_status': status,
507
+ 'process_number': process,
508
+ 'dataset': dataset
509
+ })
510
+ elif status['Code'] >= -2999 and status['Code'] <= -2000:
511
+ raise BigDataCorpAPIProblemAPIException(
512
+ message="error related to internal problems in APIs "
513
+ "or services",
514
+ payload={
515
+ 'bigdata_status': status,
516
+ 'process_number': process,
517
+ 'dataset': dataset
518
+ })
519
+ elif status['Code'] >= -1999 and status['Code'] <= -1200:
520
+ raise BigDataCorpAPIOnDemandQueriesException(
521
+ message="error related to on-demand queries",
522
+ payload={
523
+ 'bigdata_status': status,
524
+ 'process_number': process,
525
+ 'dataset': dataset
526
+ })
527
+ elif status['Code'] <= -3000:
528
+ raise BigDataCorpAPIMonitoringAPIException(
529
+ message="error related to problems in the Monitoring "
530
+ "API or Asynchronous Calls",
531
+ payload={
532
+ 'bigdata_status': status,
533
+ 'process_number': process,
534
+ 'dataset': dataset
535
+ })
536
+ else:
537
+ raise BigDataCorpAPIUnmappedErrorException(
538
+ message="unmapped error",
539
+ payload={
540
+ 'bigdata_status': status,
541
+ 'process_number': process,
542
+ 'dataset': dataset
543
+ })
544
+
545
+ # Raise if document is invalid
546
+ except BigDataCorpAPIException as e:
547
+ raise e
548
+
549
+ except Exception as e:
550
+ error_msgs.append(str(e))
551
+ print("!!Error fetching BigData API:", str(e))
552
+
553
+ msg = (
554
+ "Untreated error on API with max 5 retries:{}\n".format(
555
+ "\n".join(error_msgs)))
556
+ raise BigDataCorpAPIMaxRetryException(
557
+ message=msg, payload={"errors": error_msgs})
558
+
559
+ def get_cpf_datasets(self, cpf: str, datasets: list,
560
+ verbosity: bool = False) -> dict:
561
+ """
562
+ Fetch a list of datasets and return a dictionary with all info.
563
+
564
+ Args:
565
+ cpf [str]: Person's CPF.
566
+ datasets [list[str]]: List of all datasets to be fetched.
567
+ Kwargs:
568
+ verbosity [bool]: If set true will print a msg for each dataset
569
+ fetch.
570
+ Returns [dict]:
571
+ Return a dictionary with all dataset information, with keys
572
+ corresponding to dataset name.
573
+ """
574
+ response_dict = {}
575
+ for db in datasets:
576
+ if verbosity:
577
+ print("Fetching dataset:", db)
578
+ response_dict[db] = self.get_cpf_dataset(
579
+ cpf=cpf, dataset=db)
580
+ return response_dict
581
+
582
+ def get_cnpj_datasets(self, cnpj: str, datasets: list,
583
+ verbosity: bool = False) -> dict:
584
+ """
585
+ Fetch a list of datasets and return a dictionary with all info.
586
+
587
+ Args:
588
+ cnpj [str]: Company cnpj.
589
+ datasets [list[str]]: List of all datasets to be fetched.
590
+ Kwargs:
591
+ verbosity [bool]: If set true will print a msg for each dataset
592
+ fetch.
593
+ Returns [dict]:
594
+ Return a dictionary with all dataset information, with keys
595
+ corresponding to dataset name.
596
+ """
597
+ cnpj = cnpj.replace(".", "").replace("/", "").replace("-", "")
598
+ response_dict = {}
599
+ for db in datasets:
600
+ if verbosity:
601
+ print("Fetching dataset:", db)
602
+ response_dict[db] = self.get_cnpj_dataset(
603
+ cnpj=cnpj, dataset=db)
604
+ return response_dict
605
+
606
+
607
+ def get_process_datasets(self, process: str, datasets: list,
608
+ verbosity: bool = False) -> dict:
609
+ """Fetch a list of datasets and return a dictionary with all info.
610
+
611
+ Args:
612
+ process [str]: process number.
613
+ datasets [list[str]]: List of all datasets to be fetched.
614
+ Kwargs:
615
+ verbosity [bool]: If set true will print a msg for each dataset
616
+ fetch.
617
+ Returns [dict]:
618
+ Return a dictionary with all dataset information, with keys
619
+ corresponding to dataset name.
620
+ """
621
+ process = process.replace(".", "").replace("/", "").replace("-", "")
622
+ response_dict = {}
623
+ for db in datasets:
624
+ if verbosity:
625
+ print("Fetching dataset:", db)
626
+ response_dict[db] = self.get_process_dataset(
627
+ process=process, dataset=db)
628
+ return response_dict
629
+
630
+
631
+ def get_usage(self, initial_date: str, final_date: str):
632
+ """
633
+ Retrieves usage data for a specified date range.
634
+
635
+ Parameters:
636
+ - initial_date (str): The initial date of the range in the format
637
+ 'yyyy-MM-dd'.
638
+ - final_date (str): The final date of the range in the format
639
+ 'yyyy-MM-dd'.
640
+
641
+ Returns:
642
+ - results (list): A list of dictionaries containing the usage data for
643
+ each API and endpoint.
644
+ Each dictionary has the following keys:
645
+ - 'api_type' (str): The type of API ('people' or 'companies').
646
+ - 'end_point' (str): The endpoint of the API.
647
+ - 'successful_requests' (int): The total number of successful
648
+ requests made.
649
+ - 'requests_with_error' (int): The total number of requests
650
+ with errors.
651
+ - 'queries_charged' (int): The total number of queries charged.
652
+ - 'queries_not_charged' (int): The total number of queries
653
+ not charged.
654
+ - 'estimated_price' (float): The total estimated price
655
+ for the usage.
656
+
657
+ """
658
+ results = []
659
+ url = "https://plataforma.bigdatacorp.com.br/usage"
660
+
661
+ headers = {
662
+ "accept": "application/json",
663
+ "content-type": "application/json",
664
+ "AccessToken": self._bigdata_auth_token,
665
+ }
666
+
667
+ payload = {
668
+ "InitialReferenceDate": initial_date,
669
+ "FinalReferenceDate": final_date,
670
+ "DateFormat": "yyyy-MM-dd"
671
+ }
672
+
673
+ for api in self.CPF_DATABASES:
674
+ payload["Api"] = "people"
675
+ payload["Datasets"] = api
676
+ try:
677
+ response = requests.post(url, headers=headers, json=payload)
678
+ if response.status_code == 500:
679
+ response.raise_for_status()
680
+
681
+ if response.status_code != 200:
682
+ raise BigDataCorpAPIException(
683
+ response.json()['Status']['Message'])
684
+
685
+ results.append({
686
+ 'api_type': "people",
687
+ 'end_point': api,
688
+ "successful_requests": response.json()["UsageData"]
689
+ ["TotalSuccessfulRequests"],
690
+ "requests_with_error": response.json()["UsageData"]
691
+ ["TotalRequestsWithError"],
692
+ "queries_charged": response.json()["UsageData"]
693
+ ["TotalQueriesCharged"],
694
+ "queries_not_charged": response.json()["UsageData"]
695
+ ["TotalQueriesNotCharged"],
696
+ "estimated_price": response.json()["UsageData"]
697
+ ["TotalEstimatedPrice"],
698
+ })
699
+
700
+ except Exception as err:
701
+ print(err)
702
+
703
+ for api in self.CNPJ_DATABASES:
704
+ payload["Api"] = "companies"
705
+ payload["Datasets"] = api
706
+ try:
707
+ response = requests.post(url, headers=headers, json=payload)
708
+
709
+ if response.status_code == 500:
710
+ response.raise_for_status()
711
+
712
+ if response.status_code != 200:
713
+ raise BigDataCorpAPIException(
714
+ response.json()['Status']['Message'])
715
+
716
+ results.append({
717
+ 'api_type': "companies",
718
+ 'end_point': api,
719
+ "successful_requests": response.json()["UsageData"]
720
+ ["TotalSuccessfulRequests"],
721
+ "requests_with_error": response.json()["UsageData"]
722
+ ["TotalRequestsWithError"],
723
+ "queries_charged": response.json()["UsageData"]
724
+ ["TotalQueriesCharged"],
725
+ "queries_not_charged": response.json()["UsageData"]
726
+ ["TotalQueriesNotCharged"],
727
+ "estimated_price": response.json()["UsageData"]
728
+ ["TotalEstimatedPrice"],
729
+ })
730
+
731
+ except Exception as err:
732
+ print(err)
733
+
734
+ return results
@@ -0,0 +1,66 @@
1
+ """Custom exceptions for BigDataCorpAPI."""
2
+
3
+
4
+ class BigDataCorpAPIException(Exception):
5
+ def __repr__(self):
6
+ template = "{class_name}: {message}"
7
+ return template.format(
8
+ class_name=self.__class__.__name__,
9
+ message=self.message)
10
+
11
+ def __str__(self):
12
+ return self.__repr__()
13
+
14
+ def __init__(self, message: str, payload: dict = {}):
15
+ self.message = message
16
+ self.payload = payload
17
+
18
+ def to_dict(self):
19
+ rv = {
20
+ "payload": self.payload,
21
+ "type": self.__class__.__name__,
22
+ "message": self.message}
23
+ return rv
24
+
25
+
26
+ class BigDataCorpAPIInvalidDocumentException(BigDataCorpAPIException):
27
+ pass
28
+
29
+
30
+ class BigDataCorpAPIInvalidDatabaseException(BigDataCorpAPIException):
31
+ pass
32
+
33
+
34
+ class BigDataCorpAPIMinorDocumentException(BigDataCorpAPIException):
35
+ pass
36
+
37
+
38
+ class BigDataCorpAPIMaxRetryException(BigDataCorpAPIException):
39
+ pass
40
+
41
+
42
+ class BigDataCorpAPILoginProblemException(BigDataCorpAPIException):
43
+ pass
44
+
45
+
46
+ class BigDataCorpAPIProblemAPIException(BigDataCorpAPIException):
47
+ pass
48
+
49
+
50
+ class BigDataCorpAPIOnDemandQueriesException(BigDataCorpAPIException):
51
+ pass
52
+
53
+
54
+ class BigDataCorpAPIMonitoringAPIException(BigDataCorpAPIException):
55
+ pass
56
+
57
+
58
+ class BigDataCorpAPIUnmappedErrorException(BigDataCorpAPIException):
59
+ pass
60
+
61
+
62
+ class BigDataCorpAPIInvalidInputException(BigDataCorpAPIException):
63
+ pass
64
+
65
+ class BigDataCorpAPIEmptyEnrichedProcessException(BigDataCorpAPIException):
66
+ pass
@@ -0,0 +1,30 @@
1
+ """Test BigDataCorpAPI."""
2
+ import os
3
+ import unittest
4
+ from bigdatacorp_api.data import BigDataCorpAPI
5
+ from bigdatacorp_api.exceptions import (
6
+ BigDataCorpAPIException, BigDataCorpAPIInvalidDocumentException,
7
+ BigDataCorpAPIMaxRetryException)
8
+
9
+
10
+ BIGDATA_AUTH_TOKEN = os.environ.get("BIGDATA_AUTH_TOKEN")
11
+ TEST_CPF = os.environ.get("TEST_CPF")
12
+
13
+
14
+ class TestBigDataCorpAPI(unittest.TestCase):
15
+ """Test process Calendar input data."""
16
+
17
+ def test__ok(self):
18
+ bigdata_api = BigDataCorpAPI(
19
+ bigdata_auth_token=BIGDATA_AUTH_TOKEN)
20
+ bigdata_api.get_cpf_dataset(
21
+ cpf=TEST_CPF, dataset="basic_data")
22
+
23
+ def test__invalid(self):
24
+ bigdata_api = BigDataCorpAPI(
25
+ bigdata_auth_token=BIGDATA_AUTH_TOKEN)
26
+ with self.assertRaises(
27
+ BigDataCorpAPIInvalidDocumentException) as context:
28
+ bigdata_api.get_cpf_dataset(
29
+ cpf=TEST_CPF + "0", dataset="basic_data")
30
+ self.assertEqual(context.exception.message, 'cpf is invalid')