mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
feat(native-connector): added google maps places & reviews scrapers
This commit is contained in:
parent
cc99bc4cae
commit
7185079bd6
114 changed files with 4688 additions and 425 deletions
|
|
@ -46,9 +46,9 @@ from app.db import (
|
|||
NewChatMessage,
|
||||
NewChatMessageRole,
|
||||
NewChatThread,
|
||||
Workspace,
|
||||
TokenUsage,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.routes import new_chat_routes
|
||||
from app.services.token_tracking_service import TurnTokenAccumulator
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ from app.db import (
|
|||
NewChatMessage,
|
||||
NewChatMessageRole,
|
||||
NewChatThread,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.services.new_streaming_service import VercelStreamingService
|
||||
from app.tasks.chat import persistence as persistence_module
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ from app.db import (
|
|||
NewChatMessage,
|
||||
NewChatMessageRole,
|
||||
NewChatThread,
|
||||
Workspace,
|
||||
TokenUsage,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.services.token_tracking_service import TurnTokenAccumulator
|
||||
from app.tasks.chat import persistence as persistence_module
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
from app.auth.context import AuthContext
|
||||
from app.db import (
|
||||
ChatVisibility,
|
||||
User,
|
||||
Workspace,
|
||||
WorkspaceMembership,
|
||||
WorkspaceRole,
|
||||
User,
|
||||
)
|
||||
from app.routes import new_chat_routes
|
||||
from app.schemas.new_chat import (
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ from app.config import config
|
|||
from app.db import (
|
||||
SearchSourceConnector,
|
||||
SearchSourceConnectorType,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.utils.oauth_security import OAuthStateManager
|
||||
|
||||
|
|
@ -57,8 +57,7 @@ async def test_callback_with_error_param_redirects_to_denied_page(
|
|||
assert response.status_code in {302, 303, 307}
|
||||
location = response.headers["location"]
|
||||
assert (
|
||||
f"/dashboard/{db_workspace.id}/connectors/callback?"
|
||||
"error=composio_oauth_denied"
|
||||
f"/dashboard/{db_workspace.id}/connectors/callback?error=composio_oauth_denied"
|
||||
) in location
|
||||
|
||||
connectors = await _drive_connectors(
|
||||
|
|
|
|||
|
|
@ -320,9 +320,7 @@ class TestDocumentSearchability:
|
|||
doc_ids = resp.json()["document_ids"]
|
||||
cleanup_doc_ids.extend(doc_ids)
|
||||
|
||||
await poll_document_status(
|
||||
client, headers, doc_ids, workspace_id=workspace_id
|
||||
)
|
||||
await poll_document_status(client, headers, doc_ids, workspace_id=workspace_id)
|
||||
|
||||
search_resp = await client.get(
|
||||
"/api/v1/documents/search",
|
||||
|
|
|
|||
|
|
@ -219,9 +219,7 @@ class TestDocumentProcessingNotification:
|
|||
doc_ids = resp.json()["document_ids"]
|
||||
cleanup_doc_ids.extend(doc_ids)
|
||||
|
||||
await poll_document_status(
|
||||
client, headers, doc_ids, workspace_id=workspace_id
|
||||
)
|
||||
await poll_document_status(client, headers, doc_ids, workspace_id=workspace_id)
|
||||
|
||||
notifications = await get_notifications(
|
||||
client,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ from app.db import (
|
|||
DocumentType,
|
||||
SearchSourceConnector,
|
||||
SearchSourceConnectorType,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
|
||||
EMBEDDING_DIM = app_config.embedding_model_instance.dimension
|
||||
|
|
@ -275,9 +275,7 @@ async def seed_connector(
|
|||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
space = Workspace(
|
||||
name=f"{name_prefix} {uuid.uuid4().hex[:6]}", user_id=user.id
|
||||
)
|
||||
space = Workspace(name=f"{name_prefix} {uuid.uuid4().hex[:6]}", user_id=user.id)
|
||||
session.add(space)
|
||||
await session.flush()
|
||||
space_id = space.id
|
||||
|
|
|
|||
|
|
@ -119,9 +119,7 @@ async def test_reindex_updates_content(db_session, db_workspace, db_user, mocker
|
|||
|
||||
|
||||
@pytest.mark.usefixtures("patched_embed_texts", "patched_chunk_text")
|
||||
async def test_reindex_updates_content_hash(
|
||||
db_session, db_workspace, db_user, mocker
|
||||
):
|
||||
async def test_reindex_updates_content_hash(db_session, db_workspace, db_user, mocker):
|
||||
"""Content hash is recomputed after reindexing with new source markdown."""
|
||||
adapter = UploadDocumentAdapter(db_session)
|
||||
await adapter.index(
|
||||
|
|
|
|||
|
|
@ -55,9 +55,7 @@ async def test_edit_keeps_unchanged_rows_and_embeds_only_the_new_text(
|
|||
patched_embed_texts,
|
||||
):
|
||||
service = IndexingPipelineService(session=db_session)
|
||||
doc_v1 = make_connector_document(
|
||||
workspace_id=db_workspace.id, source_markdown=_V1
|
||||
)
|
||||
doc_v1 = make_connector_document(workspace_id=db_workspace.id, source_markdown=_V1)
|
||||
document = await _index(service, doc_v1)
|
||||
|
||||
ids_v1 = {c.content: c.id for c in await _load_chunks(db_session, document.id)}
|
||||
|
|
@ -100,9 +98,7 @@ async def test_head_insert_shifts_positions_without_new_rows_for_old_text(
|
|||
service = IndexingPipelineService(session=db_session)
|
||||
document = await _index(
|
||||
service,
|
||||
make_connector_document(
|
||||
workspace_id=db_workspace.id, source_markdown=_V1
|
||||
),
|
||||
make_connector_document(workspace_id=db_workspace.id, source_markdown=_V1),
|
||||
)
|
||||
ids_v1 = {c.content: c.id for c in await _load_chunks(db_session, document.id)}
|
||||
|
||||
|
|
@ -136,9 +132,7 @@ async def test_removed_paragraph_is_deleted_and_order_compacts(
|
|||
service = IndexingPipelineService(session=db_session)
|
||||
document = await _index(
|
||||
service,
|
||||
make_connector_document(
|
||||
workspace_id=db_workspace.id, source_markdown=_V1
|
||||
),
|
||||
make_connector_document(workspace_id=db_workspace.id, source_markdown=_V1),
|
||||
)
|
||||
ids_v1 = {c.content: c.id for c in await _load_chunks(db_session, document.id)}
|
||||
|
||||
|
|
@ -171,9 +165,7 @@ async def test_kill_switch_falls_back_to_full_replace(
|
|||
service = IndexingPipelineService(session=db_session)
|
||||
document = await _index(
|
||||
service,
|
||||
make_connector_document(
|
||||
workspace_id=db_workspace.id, source_markdown=_V1
|
||||
),
|
||||
make_connector_document(workspace_id=db_workspace.id, source_markdown=_V1),
|
||||
)
|
||||
ids_v1 = {c.id for c in await _load_chunks(db_session, document.id)}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ from app.db import (
|
|||
DocumentType,
|
||||
DocumentVersion,
|
||||
Folder,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
|
|||
|
|
@ -59,9 +59,7 @@ async def test_legacy_composio_gmail_doc_migrated_in_db(
|
|||
assert reloaded.document_type == DocumentType.GOOGLE_GMAIL_CONNECTOR
|
||||
|
||||
|
||||
async def test_no_legacy_doc_is_noop(
|
||||
db_session, db_workspace, make_connector_document
|
||||
):
|
||||
async def test_no_legacy_doc_is_noop(db_session, db_workspace, make_connector_document):
|
||||
"""When no legacy document exists, migrate_legacy_docs does nothing."""
|
||||
connector_doc = make_connector_document(
|
||||
document_type=DocumentType.GOOGLE_CALENDAR_CONNECTOR,
|
||||
|
|
|
|||
|
|
@ -225,9 +225,7 @@ async def test_updated_at_advances_when_title_only_changes(
|
|||
db_session, db_workspace, make_connector_document
|
||||
):
|
||||
"""updated_at advances even when only the title changes."""
|
||||
original = make_connector_document(
|
||||
workspace_id=db_workspace.id, title="Old Title"
|
||||
)
|
||||
original = make_connector_document(workspace_id=db_workspace.id, title="Old Title")
|
||||
service = IndexingPipelineService(session=db_session)
|
||||
|
||||
first = await service.prepare_for_indexing([original])
|
||||
|
|
@ -238,9 +236,7 @@ async def test_updated_at_advances_when_title_only_changes(
|
|||
)
|
||||
updated_at_v1 = result.scalars().first().updated_at
|
||||
|
||||
renamed = make_connector_document(
|
||||
workspace_id=db_workspace.id, title="New Title"
|
||||
)
|
||||
renamed = make_connector_document(workspace_id=db_workspace.id, title="New Title")
|
||||
await service.prepare_for_indexing([renamed])
|
||||
|
||||
result = await db_session.execute(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import pytest
|
|||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.persistence import Notification
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
@ -47,9 +47,7 @@ async def test_comment_reply_truncates_long_preview(
|
|||
db_session: AsyncSession, db_user: User, db_workspace: Workspace
|
||||
):
|
||||
"""A long comment preview is truncated in the reply message."""
|
||||
notification = await _notify(
|
||||
db_session, db_user, db_workspace, preview="y" * 150
|
||||
)
|
||||
notification = await _notify(db_session, db_user, db_workspace, preview="y" * 150)
|
||||
|
||||
assert notification.message == "y" * 100 + "..."
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from __future__ import annotations
|
|||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Workspace, User
|
||||
from app.db import User, Workspace
|
||||
from app.notifications.service import NotificationService
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
|
@ -47,9 +47,7 @@ async def test_new_mention_truncates_long_preview(
|
|||
db_session: AsyncSession, db_user: User, db_workspace: Workspace
|
||||
):
|
||||
"""A long comment preview is truncated in the mention message."""
|
||||
notification = await _notify(
|
||||
db_session, db_user, db_workspace, preview="x" * 150
|
||||
)
|
||||
notification = await _notify(db_session, db_user, db_workspace, preview="x" * 150)
|
||||
|
||||
assert notification.message == "x" * 100 + "..."
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
from app.app import app, limiter
|
||||
from app.auth.context import AuthContext
|
||||
from app.config import config as app_config
|
||||
from app.db import Workspace, User, get_async_session
|
||||
from app.db import User, Workspace, get_async_session
|
||||
from app.podcasts.persistence import Podcast, PodcastStatus
|
||||
from app.podcasts.schemas import (
|
||||
DurationTarget,
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ async def test_cancel_from_a_terminal_state_conflicts(
|
|||
assert resp.status_code == 409
|
||||
|
||||
|
||||
async def test_cancel_of_a_regeneration_is_rejected(
|
||||
client, db_workspace, make_podcast
|
||||
):
|
||||
async def test_cancel_of_a_regeneration_is_rejected(client, db_workspace, make_podcast):
|
||||
# Cancelling here would destroy a playable episode; reverting the
|
||||
# regeneration is the way back.
|
||||
podcast = await make_podcast(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import pytest_asyncio
|
|||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import config as app_config
|
||||
from app.db import Chunk, Document, DocumentType, Workspace, User
|
||||
from app.db import Chunk, Document, DocumentType, User, Workspace
|
||||
|
||||
EMBEDDING_DIM = app_config.embedding_model_instance.dimension
|
||||
DUMMY_EMBEDDING = [0.1] * EMBEDDING_DIM
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ from app.auth.context import AuthContext
|
|||
from app.db import (
|
||||
SearchSourceConnector,
|
||||
SearchSourceConnectorType,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.routes.search_source_connectors_routes import index_connector_content
|
||||
from app.routes.workspaces_routes import create_default_roles_and_membership
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import pytest_asyncio
|
|||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import Document, DocumentType, DocumentVersion, Workspace, User
|
||||
from app.db import Document, DocumentType, DocumentVersion, User, Workspace
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ from app.auth.context import AuthContext
|
|||
from app.db import (
|
||||
SearchSourceConnector,
|
||||
SearchSourceConnectorType,
|
||||
Workspace,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from app.routes.obsidian_plugin_routes import (
|
||||
obsidian_connect,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from fastapi import HTTPException
|
|||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.auth.context import AuthContext
|
||||
from app.db import PersonalAccessToken, Workspace, User
|
||||
from app.db import PersonalAccessToken, User, Workspace
|
||||
from app.users import allow_any_principal, require_session_context
|
||||
from app.utils.rbac import check_workspace_access
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from fastapi import HTTPException
|
|||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.auth.context import AuthContext
|
||||
from app.db import PersonalAccessToken, Workspace, User
|
||||
from app.db import PersonalAccessToken, User, Workspace
|
||||
from app.routes.workspaces_routes import create_default_roles_and_membership
|
||||
from app.utils.rbac import check_workspace_access, get_allowed_read_space_ids
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue