Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""add survey_responses table

Revision ID: 7209ac6b2b3a
Revises: b7b2a3b424b8
Create Date: 2026-08-03 20:42:01.152034

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql


# revision identifiers, used by Alembic.
revision: str = '7209ac6b2b3a'
down_revision: Union[str, Sequence[str], None] = 'b7b2a3b424b8'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
op.create_table(
'survey_responses',
sa.Column('point_id', sa.String(), nullable=False),
sa.Column('answers', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
sa.PrimaryKeyConstraint('point_id'),
)


def downgrade() -> None:
"""Downgrade schema."""
op.drop_table('survey_responses')
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Add bot_active to maps

Revision ID: 82ba73de882d
Revises: 7209ac6b2b3a
Create Date: 2026-08-10 00:00:00.000000

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '82ba73de882d'
down_revision: Union[str, Sequence[str], None] = '7209ac6b2b3a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
op.add_column('maps', sa.Column('bot_active', sa.Boolean(), nullable=False, server_default=sa.false()))


def downgrade() -> None:
"""Downgrade schema."""
op.drop_column('maps', 'bot_active')
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Add bot_conversation_items table

Revision ID: c41d7f9a2e08
Revises: 82ba73de882d
Create Date: 2026-08-10 00:00:00.000000

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql


# revision identifiers, used by Alembic.
revision: str = 'c41d7f9a2e08'
down_revision: Union[str, Sequence[str], None] = '82ba73de882d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


# create_type=False so create_table does not try to create it a second time:
# the type is created once, explicitly, in upgrade()
bot_item_kind = postgresql.ENUM(
'start', 'media', 'location', 'single_choice', 'end', 'recovery', 'cancellation',
name='bot_item_kind',
create_type=False,
)


def upgrade() -> None:
"""Upgrade schema."""
bot_item_kind.create(op.get_bind(), checkfirst=True)

op.create_table(
'bot_conversation_items',
sa.Column('id', sa.String(), nullable=False),
sa.Column('map_id', sa.String(), nullable=False),
sa.Column('kind', bot_item_kind, nullable=False),
sa.Column('position', sa.Integer(), nullable=True),
sa.Column('prompt', sa.String(), nullable=False, server_default=''),
sa.Column('error_message', sa.String(), nullable=True),
sa.Column('options', postgresql.JSONB(astext_type=sa.Text()), nullable=False,
server_default=sa.text("'[]'::jsonb")),
sa.ForeignKeyConstraint(['map_id'], ['maps.id'], ),
sa.PrimaryKeyConstraint('id'),
)
op.create_index(op.f('ix_bot_conversation_items_id'), 'bot_conversation_items', ['id'])
op.create_index(op.f('ix_bot_conversation_items_map_id'), 'bot_conversation_items', ['map_id'])


def downgrade() -> None:
"""Downgrade schema."""
op.drop_index(op.f('ix_bot_conversation_items_map_id'), table_name='bot_conversation_items')
op.drop_index(op.f('ix_bot_conversation_items_id'), table_name='bot_conversation_items')
op.drop_table('bot_conversation_items')
bot_item_kind.drop(op.get_bind(), checkfirst=True)
39 changes: 24 additions & 15 deletions chatmap-api/bot/flow.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,63 @@
from abc import ABC, abstractmethod
from conversation_engine.event import EventName
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from store.bot_consumed_messages_store import BotConsumedMessagesStore
from store.bot_items_store import BotConversation
from store.bot_state_store import BotStateStore
from store.message_to_send_store import MessageToSendStore
from store.survey_responses_store import SurveyResponsesStore
from typing import Callable, Awaitable

import logging

logger = logging.getLogger(__name__)


class Language(Enum):
ES = "Español"
EN = "English"
PT = "Portugués"
FR = "Francais"

@classmethod
def default(cls):
return Language.ES


@dataclass
class BotFlowContext:
state_key: str
recipient: str
sender: str
answer: str
message_id: str
occurred_at: datetime
point_id: str | None
bot_state_store: BotStateStore

async def fetch_field(self, field: str) -> str | None:
return await self.bot_state_store.fetch_field(bot_state_key=self.state_key, field=field)


class BotFlow(ABC):
def __init__(self,
state: Enum,
language: Language,
conversation: BotConversation,
bot_state_store: BotStateStore,
message_to_send_store: MessageToSendStore
message_to_send_store: MessageToSendStore,
bot_consumed_messages_store: BotConsumedMessagesStore,
survey_responses_store: SurveyResponsesStore
):
self.state = state
self.language = language
# Every message this flow sends comes from here, configured by the
# map owner on the bot setup screen
self.conversation = conversation
self.bot_state_store = bot_state_store
self.message_to_send_store = message_to_send_store
self.bot_consumed_messages_store = bot_consumed_messages_store
self.survey_responses_store = survey_responses_store

@classmethod
@abstractmethod
async def create(
cls,
bot_state_key: str,
conversation: BotConversation,
bot_state_store: BotStateStore,
message_to_send_store: MessageToSendStore
message_to_send_store: MessageToSendStore,
bot_consumed_messages_store: BotConsumedMessagesStore,
survey_responses_store: SurveyResponsesStore
):
...

Expand Down
Loading