Dink0 commited on
Commit
a48e17c
·
verified ·
1 Parent(s): ff3c435

Keep missing VK counters unknown instead of zero

Browse files
legacy_parser.py CHANGED
@@ -418,7 +418,9 @@ def process_vk_batch(batch_items: List[Tuple[str, int, str]]) -> dict:
418
  result[canonical] = (f"**Ошибка (VK)**: пост не найден — {canonical}", 0)
419
  continue
420
 
421
- views = post.get("views", {}).get("count", 0)
 
 
422
  post_owner_id = post.get("owner_id", owner_id)
423
  title = None
424
  if post_owner_id < 0:
 
418
  result[canonical] = (f"**Ошибка (VK)**: пост не найден — {canonical}", 0)
419
  continue
420
 
421
+ counter = post.get("views")
422
+ count = counter.get("count") if isinstance(counter, dict) else None
423
+ views = count if type(count) is int and count >= 0 else None
424
  post_owner_id = post.get("owner_id", owner_id)
425
  title = None
426
  if post_owner_id < 0:
service.py CHANGED
@@ -17,7 +17,7 @@ from reports import normalize, render
17
 
18
  log = logging.getLogger(__name__)
19
  API_KEY = os.getenv("BRIDGE_API_KEY", "")
20
- app = FastAPI(title="ReportBridge", version="0.1.1")
21
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=False,
22
  allow_methods=["GET", "POST", "DELETE"], allow_headers=["Content-Type", "X-Bridge-Key"])
23
  jobs, lock = {}, threading.RLock()
@@ -78,6 +78,8 @@ def run_job(job_id, records):
78
  title, views = data.get(record["url"], ("Ошибка: VK_ACCESS_TOKEN не настроен", None))
79
  error = ("Не настроен VK_ACCESS_TOKEN в Secrets PR_Tool" if not engine.VK_ACCESS_TOKEN
80
  else "Не удалось получить просмотры VK") if "ошибка" in title.lower() else None
 
 
81
  store_result(dict(record, title=title if not error else record["group"], views=None if error else views, error=error))
82
  if start + 100 < len(vk):
83
  time.sleep(0.4)
 
17
 
18
  log = logging.getLogger(__name__)
19
  API_KEY = os.getenv("BRIDGE_API_KEY", "")
20
+ app = FastAPI(title="ReportBridge", version="0.1.2")
21
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=False,
22
  allow_methods=["GET", "POST", "DELETE"], allow_headers=["Content-Type", "X-Bridge-Key"])
23
  jobs, lock = {}, threading.RLock()
 
78
  title, views = data.get(record["url"], ("Ошибка: VK_ACCESS_TOKEN не настроен", None))
79
  error = ("Не настроен VK_ACCESS_TOKEN в Secrets PR_Tool" if not engine.VK_ACCESS_TOKEN
80
  else "Не удалось получить просмотры VK") if "ошибка" in title.lower() else None
81
+ if not error and views is None:
82
+ error = "VK API не передал счётчик просмотров этого поста; это не подтверждённый ноль"
83
  store_result(dict(record, title=title if not error else record["group"], views=None if error else views, error=error))
84
  if start + 100 < len(vk):
85
  time.sleep(0.4)
tests/test_service.py CHANGED
@@ -63,6 +63,17 @@ class ServiceTests(unittest.TestCase):
63
  self.assertIn('<b>4K</b>', job['result']['html'])
64
  self.assertIn('ещё</a>', job['result']['html'])
65
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  if __name__ == '__main__':
68
  unittest.main()
 
63
  self.assertIn('<b>4K</b>', job['result']['html'])
64
  self.assertIn('ещё</a>', job['result']['html'])
65
 
66
+ def test_vk_missing_counter_is_reported_separately(self):
67
+ record = normalize('https://vk.com/wall-123_456')
68
+ service.jobs['test'] = dict(status='queued', completed=0, total=1, created=time.time())
69
+ with patch.object(service.engine, 'VK_ACCESS_TOKEN', 'test-only'), patch.object(service.engine, 'process_vk_batch', return_value={record['url']: ('Сообщество', None)}):
70
+ service.run_job('test', [record])
71
+ output = service.jobs['test']['result']
72
+ self.assertIsNone(output['records'][0]['views'])
73
+ self.assertEqual(len(output['errors']), 1)
74
+ self.assertIn('не передал счётчик', output['errors'][0])
75
+ self.assertIn('Неполный итог', output['html'])
76
+
77
 
78
  if __name__ == '__main__':
79
  unittest.main()
tests/test_vk_counters.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from unittest.mock import Mock, patch
3
+
4
+ import legacy_parser as engine
5
+
6
+
7
+ class VkCountersTests(unittest.TestCase):
8
+ def test_missing_counter_is_not_fabricated_zero(self):
9
+ url = 'https://vk.com/wall-123_456'
10
+ for field in ({}, {'views': {}}, {'views': None}, {'views': {'count': '100'}}):
11
+ with self.subTest(field=field):
12
+ post = dict(id=456, owner_id=-123, **field)
13
+ response = Mock(status_code=200, json=lambda: {'response': {'items': [post], 'groups': [{'id': 123, 'name': 'Сообщество'}]}})
14
+ with patch.object(engine.requests, 'get', return_value=response):
15
+ result = engine.process_vk_batch([(url, -123, '456')])
16
+ self.assertEqual(result[url], ('Сообщество', None))
17
+
18
+ def test_real_zero_and_nonzero_are_preserved(self):
19
+ url = 'https://vk.com/wall-123_456'
20
+ for count in (0, 23000):
21
+ post = {'id': 456, 'owner_id': -123, 'views': {'count': count}}
22
+ response = Mock(status_code=200, json=lambda: {'response': {'items': [post]}})
23
+ with patch.object(engine.requests, 'get', return_value=response):
24
+ result = engine.process_vk_batch([(url, -123, '456')])
25
+ self.assertEqual(result[url][1], count)
26
+
27
+
28
+ if __name__ == '__main__':
29
+ unittest.main()