Compare commits
8 Commits
5da0a688a8
...
0.0.5
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d075b3904 | |||
| 28c6d9e964 | |||
| 628c3e97f5 | |||
| 4bf358734a | |||
| f5b3c2bb11 | |||
| 3da419fed5 | |||
| a417563ab5 | |||
| 1940e33bc7 |
@@ -315,15 +315,15 @@ def _seed_data():
|
|||||||
global _parents_next_id, _vets_next_id, _treatments_next_id
|
global _parents_next_id, _vets_next_id, _treatments_next_id
|
||||||
global _pets_next_id, _appointments_next_id
|
global _pets_next_id, _appointments_next_id
|
||||||
|
|
||||||
_parents[1] = Parent(id=1, name="Alice Johnson", email="alice@example.com", phone="555-0101", metadata=meta)
|
_parents[1] = Parent(id=1, name="Alice Johnson", email="alice@example.com", phone="5550101", metadata=meta)
|
||||||
_parents[2] = Parent(id=2, name="Bob Smith", email="bob@example.com", phone="555-0102", metadata=meta)
|
_parents[2] = Parent(id=2, name="Bob Smith", email="bob@example.com", phone="5550102", metadata=meta)
|
||||||
_parents[3] = Parent(id=3, name="Carol Williams", email="carol@example.com", phone="555-0103", metadata=meta)
|
_parents[3] = Parent(id=3, name="Carol Williams", email="carol@example.com", phone="5550103", metadata=meta)
|
||||||
_parents[4] = Parent(id=4, name="Dave Brown", email="dave@example.com", phone="555-0104", metadata=meta)
|
_parents[4] = Parent(id=4, name="Dave Brown", email="dave@example.com", phone="5550104", metadata=meta)
|
||||||
_parents_next_id = 5
|
_parents_next_id = 5
|
||||||
|
|
||||||
_vets[1] = Vet(id=1, name="Sarah Connor", specialty="Surgery", email="sarah@clinic.com", phone="555-0201", metadata=meta)
|
_vets[1] = Vet(id=1, name="Sarah Connor", specialty="Surgery", email="sarah@clinic.com", phone="5550201", metadata=meta)
|
||||||
_vets[2] = Vet(id=2, name="James Wilson", specialty="Dentistry", email="james@clinic.com", phone="555-0202", metadata=meta)
|
_vets[2] = Vet(id=2, name="James Wilson", specialty="Dentistry", email="james@clinic.com", phone="5550202", metadata=meta)
|
||||||
_vets[3] = Vet(id=3, name="Emily Davis", specialty="General Practice", email="emily@clinic.com", phone="555-0203", metadata=meta)
|
_vets[3] = Vet(id=3, name="Emily Davis", specialty="General Practice", email="emily@clinic.com", phone="5550203", metadata=meta)
|
||||||
_vets_next_id = 4
|
_vets_next_id = 4
|
||||||
|
|
||||||
_treatments[1] = Treatment(id=1, label="Annual Checkup", description="Full physical examination", metadata=meta)
|
_treatments[1] = Treatment(id=1, label="Annual Checkup", description="Full physical examination", metadata=meta)
|
||||||
|
|||||||
@@ -190,19 +190,19 @@ components:
|
|||||||
x-label: "Complications"
|
x-label: "Complications"
|
||||||
required: [noteType, surgeryType]
|
required: [noteType, surgeryType]
|
||||||
|
|
||||||
Call:
|
Action:
|
||||||
type: object
|
type: object
|
||||||
x-primary-key: _received_at
|
x-primary-key: _received_at
|
||||||
x-display-format: "{sound}"
|
x-display-format: "{action}"
|
||||||
x-list-columns: [sound]
|
x-list-columns: [action]
|
||||||
properties:
|
properties:
|
||||||
sound:
|
action:
|
||||||
type: string
|
type: string
|
||||||
enum: [woof, meow, coo]
|
enum: [wagging_tail, blep, stretching, showing_belly, barking, panting, purring, kneading, head_tilt, chirping, wing_flap]
|
||||||
x-label: "Sound"
|
x-label: "Action"
|
||||||
x-order: 1
|
x-order: 1
|
||||||
x-filterable: false
|
x-filterable: false
|
||||||
required: [sound]
|
required: [action]
|
||||||
|
|
||||||
Parent:
|
Parent:
|
||||||
type: object
|
type: object
|
||||||
@@ -223,6 +223,7 @@ components:
|
|||||||
x-description: "Parent's full name"
|
x-description: "Parent's full name"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
x-sortable: true
|
x-sortable: true
|
||||||
|
x-autocomplete: token
|
||||||
email:
|
email:
|
||||||
type: string
|
type: string
|
||||||
format: email
|
format: email
|
||||||
@@ -230,12 +231,14 @@ components:
|
|||||||
x-label: "Email"
|
x-label: "Email"
|
||||||
x-description: "Email address"
|
x-description: "Email address"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
|
x-autocomplete: email
|
||||||
phone:
|
phone:
|
||||||
type: string
|
type: string
|
||||||
x-order: 3
|
x-order: 3
|
||||||
x-label: "Phone"
|
x-label: "Phone"
|
||||||
x-description: "Contact phone number"
|
x-description: "Contact phone number"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
|
x-autocomplete: phone
|
||||||
metadata:
|
metadata:
|
||||||
$ref: '#/components/schemas/Metadata'
|
$ref: '#/components/schemas/Metadata'
|
||||||
x-order: 4
|
x-order: 4
|
||||||
@@ -261,12 +264,14 @@ components:
|
|||||||
x-description: "Veterinarian's full name"
|
x-description: "Veterinarian's full name"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
x-sortable: true
|
x-sortable: true
|
||||||
|
x-autocomplete: token
|
||||||
specialty:
|
specialty:
|
||||||
type: string
|
type: string
|
||||||
x-order: 2
|
x-order: 2
|
||||||
x-label: "Specialty"
|
x-label: "Specialty"
|
||||||
x-description: "Area of specialization"
|
x-description: "Area of specialization"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
|
x-autocomplete: token
|
||||||
email:
|
email:
|
||||||
type: string
|
type: string
|
||||||
format: email
|
format: email
|
||||||
@@ -274,6 +279,7 @@ components:
|
|||||||
x-label: "Email"
|
x-label: "Email"
|
||||||
x-description: "Email address"
|
x-description: "Email address"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
|
x-autocomplete: email
|
||||||
phone:
|
phone:
|
||||||
type: string
|
type: string
|
||||||
x-order: 4
|
x-order: 4
|
||||||
@@ -281,7 +287,7 @@ components:
|
|||||||
x-description: "Contact phone number"
|
x-description: "Contact phone number"
|
||||||
metadata:
|
metadata:
|
||||||
$ref: '#/components/schemas/Metadata'
|
$ref: '#/components/schemas/Metadata'
|
||||||
x-order: 4
|
x-order: 5
|
||||||
x-label: "Metadata"
|
x-label: "Metadata"
|
||||||
required: [id, name]
|
required: [id, name]
|
||||||
|
|
||||||
@@ -304,11 +310,14 @@ components:
|
|||||||
x-description: "Name of the treatment"
|
x-description: "Name of the treatment"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
x-sortable: true
|
x-sortable: true
|
||||||
|
x-autocomplete: token
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
x-order: 2
|
x-order: 2
|
||||||
x-label: "Description"
|
x-label: "Description"
|
||||||
x-description: "Detailed description of the treatment"
|
x-description: "Detailed description of the treatment"
|
||||||
|
x-filterable: true
|
||||||
|
x-autocomplete: text
|
||||||
metadata:
|
metadata:
|
||||||
$ref: '#/components/schemas/Metadata'
|
$ref: '#/components/schemas/Metadata'
|
||||||
x-order: 4
|
x-order: 4
|
||||||
@@ -334,6 +343,7 @@ components:
|
|||||||
x-description: "Name of the pet"
|
x-description: "Name of the pet"
|
||||||
x-filterable: true
|
x-filterable: true
|
||||||
x-sortable: true
|
x-sortable: true
|
||||||
|
x-autocomplete: token
|
||||||
species:
|
species:
|
||||||
type: string
|
type: string
|
||||||
enum: [dog, cat, bird]
|
enum: [dog, cat, bird]
|
||||||
@@ -381,7 +391,7 @@ components:
|
|||||||
x-filterable: true
|
x-filterable: true
|
||||||
metadata:
|
metadata:
|
||||||
$ref: '#/components/schemas/Metadata'
|
$ref: '#/components/schemas/Metadata'
|
||||||
x-order: 4
|
x-order: 8
|
||||||
x-label: "Metadata"
|
x-label: "Metadata"
|
||||||
required: [id, name, parents]
|
required: [id, name, parents]
|
||||||
|
|
||||||
@@ -1058,10 +1068,10 @@ paths:
|
|||||||
$ref: '#/components/responses/ValidationError'
|
$ref: '#/components/responses/ValidationError'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/components/responses/InternalServerError'
|
$ref: '#/components/responses/InternalServerError'
|
||||||
/pets/{id}/calls:
|
/pets/{id}/actions:
|
||||||
get:
|
get:
|
||||||
summary: Stream animal sounds via SSE, scoped to a pet's species
|
summary: Stream animal actions via SSE, scoped to a pet's species
|
||||||
operationId: stream_calls
|
operationId: stream_actions
|
||||||
x-sse: true
|
x-sse: true
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
@@ -1070,11 +1080,11 @@ paths:
|
|||||||
schema: {type: integer}
|
schema: {type: integer}
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: SSE stream of random animal sounds
|
description: SSE stream of random animal actions (behaviors)
|
||||||
content:
|
content:
|
||||||
text/event-stream:
|
text/event-stream:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Call'
|
$ref: '#/components/schemas/Action'
|
||||||
|
|
||||||
/appointments:
|
/appointments:
|
||||||
get:
|
get:
|
||||||
|
|||||||
@@ -369,8 +369,8 @@ def delete_appointment(id: int, response: Response):
|
|||||||
response.status_code = 204
|
response.status_code = 204
|
||||||
|
|
||||||
|
|
||||||
async def stream_calls(id: int):
|
async def stream_actions(id: int):
|
||||||
"""Stream animal sounds via SSE, scoped to a pet's species."""
|
"""Stream animal actions via SSE, scoped to a pet's species."""
|
||||||
try:
|
try:
|
||||||
pet = _get_pet(id)
|
pet = _get_pet(id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|||||||
@@ -2,16 +2,20 @@ import asyncio
|
|||||||
import random
|
import random
|
||||||
import json
|
import json
|
||||||
|
|
||||||
_sounds_by_species = {"dog": ["woof"], "cat": ["meow"], "bird": ["coo"]}
|
_behaviors_by_species = {
|
||||||
|
"dog": ["wagging_tail", "stretching", "showing_belly", "barking", "panting"],
|
||||||
|
"cat": ["blep", "stretching", "showing_belly", "purring", "kneading"],
|
||||||
|
"bird": ["head_tilt", "stretching", "chirping", "wing_flap"],
|
||||||
|
}
|
||||||
_subscribers: dict[int, list[asyncio.Queue]] = {}
|
_subscribers: dict[int, list[asyncio.Queue]] = {}
|
||||||
_worker_tasks: dict[int, asyncio.Task] = {}
|
_worker_tasks: dict[int, asyncio.Task] = {}
|
||||||
|
|
||||||
|
|
||||||
async def _sound_worker(pet_id: int, species: str):
|
async def _behavior_worker(pet_id: int, species: str):
|
||||||
sounds = _sounds_by_species.get(species, ["woof"])
|
behaviors = _behaviors_by_species.get(species, ["wagging_tail"])
|
||||||
while True:
|
while True:
|
||||||
sound = random.choice(sounds)
|
action = random.choice(behaviors)
|
||||||
data = json.dumps({"sound": sound})
|
data = json.dumps({"action": action})
|
||||||
queues = _subscribers.get(pet_id, [])
|
queues = _subscribers.get(pet_id, [])
|
||||||
for q in queues:
|
for q in queues:
|
||||||
await q.put(data)
|
await q.put(data)
|
||||||
@@ -22,7 +26,7 @@ def _ensure_worker(pet_id: int, species: str):
|
|||||||
if pet_id not in _worker_tasks or _worker_tasks[pet_id].done():
|
if pet_id not in _worker_tasks or _worker_tasks[pet_id].done():
|
||||||
_subscribers.setdefault(pet_id, [])
|
_subscribers.setdefault(pet_id, [])
|
||||||
_worker_tasks[pet_id] = asyncio.create_task(
|
_worker_tasks[pet_id] = asyncio.create_task(
|
||||||
_sound_worker(pet_id, species)
|
_behavior_worker(pet_id, species)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "openapi-first"
|
name = "openapi-first"
|
||||||
version = "0.0.4"
|
version = "0.0.5"
|
||||||
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
description = "Strict OpenAPI-first application bootstrap for FastAPI."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
Reference in New Issue
Block a user