update_companies.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. from __future__ import annotations
  2. import time
  3. import itertools
  4. import json
  5. import sys
  6. import tomllib
  7. import typing
  8. import httpx
  9. def main() -> None:
  10. (month,) = sys.argv[1:]
  11. with open('config.toml', 'rb') as f:
  12. config = tomllib.load(f)
  13. fio_api_key = config['fio_api_key']
  14. with open(f'www/data/company-data-{month}.json', 'r') as f:
  15. company_ids = frozenset(json.load(f)['totals'])
  16. print(len(company_ids), 'companies in', month, 'data')
  17. with open('www/data/knownCompanies.json', 'r') as f:
  18. companies = json.load(f)
  19. now = int(time.time())
  20. cached: set[str] = set()
  21. for co in companies.values():
  22. co_last_update = co.get('LastUpdated')
  23. if co_last_update is not None and now - co_last_update < 24 * 60 * 60:
  24. cached.add(co['Username'].casefold())
  25. print(len(cached), 'cached companies')
  26. all_users: typing.Collection[str] = httpx.get('https://rest.fnar.net/user/allusers',
  27. headers={'Authorization': fio_api_key}).raise_for_status().json()
  28. try:
  29. for username in all_users:
  30. if username.casefold() in cached:
  31. continue
  32. response = get_with_retry('https://rest.fnar.net/user/' + username)
  33. if response.status_code in (204, 404):
  34. print(username, 'not found')
  35. continue
  36. fio_user = response.json()
  37. assert username.casefold() == fio_user['UserName'].casefold(), f'{username} != {fio_user["UserName"]}'
  38. known_user = {'Username': username, 'LastUpdated': now}
  39. if corp := fio_user.get('CorporationCode'):
  40. known_user['Corporation'] = corp
  41. if known_user == companies.get(fio_user['CompanyId']):
  42. print(username, 'had no change')
  43. else:
  44. print(known_user)
  45. companies[fio_user['CompanyId']] = known_user
  46. finally:
  47. with open('www/data/knownCompanies.json', 'w') as f:
  48. json.dump(companies, f)
  49. unknown_companies = company_ids - companies.keys()
  50. print(len(unknown_companies), 'unknown companies')
  51. try:
  52. for batch in itertools.batched(unknown_companies, 10):
  53. company_data: typing.Collection[FIOCompany | None] = httpx.get('https://api.fnar.net/company/lookup',
  54. params={'company': batch}).raise_for_status().json()
  55. for fio_co in company_data:
  56. if fio_co is None:
  57. continue
  58. known_user = {'Username': fio_co['UserName'], 'LastUpdated': now}
  59. if (corp := fio_co.get('CorporationCode')) and corp != 'UNKN':
  60. known_user['Corporation'] = corp
  61. print(known_user)
  62. assert fio_co['CompanyId'] not in companies
  63. companies[fio_co['CompanyId']] = known_user
  64. finally:
  65. with open('www/data/knownCompanies.json', 'w') as f:
  66. json.dump(companies, f)
  67. def get_with_retry(url) -> httpx.Response:
  68. for attempt in range(10):
  69. try:
  70. response = httpx.get(url)
  71. except httpx.TransportError as e:
  72. print(e, 'retrying in', attempt)
  73. time.sleep(attempt)
  74. continue
  75. if response.status_code in (204, 404) or response.is_success:
  76. return response
  77. elif attempt == 9:
  78. response.raise_for_status()
  79. print(response.status_code, 'retrying in', attempt)
  80. time.sleep(attempt)
  81. raise AssertionError
  82. class FIOCompany(typing.TypedDict):
  83. CompanyId: str
  84. UserName: str
  85. CorporationCode: str | None
  86. if __name__ == '__main__':
  87. main()