Compare commits

..
Author SHA1 Message Date
Typosaurus 6a6e6716b1 ticket #112 attempt 1 2026-07-23 02:49:57 +00:00
6 changed files with 152 additions and 119 deletions
File diff suppressed because one or more lines are too long
+7
View File
@@ -46,6 +46,7 @@ from devplacepy.utils import (
track_action,
build_achievements,
)
from devplacepy.utils.rewards import LEVEL_XP
from devplacepy.responses import respond, action_result, wants_json
from devplacepy.schemas import ProfileOut
from devplacepy.avatar import avatar_url, avatar_seed
@@ -380,6 +381,10 @@ async def profile_page(
],
)
xp = profile_user.get("xp") or 0
xp_progress_pct = xp % LEVEL_XP
xp_next_level = ((xp // LEVEL_XP) + 1) * LEVEL_XP
return respond(
request,
"profile.html",
@@ -439,6 +444,8 @@ async def profile_page(
"awards_pagination": awards_pagination,
"awards_count": awards_count,
"prominent_award": prominent_award,
"xp_progress_pct": xp_progress_pct,
"xp_next_level": xp_next_level,
"can_give_award": can_give,
},
model=ProfileOut,
+2
View File
@@ -80,6 +80,8 @@ class ProfileOut(_Out):
awards_count: int = 0
prominent_award: Optional[AwardOut] = None
can_give_award: bool = False
xp_progress_pct: Optional[int] = None
xp_next_level: Optional[int] = None
class TelegramPairOut(_Out):
+15 -18
View File
@@ -22,8 +22,6 @@ from .pdf import MAX_PDF_BYTES, extract_pdf_text, is_pdf
logger = logging.getLogger(__name__)
_pw_lock = asyncio.Lock()
RSEARCH_URL = "https://rsearch.app.molodetz.nl"
RSEARCH_TIMEOUT_SECONDS = 45.0
FETCH_TIMEOUT_SECONDS = 20.0
@@ -174,7 +172,13 @@ async def _render_with_playwright(
) -> tuple[str, str, int, list[tuple[str, str]]]:
from playwright.async_api import async_playwright
async def _render(browser) -> tuple[str, str, int, list[tuple[str, str]]]:
own_browser = browser is None
if own_browser:
pw = await async_playwright().__aenter__()
browser = await pw.chromium.launch(
headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"]
)
try:
context = await browser.new_context(user_agent=USER_AGENT)
page = await context.new_page()
response = await page.goto(url, wait_until="load", timeout=30000)
@@ -185,18 +189,10 @@ async def _render_with_playwright(
await context.close()
extracted = extract_html(content, base_url=url)
return extracted.title, extracted.text, status, extracted.links
if browser is None:
async with async_playwright() as pw:
browser = await pw.chromium.launch(
headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"]
)
try:
return await _render(browser)
finally:
if own_browser:
await browser.close()
else:
return await _render(browser)
await pw.__aexit__(None, None, None)
async def fetch_page(url: str, depth: int, browser=None) -> CrawledPage | None:
@@ -246,12 +242,8 @@ async def fetch_page(url: str, depth: int, browser=None) -> CrawledPage | None:
except (LookupError, ValueError) as exc:
logger.info("deepsearch decode failed for %s: %s", url, exc)
if len(text) < MIN_PAGE_CHARS:
async with _pw_lock:
try:
r_title, r_text, r_status, r_links = await _render_with_playwright(url, browser)
except Exception as exc:
logger.info("deepsearch render failed for %s: %s", url, exc)
r_title, r_text, r_status, r_links = "", "", 0, []
if len(r_text) > len(text):
title, text, status, source, links = (
r_title or title,
@@ -260,6 +252,8 @@ async def fetch_page(url: str, depth: int, browser=None) -> CrawledPage | None:
"playwright",
r_links,
)
except Exception as exc:
logger.info("deepsearch render failed for %s: %s", url, exc)
if len(text) < MIN_PAGE_CHARS:
return None
return CrawledPage(
@@ -302,9 +296,10 @@ async def crawl(
total = min(len(level_candidates), max_pages)
cancelled = False
pw = None
browser = None
async with async_playwright() as pw:
try:
pw = await async_playwright().__aenter__()
browser = await pw.chromium.launch(
headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"]
)
@@ -397,4 +392,6 @@ async def crawl(
finally:
if browser is not None:
await browser.close()
if pw is not None:
await pw.__aexit__(None, None, None)
return outcome
-4
View File
@@ -620,10 +620,6 @@ img {
.topnav-logo span { color: var(--accent); }
.topnav-links { display: flex; gap: 0.25rem; }
.topnav-link {
display: inline-flex;
align-items: center;
gap: 0.375rem;
white-space: nowrap;
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
font-size: 0.875rem;
+31
View File
@@ -267,6 +267,37 @@ def test_activity_comment_card_renders_overlay_link(app_server):
assert f'class="card-link" href="/posts/{post_slug}#comment-{comment_uid}"' in r.text
def test_profile_json_contains_xp_progress(app_server):
from devplacepy.database import get_table, refresh_snapshot
import time
name = f"xp{int(time.time() * 1000)}"
session = requests.Session()
session.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
},
allow_redirects=True,
)
user = get_table("users").find_one(username=name)
assert user is not None
get_table("users").update({"uid": user["uid"], "xp": 250}, ["uid"])
refresh_snapshot()
r = session.get(
f"{BASE_URL}/profile/{name}", headers={"Accept": "application/json"}
)
assert r.status_code == 200, f"status {r.status_code}: {r.text[:200]}"
body = r.json()
assert body["xp_progress_pct"] == 50
assert body["xp_next_level"] == 300
def test_profile_json_exposes_online_presence(app_server):
import time