deepdata: core product code (backend, frontend, runtime, deepflow module)
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
from .cleaning_task_repository import CleaningTaskRepository
|
||||
from .cleaning_template_repository import CleaningTemplateRepository
|
||||
from .cleaning_result_repository import CleaningResultRepository
|
||||
from .operator_instance_repository import OperatorInstanceRepository
|
||||
|
||||
__all__ = [
|
||||
"CleaningTaskRepository",
|
||||
"CleaningTemplateRepository",
|
||||
"CleaningResultRepository",
|
||||
"OperatorInstanceRepository",
|
||||
]
|
||||
@@ -0,0 +1,110 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, func, Integer
|
||||
from app.db.models.cleaning import CleaningResult
|
||||
from app.module.cleaning.schema import CleaningResultDto
|
||||
|
||||
|
||||
class CleaningResultRepository:
|
||||
"""Repository for cleaning result operations"""
|
||||
|
||||
def __init__(self, model=None):
|
||||
self.model = model if model else CleaningResult
|
||||
|
||||
async def find_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str,
|
||||
status: Optional[str] = None
|
||||
) -> List[CleaningResultDto]:
|
||||
"""Query results by instance ID"""
|
||||
query = select(self.model).where(self.model.instance_id == instance_id)
|
||||
|
||||
if status:
|
||||
query = query.where(self.model.status == status)
|
||||
|
||||
result = await db.execute(query)
|
||||
results = result.scalars().all()
|
||||
|
||||
return [
|
||||
CleaningResultDto(
|
||||
instance_id=res.instance_id,
|
||||
src_file_id=res.src_file_id,
|
||||
dest_file_id=res.dest_file_id,
|
||||
src_name=res.src_name,
|
||||
dest_name=res.dest_name,
|
||||
src_type=res.src_type,
|
||||
dest_type=res.dest_type,
|
||||
src_size=res.src_size,
|
||||
dest_size=res.dest_size,
|
||||
status=res.status,
|
||||
result=res.result
|
||||
)
|
||||
for res in results
|
||||
]
|
||||
|
||||
async def count_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str
|
||||
) -> tuple[int, int]:
|
||||
"""Count results by instance ID (completed, failed) using single SQL"""
|
||||
query = select(
|
||||
func.sum(func.cast(self.model.status == "COMPLETED", Integer)).label("completed"),
|
||||
func.sum(func.cast(self.model.status == "FAILED", Integer)).label("failed"),
|
||||
).where(self.model.instance_id == instance_id)
|
||||
result = await db.execute(query)
|
||||
row = result.one()
|
||||
return (row.completed or 0, row.failed or 0)
|
||||
|
||||
async def count_total_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str
|
||||
) -> int:
|
||||
"""Count total results by instance ID using efficient SQL COUNT"""
|
||||
query = select(func.count()).select_from(self.model).where(
|
||||
self.model.instance_id == instance_id
|
||||
)
|
||||
result = await db.scalar(query)
|
||||
return result or 0
|
||||
|
||||
async def batch_count_by_instance_ids(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_ids: list[str]
|
||||
) -> dict[str, tuple[int, int, int]]:
|
||||
"""Batch count completed/failed/total for multiple instance IDs using single SQL.
|
||||
|
||||
Returns dict: {instance_id: (completed, failed, total)}
|
||||
"""
|
||||
if not instance_ids:
|
||||
return {}
|
||||
|
||||
query = select(
|
||||
self.model.instance_id,
|
||||
func.sum(func.cast(self.model.status == "COMPLETED", Integer)).label("completed"),
|
||||
func.sum(func.cast(self.model.status == "FAILED", Integer)).label("failed"),
|
||||
func.count().label("total"),
|
||||
).where(self.model.instance_id.in_(instance_ids)).group_by(self.model.instance_id)
|
||||
|
||||
result = await db.execute(query)
|
||||
return {
|
||||
row.instance_id: (row.completed or 0, row.failed or 0, row.total or 0)
|
||||
for row in result.all()
|
||||
}
|
||||
|
||||
async def delete_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str,
|
||||
status: Optional[str] = None
|
||||
) -> None:
|
||||
"""Delete results by instance ID"""
|
||||
query = delete(self.model).where(self.model.instance_id == instance_id)
|
||||
|
||||
if status:
|
||||
query = query.where(self.model.status == status)
|
||||
|
||||
await db.execute(query)
|
||||
await db.flush()
|
||||
@@ -0,0 +1,161 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, func
|
||||
from app.db.models.cleaning import CleaningTask
|
||||
from app.module.cleaning.schema import CleaningTaskDto
|
||||
|
||||
|
||||
class CleaningTaskRepository:
|
||||
"""Repository for cleaning task operations"""
|
||||
|
||||
def __init__(self, model=None):
|
||||
self.model = model if model else CleaningTask
|
||||
|
||||
async def find_tasks(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
status: Optional[str] = None,
|
||||
keyword: Optional[str] = None,
|
||||
page: Optional[int] = None,
|
||||
size: Optional[int] = None
|
||||
) -> List[CleaningTaskDto]:
|
||||
"""Query cleaning tasks"""
|
||||
query = select(self.model)
|
||||
|
||||
if status:
|
||||
query = query.where(self.model.status == status)
|
||||
|
||||
if keyword:
|
||||
keyword_pattern = f"%{keyword}%"
|
||||
query = query.where(
|
||||
self.model.name.ilike(keyword_pattern) | self.model.description.ilike(keyword_pattern)
|
||||
)
|
||||
|
||||
query = query.order_by(self.model.created_at.desc())
|
||||
|
||||
if page is not None and size is not None:
|
||||
offset = max(page * size, 0)
|
||||
query = query.offset(offset).limit(size)
|
||||
|
||||
result = await db.execute(query)
|
||||
tasks = result.scalars().all()
|
||||
|
||||
return [
|
||||
CleaningTaskDto(
|
||||
id=task.id,
|
||||
name=task.name,
|
||||
description=task.description,
|
||||
status=task.status,
|
||||
src_dataset_id=task.src_dataset_id,
|
||||
src_dataset_name=task.src_dataset_name,
|
||||
dest_dataset_id=task.dest_dataset_id,
|
||||
dest_dataset_name=task.dest_dataset_name,
|
||||
before_size=task.before_size,
|
||||
after_size=task.after_size,
|
||||
file_count=task.file_count,
|
||||
retry_count=task.retry_count,
|
||||
started_at=task.started_at,
|
||||
finished_at=task.finished_at,
|
||||
created_at=task.created_at
|
||||
)
|
||||
for task in tasks
|
||||
]
|
||||
|
||||
async def find_task_by_id(self, db: AsyncSession, task_id: str) -> Optional[CleaningTaskDto]:
|
||||
"""Query task by ID"""
|
||||
query = select(self.model).where(self.model.id == task_id)
|
||||
result = await db.execute(query)
|
||||
task = result.scalar_one_or_none()
|
||||
|
||||
if not task:
|
||||
return None
|
||||
|
||||
return CleaningTaskDto(
|
||||
id=task.id,
|
||||
name=task.name,
|
||||
description=task.description,
|
||||
status=task.status,
|
||||
src_dataset_id=task.src_dataset_id,
|
||||
src_dataset_name=task.src_dataset_name,
|
||||
dest_dataset_id=task.dest_dataset_id,
|
||||
dest_dataset_name=task.dest_dataset_name,
|
||||
before_size=task.before_size,
|
||||
after_size=task.after_size,
|
||||
file_count=task.file_count,
|
||||
retry_count=task.retry_count,
|
||||
started_at=task.started_at,
|
||||
finished_at=task.finished_at,
|
||||
created_at=task.created_at
|
||||
)
|
||||
|
||||
async def insert_task(self, db: AsyncSession, task: CleaningTaskDto) -> None:
|
||||
"""Insert new task"""
|
||||
from app.db.models.cleaning import CleaningTask as CleaningTaskModel
|
||||
|
||||
db_task = CleaningTaskModel(
|
||||
id=task.id,
|
||||
name=task.name,
|
||||
description=task.description,
|
||||
status=task.status,
|
||||
src_dataset_id=task.src_dataset_id,
|
||||
src_dataset_name=task.src_dataset_name,
|
||||
dest_dataset_id=task.dest_dataset_id,
|
||||
dest_dataset_name=task.dest_dataset_name,
|
||||
before_size=task.before_size,
|
||||
after_size=task.after_size,
|
||||
file_count=task.file_count,
|
||||
retry_count=task.retry_count
|
||||
)
|
||||
db.add(db_task)
|
||||
await db.flush()
|
||||
|
||||
async def update_task(self, db: AsyncSession, task: CleaningTaskDto) -> None:
|
||||
"""Update task"""
|
||||
query = select(CleaningTask).where(CleaningTask.id == task.id)
|
||||
result = await db.execute(query)
|
||||
db_task = result.scalar_one_or_none()
|
||||
|
||||
if db_task:
|
||||
if task.status:
|
||||
db_task.status = task.status
|
||||
if task.started_at:
|
||||
db_task.started_at = task.started_at
|
||||
if task.finished_at:
|
||||
db_task.finished_at = task.finished_at
|
||||
if task.retry_count is not None:
|
||||
db_task.retry_count = task.retry_count
|
||||
|
||||
await db.flush()
|
||||
|
||||
async def delete_task_by_id(self, db: AsyncSession, task_id: str) -> None:
|
||||
"""Delete task by ID"""
|
||||
query = delete(self.model).where(self.model.id == task_id)
|
||||
await db.execute(query)
|
||||
await db.flush()
|
||||
|
||||
async def count_tasks(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
status: Optional[str] = None,
|
||||
keyword: Optional[str] = None
|
||||
) -> int:
|
||||
"""Count cleaning tasks using SQL COUNT"""
|
||||
query = select(func.count()).select_from(self.model)
|
||||
|
||||
if status:
|
||||
query = query.where(self.model.status == status)
|
||||
|
||||
if keyword:
|
||||
keyword_pattern = f"%{keyword}%"
|
||||
query = query.where(
|
||||
self.model.name.ilike(keyword_pattern) | self.model.description.ilike(keyword_pattern)
|
||||
)
|
||||
|
||||
result = await db.scalar(query)
|
||||
return result or 0
|
||||
|
||||
async def is_name_exist(self, db: AsyncSession, name: str) -> bool:
|
||||
"""Check if task name exists"""
|
||||
query = select(func.count()).select_from(self.model).where(self.model.name == name)
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one() > 0 if result else False
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, func
|
||||
from app.db.models.cleaning import CleaningTemplate
|
||||
|
||||
|
||||
class CleaningTemplateRepository:
|
||||
"""Repository for cleaning template operations"""
|
||||
|
||||
def __init__(self, model=None):
|
||||
self.model = model if model else CleaningTemplate
|
||||
|
||||
async def find_all_templates(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
keyword: Optional[str] = None,
|
||||
page: Optional[int] = None,
|
||||
size: Optional[int] = None,
|
||||
) -> List[CleaningTemplate]:
|
||||
"""Query all templates with optional pagination"""
|
||||
query = select(self.model)
|
||||
|
||||
if keyword:
|
||||
keyword_pattern = f"%{keyword}%"
|
||||
query = query.where(
|
||||
self.model.name.ilike(keyword_pattern)
|
||||
| self.model.description.ilike(keyword_pattern)
|
||||
)
|
||||
|
||||
query = query.order_by(self.model.created_at.desc())
|
||||
|
||||
if page is not None and size is not None:
|
||||
offset = page * size
|
||||
query = query.offset(offset).limit(size)
|
||||
|
||||
result = await db.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def count_templates(
|
||||
self, db: AsyncSession, keyword: Optional[str] = None
|
||||
) -> int:
|
||||
"""Count templates"""
|
||||
query = select(func.count()).select_from(self.model)
|
||||
|
||||
if keyword:
|
||||
keyword_pattern = f"%{keyword}%"
|
||||
query = query.where(
|
||||
self.model.name.ilike(keyword_pattern)
|
||||
| self.model.description.ilike(keyword_pattern)
|
||||
)
|
||||
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one()
|
||||
|
||||
async def find_template_by_id(
|
||||
self, db: AsyncSession, template_id: str
|
||||
) -> Optional[CleaningTemplate]:
|
||||
"""Query template by ID"""
|
||||
query = select(self.model).where(self.model.id == template_id)
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def insert_template(
|
||||
self, db: AsyncSession, template: CleaningTemplate
|
||||
) -> None:
|
||||
"""Insert new template"""
|
||||
db.add(template)
|
||||
await db.flush()
|
||||
|
||||
async def update_template(
|
||||
self, db: AsyncSession, template: CleaningTemplate
|
||||
) -> None:
|
||||
"""Update template"""
|
||||
query = select(self.model).where(self.model.id == template.id)
|
||||
result = await db.execute(query)
|
||||
db_template = result.scalar_one_or_none()
|
||||
|
||||
if db_template:
|
||||
db_template.name = template.name
|
||||
db_template.description = template.description
|
||||
await db.flush()
|
||||
|
||||
async def delete_template(self, db: AsyncSession, template_id: str) -> None:
|
||||
"""Delete template"""
|
||||
query = delete(self.model).where(self.model.id == template_id)
|
||||
await db.execute(query)
|
||||
await db.flush()
|
||||
|
||||
async def is_name_exist(self, db: AsyncSession, name: str) -> bool:
|
||||
"""Check if template name exists"""
|
||||
query = (
|
||||
select(func.count()).select_from(self.model).where(self.model.name == name)
|
||||
)
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one() > 0 if result else False
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import json
|
||||
from typing import List
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete
|
||||
from app.db.models.cleaning import OperatorInstance
|
||||
|
||||
|
||||
class OperatorInstanceRepository:
|
||||
"""Repository for operator instance operations"""
|
||||
|
||||
def __init__(self, model=None):
|
||||
self.model = model if model else OperatorInstance
|
||||
|
||||
async def find_operator_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str
|
||||
) -> List[OperatorInstance]:
|
||||
"""Query operator instances by instance ID"""
|
||||
query = select(self.model).where(self.model.instance_id == instance_id)
|
||||
query = query.order_by(self.model.op_index.asc())
|
||||
result = await db.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def find_instance_by_instance_id(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str
|
||||
) -> List[OperatorInstance]:
|
||||
"""Query instances for template (same as find_operator_by_instance_id)"""
|
||||
return await self.find_operator_by_instance_id(db, instance_id)
|
||||
|
||||
async def insert_instance(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
instance_id: str,
|
||||
instances: List
|
||||
) -> None:
|
||||
"""Insert operator instances"""
|
||||
from app.db.models.cleaning import OperatorInstance as OperatorInstanceModel
|
||||
|
||||
for idx, instance in enumerate(instances):
|
||||
db_instance = OperatorInstanceModel(
|
||||
instance_id=instance_id,
|
||||
operator_id=instance.id,
|
||||
op_index=idx,
|
||||
settings_override=json.dumps(instance.overrides),
|
||||
)
|
||||
db.add(db_instance)
|
||||
await db.flush()
|
||||
|
||||
async def delete_by_instance_id(self, db: AsyncSession, instance_id: str) -> None:
|
||||
"""Delete instances by instance ID"""
|
||||
query = delete(self.model).where(self.model.instance_id == instance_id)
|
||||
await db.execute(query)
|
||||
await db.flush()
|
||||
Reference in New Issue
Block a user