Files
devplacepy/devplacepy/routers/gists.py
T

143 lines
5.5 KiB
Python
Raw Normal View History

import logging
from typing import Annotated
from fastapi import APIRouter, Request, Form
from devplacepy.models import GistForm, GistEditForm
from fastapi.responses import HTMLResponse, RedirectResponse
from devplacepy.database import get_table, get_users_by_uids, get_gist_languages, paginate
from devplacepy.content import load_detail, edit_content_item, delete_content_item, enrich_items, create_content_item, detail_context, canonical_redirect, first_image_url
from devplacepy.templating import templates
from devplacepy.utils import get_current_user, require_user, not_found, XP_GIST
from devplacepy.seo import base_seo_context, site_url, website_schema, software_source_code_schema, list_page_seo, next_page_url
from devplacepy.responses import respond, action_result
from devplacepy.schemas import GistsOut, GistDetailOut
logger = logging.getLogger(__name__)
router = APIRouter()
LANGUAGES = [
("python", "Python"), ("javascript", "JavaScript"), ("typescript", "TypeScript"),
("html", "HTML"), ("css", "CSS"), ("c", "C"), ("cpp", "C++"), ("java", "Java"),
("go", "Go"), ("rust", "Rust"), ("sql", "SQL"), ("bash", "Bash"),
("yaml", "YAML"), ("json", "JSON"), ("markdown", "Markdown"),
("swift", "Swift"), ("php", "PHP"), ("ruby", "Ruby"), ("kotlin", "Kotlin"),
("lua", "Lua"), ("perl", "Perl"), ("haskell", "Haskell"), ("elixir", "Elixir"),
("r", "R"), ("dart", "Dart"), ("scala", "Scala"), ("plaintext", "Plain Text"),
]
def get_gists_list(user_uid=None, language=None, before=None, viewer=None):
gists_table = get_table("gists")
filters = {}
if user_uid:
filters["user_uid"] = user_uid
if language:
filters["language"] = language
total = gists_table.count(**filters)
gists, next_cursor = paginate(gists_table, before=before, **filters)
if not gists:
return [], next_cursor, total
users_map = get_users_by_uids([g["user_uid"] for g in gists])
return enrich_items(gists, "gist", users_map, user=viewer), next_cursor, total
@router.get("", response_class=HTMLResponse)
async def gists_page(request: Request, language: str = None, user_uid: str = None, before: str = None):
user = get_current_user(request)
gists_data, next_cursor, total_count = get_gists_list(user_uid, language, before, viewer=user)
seo_ctx = list_page_seo(
request,
title="Gists",
description=f"Browse {total_count} code snippets on DevPlace.",
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Gists", "url": "/gists"},
],
next_url=next_page_url(request, next_cursor),
)
return respond(request, "gists.html", {
**seo_ctx,
"request": request,
"user": user,
"gists": gists_data,
"total_count": total_count,
"next_cursor": next_cursor,
"current_language": language,
"languages": LANGUAGES,
"gist_language_codes": get_gist_languages(),
}, model=GistsOut)
@router.get("/{gist_slug}", response_class=HTMLResponse)
async def gist_detail(request: Request, gist_slug: str):
user = get_current_user(request)
detail = load_detail("gists", "gist", gist_slug, user)
if not detail:
raise not_found("Gist not found")
gist = detail["item"]
redirect = canonical_redirect("gists", gist, gist_slug)
if redirect:
return redirect
base = site_url(request)
seo_ctx = base_seo_context(
request,
title=gist.get("title", "Gist"),
description=gist.get("description", "")[:160],
og_type="article",
og_image=first_image_url(gist, detail["attachments"]),
breadcrumbs=[
{"name": "Home", "url": "/feed"},
{"name": "Gists", "url": "/gists"},
{"name": gist.get("title", "Gist"), "url": f"/gists/{gist['slug'] or gist['uid']}"},
],
schemas=[website_schema(base), software_source_code_schema(gist, base)],
)
return respond(request, "gist_detail.html", detail_context(request, user, detail, "gist", seo_ctx, {
"languages": LANGUAGES,
}), model=GistDetailOut)
@router.post("/create")
async def create_gist(request: Request, data: Annotated[GistForm, Form()]):
user = require_user(request)
title = data.title.strip()
description = data.description.strip()
source_code = data.source_code.strip()
language = data.language
valid_languages = {l[0] for l in LANGUAGES}
if language not in valid_languages:
language = "plaintext"
uid, gist_slug = create_content_item("gists", "gist", user, {
"title": title,
"description": description or None,
"source_code": source_code,
"language": language,
}, title, XP_GIST, "First Gist", description or "", data.attachment_uids)
url = f"/gists/{gist_slug}"
return action_result(request, url, data={"uid": uid, "slug": gist_slug, "url": url})
@router.post("/edit/{gist_slug}")
async def edit_gist(request: Request, gist_slug: str, data: Annotated[GistEditForm, Form()]):
user = require_user(request)
language = data.language
if language not in {l[0] for l in LANGUAGES}:
language = "plaintext"
return edit_content_item(request, "gists", user, gist_slug, {
"title": data.title.strip(),
"description": data.description.strip() or None,
"source_code": data.source_code.strip(),
"language": language,
}, "/gists")
@router.post("/delete/{gist_slug}")
async def delete_gist(request: Request, gist_slug: str):
user = require_user(request)
return delete_content_item(request, "gists", "gist", user, gist_slug, "/gists")