1
0

8 Коммиты 1ed495a72d ... dc59d692d2

Автор SHA1 Сообщение Дата
  raylu dc59d692d2 stop busting the cache with ?cb=now 1 неделя назад
  raylu f8e4a7d5a7 update all companies 1 неделя назад
  raylu e1d74ab55b known companies 1 неделя назад
  raylu 1ee4d87c94 permalink to new domain 1 неделя назад
  raylu de027f5906 may26 1 неделя назад
  raylu 45c9e31fc4 prepare data 1 неделя назад
  raylu 1ed495a72d may26 1 неделя назад
  raylu 6746817843 prepare data 1 неделя назад
10 измененных файлов с 155 добавлено и 8 удалено
  1. 1 0
      .gitignore
  2. 3 0
      py/prepare.py
  3. 69 0
      py/update_companies.py
  4. 1 0
      pyproject.toml
  5. 2 2
      src/permalink.ts
  6. 5 5
      src/utils.ts
  7. 73 0
      uv.lock
  8. 0 0
      www/data/knownCompanies.json
  9. 0 0
      www/data/ship-data-may26.json
  10. 1 1
      www/index.html

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
+config.toml
 node_modules/
 www/main.js
 www/main.js.map

+ 3 - 0
py/prepare.py

@@ -16,6 +16,9 @@ def main() -> None:
 	bases_data: dict[str, dict[str, int]] = {r.company_id: {'bases': r.num, 'rank': r.rank} for r in data['BASES']}
 	with open(f'www/data/base-data-{month}.json', 'w') as f:
 		json.dump(bases_data, f)
+	ships_data: dict[str, dict[str, int]] = {r.company_id: {'ships': r.num, 'rank': r.rank} for r in data['SHIPS']}
+	with open(f'www/data/ship-data-{month}.json', 'w') as f:
+		json.dump(ships_data, f)
 
 	with open(f'rawData/{month}-prices.json', 'r') as f:
 		prices = get_prices(f)

+ 69 - 0
py/update_companies.py

@@ -0,0 +1,69 @@
+from __future__ import annotations
+import time
+
+import json
+import tomllib
+import typing
+
+import httpx
+
+def main() -> None:
+	with open('config.toml', 'rb') as f:
+		config = tomllib.load(f)
+	fio_api_key = config['fio_api_key']
+
+	with open('www/data/knownCompanies.json', 'r') as f:
+		companies = json.load(f)
+
+	now = int(time.time())
+	cached: set[str] = set()
+	for co in companies.values():
+		co_last_update = co.get('LastUpdated')
+		if co_last_update is not None and now - co_last_update < 24 * 60 * 60:
+			cached.add(co['Username'].casefold())
+	print(len(cached), 'cached companies')
+
+	all_users: typing.Collection[str] = httpx.get('https://rest.fnar.net/user/allusers',
+			headers={'Authorization': fio_api_key}).raise_for_status().json()
+
+	try:
+		for username in all_users:
+			if username.casefold() in cached:
+				continue
+
+			response = get_with_retry('https://rest.fnar.net/user/' + username)
+			if response.status_code in (204, 404):
+				print(username, 'not found')
+				continue
+			fio_user = response.json()
+			assert username.casefold() == fio_user['UserName'].casefold(), f'{username} != {fio_user["UserName"]}'
+			known_user = {'Username': username, 'LastUpdated': now}
+			if corp := fio_user.get('CorporationCode'):
+				known_user['Corporation'] = corp
+			if known_user == companies.get(fio_user['CompanyId']):
+				print(username, 'had no change')
+			else:
+				print(known_user)
+				companies[fio_user['CompanyId']] = known_user
+	finally:
+		with open('www/data/knownCompanies.json', 'w') as f:
+			json.dump(companies, f)
+
+def get_with_retry(url) -> httpx.Response:
+	for attempt in range(10):
+		try:
+			response = httpx.get(url)
+		except httpx.TransportError as e:
+			print(e, 'retrying in', attempt)
+			time.sleep(attempt)
+			continue
+		if response.status_code in (204, 404) or response.is_success:
+			return response
+		elif attempt == 9:
+			response.raise_for_status()
+		print(response.status_code, 'retrying in', attempt)
+		time.sleep(attempt)
+	raise AssertionError
+
+if __name__ == '__main__':
+	main()

+ 1 - 0
pyproject.toml

@@ -3,4 +3,5 @@ name = 'pruncalc'
 version = '0'
 requires-python = '>=3.13'
 dependencies = [
+    'httpx',
 ]

+ 2 - 2
src/permalink.ts

@@ -63,7 +63,7 @@ export function updatePermalink()
 	const hideOptionsButton = document.getElementById("hideOptions") as HTMLInputElement;
 	const latestMonthButton = document.getElementById("latestMonth") as HTMLInputElement;
 	
-	var permalink = "https://pmmg-products.github.io/reports/?type=" + graphSelect.value;
+	var permalink = "https://prun.raylu.net/stats/?type=" + graphSelect.value;
 	var rprunLink = "XIT PRUNSTATS type-" + graphSelect.value;
 	
     const graph = graphs.find(obj => obj.id == graphSelect.value);
@@ -87,4 +87,4 @@ export function updatePermalink()
 	permalinkInput.value = permalink;
 	rprunInput.value = rprunLink;
 	return;
-}
+}

+ 5 - 5
src/utils.ts

@@ -113,7 +113,7 @@ export async function getData(loadedData: any, dataType: string, month?: string)
 		case "ship":
 			if(!loadedData[dataType + '-data-' + month])
 			{
-				const response = await fetch('data/' + dataType + '-data-' + month + '.json?cb=' + Date.now())
+				const response = await fetch(`data/${dataType}-data-${month}.json`)
 				if(response.status == 200)
 				{
 					loadedData[dataType + '-data-' + month] = response.json();
@@ -123,19 +123,19 @@ export async function getData(loadedData: any, dataType: string, month?: string)
 		case "knownCompanies":
 			if(!loadedData['known-companies']) 
 			{ 
-				loadedData['known-companies'] = await fetch('data/knownCompanies.json?cb=' + Date.now()).then(response => response.json());
+				loadedData['known-companies'] = await fetch('data/knownCompanies.json').then(response => response.json());
 			}
 			return loadedData['known-companies']
 		case "universe":
 			if(!loadedData['universe-data']) 
 			{ 
-				loadedData['universe-data'] = await fetch('data/universe-data.json?cb=' + Date.now()).then(response => response.json());
+				loadedData['universe-data'] = await fetch('data/universe-data.json').then(response => response.json());
 			}
 			return loadedData['universe-data']
 		case "parentCorps":
 			if(!loadedData['parent-corps']) 
 			{ 
-				loadedData['parent-corps'] = await fetch('data/parentCorps.json?cb=' + Date.now()).then(response => response.json());
+				loadedData['parent-corps'] = await fetch('data/parentCorps.json').then(response => response.json());
 			}
 			return loadedData['parent-corps']
 	}
@@ -189,4 +189,4 @@ export function updateUsernameLabel()
             usernameLabel.firstChild.nodeValue = 'Username: ';
         }
     }
-}
+}

+ 73 - 0
uv.lock

@@ -2,7 +2,80 @@ version = 1
 revision = 3
 requires-python = ">=3.13"
 
+[[package]]
+name = "anyio"
+version = "4.13.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2026.5.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "certifi" },
+    { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
+]
+
+[[package]]
+name = "httpx"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+    { name = "certifi" },
+    { name = "httpcore" },
+    { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.18"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
+]
+
 [[package]]
 name = "pruncalc"
 version = "0"
 source = { virtual = "." }
+dependencies = [
+    { name = "httpx" },
+]
+
+[package.metadata]
+requires-dist = [{ name = "httpx" }]

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
www/data/knownCompanies.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
www/data/ship-data-may26.json


+ 1 - 1
www/index.html

@@ -14,7 +14,7 @@
 <div class="topTabContainer" id="topTabContainer">
 	<div>
 		<div class="topTab">
-			<a class="topTabLink" href="https://github.com/PMMG-Products/pmmg-products.github.io">GitHub</a>
+			<a class="topTabLink" href="https://prun.raylu.net/stats/">GitHub</a>
 			<div class="toggleIndicator toggleIndicatorActive"></div>
 		</div>
 	</div>

Некоторые файлы не были показаны из-за большого количества измененных файлов