Spaces:
Sleeping
Sleeping
Keep missing VK counters unknown instead of zero
Browse files- legacy_parser.py +3 -1
- service.py +3 -1
- tests/test_service.py +11 -0
- tests/test_vk_counters.py +29 -0
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 |
-
|
|
|
|
|
|
|
| 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.
|
| 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()
|