mirror of
https://github.com/bellingcat/whisperbox-transcribe.git
synced 2026-06-08 03:28:35 +03:00
feat: add celery job queue
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
DATABASE_URI="postgresql://postgres:postgres@localhost:5432/whisper_api_test"
|
DATABASE_URI="postgresql://postgres:postgres@localhost:5432/whisper_api_test"
|
||||||
ENVIRONMENT="development"
|
ENVIRONMENT="development"
|
||||||
API_SECRET="foo"
|
API_SECRET="foo"
|
||||||
|
REDIS_URI="redis://localhost:6379/0"
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ Revises:
|
|||||||
Create Date: 2023-01-05 12:00:58.824773
|
Create Date: 2023-01-05 12:00:58.824773
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
@@ -26,9 +26,7 @@ def upgrade() -> None:
|
|||||||
sa.Enum("Create", "Error", "Success", name="jobstatus"),
|
sa.Enum("Create", "Error", "Success", name="jobstatus"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
),
|
),
|
||||||
sa.Column(
|
sa.Column("type", sa.Enum("Transcript", name="jobtype"), nullable=False),
|
||||||
"type", sa.Enum("Transcript", name="jobtype"), nullable=False
|
|
||||||
),
|
|
||||||
sa.Column(
|
sa.Column(
|
||||||
"created_at",
|
"created_at",
|
||||||
sa.DateTime(),
|
sa.DateTime(),
|
||||||
|
|||||||
@@ -4,13 +4,10 @@ from pydantic import BaseSettings
|
|||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
API_SECRET: str
|
||||||
DATABASE_URI: str
|
DATABASE_URI: str
|
||||||
ENVIRONMENT: str
|
ENVIRONMENT: str
|
||||||
API_SECRET: str
|
REDIS_URI: str
|
||||||
|
|
||||||
class Config:
|
|
||||||
env_file = ".env"
|
|
||||||
env_file_encoding = "utf-8"
|
|
||||||
|
|
||||||
|
|
||||||
if "ENVIRONMENT" in os.environ and os.environ["ENVIRONMENT"] == "test":
|
if "ENVIRONMENT" in os.environ and os.environ["ENVIRONMENT"] == "test":
|
||||||
|
|||||||
29
app/main.py
29
app/main.py
@@ -4,12 +4,11 @@ from uuid import UUID
|
|||||||
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Path
|
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Path
|
||||||
from pydantic import AnyHttpUrl, BaseModel
|
from pydantic import AnyHttpUrl, BaseModel
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.db.base import get_session
|
|
||||||
|
|
||||||
from app.db.dtos import Job, JobStatus, JobType
|
import app.db.dtos as dtos
|
||||||
import app.db.models as models
|
import app.db.models as models
|
||||||
|
from app.db.base import get_session
|
||||||
from .security import authenticate_api_key
|
from app.utils.security import authenticate_api_key
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@@ -25,29 +24,35 @@ class TranscriptPayload(BaseModel):
|
|||||||
url: AnyHttpUrl
|
url: AnyHttpUrl
|
||||||
|
|
||||||
|
|
||||||
@api_router.post("/transcripts", response_model=Job)
|
@api_router.post("/transcripts", response_model=dtos.Job)
|
||||||
def create_transcript(
|
def create_transcript(
|
||||||
payload: TranscriptPayload, session: Session = Depends(get_session)
|
payload: TranscriptPayload, session: Session = Depends(get_session)
|
||||||
) -> models.Job:
|
) -> models.Job:
|
||||||
job = models.Job(url=payload.url, status=JobStatus.Create, type=JobType.Transcript)
|
job = models.Job(
|
||||||
|
url=payload.url, status=dtos.JobStatus.Create, type=dtos.JobType.Transcript
|
||||||
|
)
|
||||||
session.add(job)
|
session.add(job)
|
||||||
session.flush()
|
session.flush()
|
||||||
return job
|
return job
|
||||||
|
|
||||||
|
|
||||||
@api_router.get("/transcripts", response_model=List[Job])
|
@api_router.get("/transcripts", response_model=List[dtos.Job])
|
||||||
def get_transcripts(session: Session = Depends(get_session)) -> List[models.Job]:
|
def get_transcripts(session: Session = Depends(get_session)) -> List[models.Job]:
|
||||||
return session.query(models.Job).filter(models.Job.type == JobType.Transcript).all()
|
return (
|
||||||
|
session.query(models.Job)
|
||||||
|
.filter(models.Job.type == dtos.JobType.Transcript)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@api_router.get("/transcripts/{id}", response_model=Job)
|
@api_router.get("/transcripts/{id}", response_model=dtos.Job)
|
||||||
def get_transcript(
|
def get_transcript(
|
||||||
id: UUID = Path(), session: Session = Depends(get_session)
|
id: UUID = Path(), session: Session = Depends(get_session)
|
||||||
) -> Optional[Job]:
|
) -> Optional[dtos.Job]:
|
||||||
job = (
|
job = (
|
||||||
session.query(models.Job)
|
session.query(models.Job)
|
||||||
.filter(models.Job.id == id)
|
.filter(models.Job.id == id)
|
||||||
.filter(models.Job.type == JobType.Transcript)
|
.filter(models.Job.type == dtos.JobType.Transcript)
|
||||||
.one_or_none()
|
.one_or_none()
|
||||||
)
|
)
|
||||||
if not job:
|
if not job:
|
||||||
@@ -60,7 +65,7 @@ def delete_transcript(
|
|||||||
id: UUID = Path(), session: Session = Depends(get_session)
|
id: UUID = Path(), session: Session = Depends(get_session)
|
||||||
) -> None:
|
) -> None:
|
||||||
session.query(models.Job).filter(models.Job.id == id).filter(
|
session.query(models.Job).filter(models.Job.id == id).filter(
|
||||||
models.Job.type == JobType.Transcript
|
models.Job.type == dtos.JobType.Transcript
|
||||||
).delete()
|
).delete()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
from fastapi.testclient import TestClient
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from app.main import app
|
|
||||||
import app.db.models as models
|
|
||||||
|
|
||||||
client = TestClient(app)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_task(db_session: Session) -> None:
|
|
||||||
jobs = db_session.query(models.Job).all()
|
|
||||||
assert len(jobs) == 0
|
|
||||||
0
app/utils/__init__.py
Normal file
0
app/utils/__init__.py
Normal file
7
app/worker.py
Normal file
7
app/worker.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
from .config import settings
|
||||||
|
|
||||||
|
celery = Celery(__name__)
|
||||||
|
|
||||||
|
celery.conf.broker_url = settings.REDIS_URI
|
||||||
@@ -11,5 +11,3 @@ RUN pip install -U pip
|
|||||||
RUN pip install .[test]
|
RUN pip install .[test]
|
||||||
|
|
||||||
# The source code is mounted as a volume at /code, no need to copy.
|
# The source code is mounted as a volume at /code, no need to copy.
|
||||||
|
|
||||||
ENTRYPOINT ["bash", "./app/start.sh"]
|
|
||||||
|
|||||||
@@ -1,27 +1,12 @@
|
|||||||
version: "3.8"
|
version: "3.8"
|
||||||
|
|
||||||
services:
|
x-app-variables: &app-variables
|
||||||
app:
|
API_SECRET: a_very_secret_token
|
||||||
container_name: whisper_api_app
|
DATABASE_URI: postgresql://postgres:postgres@postgres/whisper_api
|
||||||
build:
|
ENVIRONMENT: development
|
||||||
context: .
|
REDIS_URI: redis://redis:6379/0
|
||||||
dockerfile: dev.Dockerfile
|
|
||||||
environment:
|
|
||||||
DATABASE_URI: postgresql://postgres:postgres@postgres/whisper_api
|
|
||||||
ENVIRONMENT: development
|
|
||||||
API_SECRET: foobar
|
|
||||||
ports:
|
|
||||||
- "8000:80"
|
|
||||||
networks:
|
|
||||||
- app
|
|
||||||
volumes:
|
|
||||||
- ./:/code
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_started
|
|
||||||
|
|
||||||
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
container_name: whisper_api_postgres
|
container_name: whisper_api_postgres
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
@@ -44,6 +29,53 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
container_name: whisper_api_redis
|
container_name: whisper_api_redis
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
app:
|
||||||
|
container_name: whisper_api_app
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: dev.Dockerfile
|
||||||
|
command: bash ./app/start.sh
|
||||||
|
environment: *app-variables
|
||||||
|
ports:
|
||||||
|
- "8000:80"
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
volumes:
|
||||||
|
- ./:/code
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: dev.Dockerfile
|
||||||
|
container_name: whisper_api_worker
|
||||||
|
command: celery --app=app.worker.celery worker --loglevel=info
|
||||||
|
volumes:
|
||||||
|
- ./:/code
|
||||||
|
environment: *app-variables
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
flower:
|
||||||
|
container_name: whisper_api_flower
|
||||||
|
image: mher/flower
|
||||||
|
command: celery --broker redis://redis:6379/0 flower --port=5555
|
||||||
|
ports:
|
||||||
|
- 5555:5555
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
networks:
|
networks:
|
||||||
- app
|
- app
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ version = "0.0.1"
|
|||||||
|
|
||||||
dependencies=[
|
dependencies=[
|
||||||
"alembic ==1.9.0",
|
"alembic ==1.9.0",
|
||||||
|
"celery[redis] ==5.2.7",
|
||||||
"fastapi ==0.88.0",
|
"fastapi ==0.88.0",
|
||||||
"psycopg2 ==2.9.5",
|
"psycopg2 ==2.9.5",
|
||||||
"python-dotenv ==0.21.0",
|
"python-dotenv ==0.21.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user