Skip to main content

stapel-classified

CI coverage pypi downloads python license llms.txt

Composite for location-bound classified ads: the stapel-shop composite (categories + listings + reviews) plus stapel-geo, stapel-search and stapel-moderation, and the cross-domain declarations no member is allowed to write — the listing search source, the listing/review/seller/chat_message moderation target policies and the marketplace reason taxonomy. Since 0.2.0 it also owns the one kind of state a composite may hold: the JOIN between a chat conversation and the listing it is about, because stapel-chat may not know what a listing is and stapel-listings may not know what a conversation is. Off that join it serves a conversation HEADER — the short listing card (title, price, primary image with CDN render metadata, and a state that says available, unavailable or gone, which is exactly the answer a public read cannot give) plus the counterparty's public seller card — and it enforces a user-to-user block at the one place a classified conversation begins, announcing at every boot whether that enforcement is live in this deployment.

Part of the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.

Install

pip install stapel-classified

At a glance

Fact Value
Version 0.2.1
Python >=3.11 (3.11, 3.12, 3.13, 3.14)
HTTP operations 3
Config axes 4
Usage surface 9
Extension points 3
Error codes 47
Fleet dependencies stapel-attributes · stapel-categories · stapel-cdn (optional) · stapel-chat (optional) · stapel-geo · stapel-listings · stapel-moderation · stapel-notifications (optional) · stapel-profiles (optional) · stapel-reviews · stapel-search

Documentation

OpenAPI · capabilities.json · llms.txt (for agents)

Assemble (one line)

pip install stapel-tools
stapel-assemble myads --libs classified
cd myads && make test

That expands classified through the STAPEL_LIBS requires closure and wires every member module into INSTALLED_APPS, requirements.txt, urls.py and CONFIG.MD, then runs the verify gates.

Manual wiring (no scaffold)

# settings.py
from stapel_classified import preset

INSTALLED_APPS = [
    # ... django/stapel-core baseline (incl. stapel_core.django.projections)
    *preset.INSTALLED_APPS,
]
for _k, _v in preset.SETTINGS_DEFAULTS.items():
    globals().setdefault(_k, _v)

# urls.py
from django.urls import include, path

from stapel_classified import preset

urlpatterns = [
    path(prefix, include(module)) for prefix, module in preset.URL_INCLUDES
]

Mount from preset.URL_INCLUDES rather than by hand: stapel-classified (its own conversation surface), stapel-categories and stapel-listings contribute only the v1/ segment and belong under <mod>/api/, while reviews, geo, search and moderation bake api/v1/ in themselves. Both end at /<mod>/api/v1/..., and getting it wrong is a stapel_core.mounts.E004 refusal to boot, not a cosmetic difference.

Config checklist (fill these, in the generated project's CONFIG.MD too)

Key Note
STAPEL_REVIEWS["TARGET_TYPES"] prefilled by the preset (targets listing)
STAPEL_SEARCH["SOURCES"] prefilled by the preset (the listing source)
STAPEL_MODERATION["TARGET_TYPES"] prefilled by the preset (listing pre-publication, review/seller/chat_message post)
STAPEL_CLASSIFIED["BLOCK_ENFORCEMENT"] auto — enforced where a block provider answers, and manage.py check says at every boot which state you are in. Set required once one does.
STAPEL_ACCESS["ROLES"] yours — the moderation console is staff-only; preset.RECOMMENDED_ACCESS_ROLES shows the shape
STAPEL_GDPR["DATA_OWNERS"] yours — must list "moderation", or erasure never closes over complaint data
STAPEL_MODERATION["APPEAL_URL_TEMPLATE"] yours — an empty appeal link is what DSA Art. 17 notices
STAPEL_SEARCH["BACKEND"] defaults to Postgres; name naive or meili if that is not your engine
STAPEL_LISTINGS["BASE_CURRENCY"] default USD — set your currency
STAPEL_GEO[...] geocoder provider/keys — see stapel-geo CONFIG.MD
listing coordinates lat/lon are LISTING fields (no projection needed) — see stapel-listings

Glue

Two members ship deliberately empty registries, because neither may know what a listing is. This package is the one place that knows both sides:

  • STAPEL_SEARCH["SOURCES"]["listing"]stapel_classified.search_sources.listing_source. Pulls documents through listings.search_documents / listings.search_export, invalidated by listing.published / listing.updated / listing.removed. Registering it is also what wires the subscribers — you write no signal handler. Facets are built from listings' features_search (stapel-search's declared lossy fallback: attribute range filters do not work until listings serves DAOs).
  • STAPEL_MODERATION["TARGET_TYPES"]listing (pre-publication: listing.submitted opens the case and nothing is public until the verdict) and review (post: live on arrival, a verdict is a takedown).

Coordinates need no glue at all: they are the listing's OWN fields (lat/lon on the listing), not a foreign aggregate.

The conversation header (0.2.0)

A chat in a classified marketplace is always ABOUT something and BETWEEN two identified people. Neither fact belongs to the messaging engine, so this composite owns the join and serves the header:

POST /classified/api/v1/conversations           bind a chat thread to a listing
GET  /classified/api/v1/conversations/{id}      one header
POST /classified/api/v1/conversations/contexts  a page of them, for the inbox

A header carries the short listing card — title, price, primary image with CDN render metadata, and a state of available / unavailable (sold, paused, expired) / gone, which is the answer a public listing read cannot give and exactly the case a buyer is most confused by — plus the counterparty's public seller card. Never more of a person than their public profile.

docs/frontend-contract.md is the document the default skins build against: every payload, every refusal, and the six things the fleet does not serve yet with what the UI does about each in the meantime.

License

MIT — see LICENSE.


This page is assembled by stapel-readme from docs/readme.md plus the contract artifacts in docs/. Edit the prose in docs/readme.md; the badges, facts and links above and below it are generated — do not hand-edit README.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stapel_classified-0.2.1.tar.gz (89.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stapel_classified-0.2.1-py3-none-any.whl (107.0 kB view details)

Uploaded Python 3

File details

Details for the file stapel_classified-0.2.1.tar.gz.

File metadata

  • Download URL: stapel_classified-0.2.1.tar.gz
  • Upload date:
  • Size: 89.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for stapel_classified-0.2.1.tar.gz
Algorithm Hash digest
SHA256 804e8b1cd247d8940064c7328fedb6bfca07e0600fe75d266d2969b42a9114c9
MD5 fe8de0a109f0ddda13d5d3000ce3108d
BLAKE2b-256 e935a69fb8addcd6c0a358e3ffbc80ee7024cf881d0cfd00f844bf29f2626a8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_classified-0.2.1.tar.gz:

Publisher: publish.yml on usestapel/stapel-classified

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stapel_classified-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for stapel_classified-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 317a250f101ea4fbd7beb477e88dbc2b51f8d6ce5851cba508affba4bc4889d3
MD5 b76dab019630e418a2c00851b1475830
BLAKE2b-256 151ac889904bca2bc74374cb8c77c232f5c32589b7f542569e9a8bf2b7555480

See more details on using hashes here.

Provenance

The following attestation bundles were made for stapel_classified-0.2.1-py3-none-any.whl:

Publisher: publish.yml on usestapel/stapel-classified

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.10.19

2 files

0.10.18

2 files

0.10.17

2 files

0.10.16

2 files

0.10.15

2 files

0.10.14

2 files

0.10.13

2 files

0.10.11

2 files

0.10.10

2 files

0.10.9

2 files

0.10.8

2 files

0.10.7

2 files

0.10.6

2 files

0.10.5

2 files

0.10.4

2 files

0.10.3

2 files

0.10.2

2 files

0.10.1

2 files

0.10.0

2 files

0.9.5

2 files

0.9.4

2 files

0.9.3

2 files

0.9.1

2 files

0.9.0

2 files

0.8.7

2 files

0.8.6

2 files

0.8.5

2 files

0.8.4

2 files

0.8.3

2 files

0.8.1

2 files

0.8.0

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.2

2 files

0.6.0

2 files

0.5.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.3

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.8

2 files

0.1.7

2 files

0.1.5

2 files

0.1.4

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page