feat: add location and categories fields, simplify event creation
Some checks failed
lint.yml / feat: add location and categories fields, simplify event creation (pull_request) Failing after 0s
Some checks failed
lint.yml / feat: add location and categories fields, simplify event creation (pull_request) Failing after 0s
- Add location (text) and categories (JSON list) to Event model - Make most CreateEvent fields optional: only title + start date required - Default end_date to start_date, closing_date to end_date - Categories stored as JSON text, parsed via validator - NIP-52 publisher includes location tag and t tags for categories - Migration m011 adds location and categories columns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d69ec7dda2
commit
29045163a3
4 changed files with 48 additions and 15 deletions
6
crud.py
6
crud.py
|
|
@ -164,6 +164,12 @@ async def purge_unpaid_tickets(event_id: str) -> None:
|
|||
|
||||
async def create_event(data: CreateEvent) -> Event:
|
||||
event_id = urlsafe_short_hash()
|
||||
# Default end date to start date if not provided
|
||||
if not data.event_end_date:
|
||||
data.event_end_date = data.event_start_date
|
||||
# Default closing date to end date if not provided
|
||||
if not data.closing_date:
|
||||
data.closing_date = data.event_end_date
|
||||
event = Event(id=event_id, time=datetime.now(timezone.utc), **data.dict())
|
||||
await db.insert("events.events", event)
|
||||
return event
|
||||
|
|
|
|||
|
|
@ -231,3 +231,15 @@ async def m010_add_events_settings(db):
|
|||
await db.execute(
|
||||
"INSERT OR IGNORE INTO events.settings (id, auto_approve) VALUES (1, FALSE);"
|
||||
)
|
||||
|
||||
|
||||
async def m011_add_location_and_categories(db):
|
||||
"""
|
||||
Add location and categories columns for NIP-52 calendar event support.
|
||||
"""
|
||||
await db.execute(
|
||||
"ALTER TABLE events.events ADD COLUMN location TEXT;"
|
||||
)
|
||||
await db.execute(
|
||||
"ALTER TABLE events.events ADD COLUMN categories TEXT;"
|
||||
)
|
||||
|
|
|
|||
39
models.py
39
models.py
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -29,15 +30,17 @@ class EventExtra(BaseModel):
|
|||
|
||||
class CreateEvent(BaseModel):
|
||||
wallet: Optional[str] = None
|
||||
name: str
|
||||
info: str
|
||||
closing_date: str
|
||||
event_start_date: str
|
||||
event_end_date: str
|
||||
name: str # title (required)
|
||||
info: str = "" # description (optional, visible by default)
|
||||
closing_date: Optional[str] = None # defaults to event_end_date or event_start_date
|
||||
event_start_date: str # required
|
||||
event_end_date: Optional[str] = None # defaults to event_start_date
|
||||
currency: str = "sat"
|
||||
amount_tickets: int = Query(..., ge=0)
|
||||
price_per_ticket: float = Query(..., ge=0)
|
||||
banner: Optional[str] = None
|
||||
amount_tickets: int = 0 # 0 = unlimited / not ticketed
|
||||
price_per_ticket: float = 0 # 0 = free
|
||||
banner: Optional[str] = None # image URL (optional, visible by default)
|
||||
location: Optional[str] = None # venue/address (optional, visible by default)
|
||||
categories: list[str] = Field(default_factory=list) # NIP-52 't' tags
|
||||
extra: EventExtra = Field(default_factory=EventExtra)
|
||||
status: str = "approved" # proposed, approved, rejected
|
||||
|
||||
|
|
@ -67,22 +70,30 @@ class Event(BaseModel):
|
|||
id: str
|
||||
wallet: str
|
||||
name: str
|
||||
info: str
|
||||
closing_date: str
|
||||
info: str = ""
|
||||
closing_date: str | None = None
|
||||
canceled: bool = False
|
||||
event_start_date: str
|
||||
event_end_date: str
|
||||
currency: str
|
||||
amount_tickets: int
|
||||
price_per_ticket: float
|
||||
event_end_date: str | None = None
|
||||
currency: str = "sat"
|
||||
amount_tickets: int = 0
|
||||
price_per_ticket: float = 0
|
||||
time: datetime
|
||||
sold: int = 0
|
||||
banner: str | None = None
|
||||
location: str | None = None
|
||||
categories: list[str] = Field(default_factory=list)
|
||||
extra: EventExtra = Field(default_factory=EventExtra)
|
||||
status: str = "approved" # proposed, approved, rejected
|
||||
nostr_event_id: str | None = None
|
||||
nostr_event_created_at: int | None = None
|
||||
|
||||
@validator("categories", pre=True)
|
||||
def parse_categories(cls, v):
|
||||
if isinstance(v, str):
|
||||
return json.loads(v) if v else []
|
||||
return v or []
|
||||
|
||||
|
||||
class EventsSettings(BaseModel):
|
||||
"""Extension-level settings for the events extension."""
|
||||
|
|
|
|||
|
|
@ -40,13 +40,17 @@ def build_nip52_event(event: Event, pubkey: str) -> NostrEvent:
|
|||
tags.append(["end", event.event_end_date])
|
||||
if event.banner:
|
||||
tags.append(["image", event.banner])
|
||||
if event.location:
|
||||
tags.append(["location", event.location])
|
||||
for cat in (event.categories or []):
|
||||
tags.append(["t", cat])
|
||||
|
||||
nostr_event = NostrEvent(
|
||||
pubkey=pubkey,
|
||||
created_at=int(time.time()),
|
||||
kind=31922,
|
||||
tags=tags,
|
||||
content=event.info,
|
||||
content=event.info or "",
|
||||
)
|
||||
nostr_event.id = nostr_event.event_id
|
||||
return nostr_event
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue