Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,4 @@ AGENTS.MD
# Exclude patterns
ai-engine/tests/test_smart_assumptions.py

.worktrees/
38 changes: 36 additions & 2 deletions ai-engine/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ exclude = [
]

[tool.ruff.lint]
# Enable pycodestyle (E), Pyflakes (F), isort (I), flake8-quotes (Q), flake8-naming (N)
select = ["E", "F", "I", "Q", "N"]
# Enable pycodestyle (E), Pyflakes (F), isort (I), flake8-quotes (Q), flake8-naming (N), pydocstyle (D), pyupgrade (UP)
select = ["E", "F", "I", "Q", "N", "D", "UP"]
ignore = [
"E402", # module level import not at top of file
"E501", # line too long (handled by formatter)
Expand All @@ -38,6 +38,40 @@ ignore = [
"E722", # bare except (legacy code uses bare except for compatibility)
"E713", # test for membership should be `not in`
"F821", # undefined name (type hints and forward references)
# pydocstyle (D) ignores for legacy code
"D100", # Missing docstring in public module
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
"D103", # Missing docstring in public function
"D104", # Missing docstring in public package
"D105", # Missing docstring in magic method
"D106", # Missing docstring in public nested class
"D107", # Missing docstring in __init__
"D200", # One-line docstring should fit on one line with quotes
"D205", # 1 blank line required between summary line and description
"D400", # First line should end with a period
"D401", # First line should be in imperative mood
"D415", # First line should end with a period, question mark, or exclamation point
# pydocstyle additional ignores for legacy code
"D201", # No blank lines allowed before function docstring
"D202", # no blank lines allowed after function docstring
"D204", # 1 blank line required after class docstring
"D212", # Multi-line docstring summary should start at the first line
"D213", # Multi-line docstring summary should start at the second line
"D413", # Missing blank line after last section
"D417", # Missing argument description in the docstring
"D300", # Use triple double quotes `"""`
# pyupgrade (UP) ignores for legacy code
"UP035", # deprecated typing imports (List, Dict, etc.)
"UP006", # use built-in types instead of typing (not yet enforced)
"UP007", # use X | Y for type annotations
"UP015", # unnecessary mode argument in open()
"UP024", # replace IOError with OSError
"UP031", # use format specifiers instead of percent format
"UP032", # use f-string instead of format() call
"UP045", # use X | None instead of Optional[X]
"UP017", # use datetime.UTC instead of datetime.now(timezone.utc)
"UP042", # inherit from enum.StrEnum instead of str, Enum
]

[tool.ruff.lint.per-file-ignores]
Expand Down
37 changes: 35 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ exclude = [
"*.egg-info/",
".venv/",
"venv/",
"mutants/", # mutation testing generates code with odd names
]

[tool.ruff.lint]
# Enable pycodestyle (E), Pyflakes (F), isort (I), flake8-quotes (Q), flake8-naming (N)
select = ["E", "F", "I", "Q", "N"]
# Enable pycodestyle (E), Pyflakes (F), isort (I), flake8-quotes (Q), flake8-naming (N), pydocstyle (D), pyupgrade (UP)
select = ["E", "F", "I", "Q", "N", "D", "UP"]
ignore = [
"E402", # module level import not at top of file
"E501", # line too long (handled by formatter)
Expand All @@ -94,6 +95,38 @@ ignore = [
"E722", # bare except (legacy code uses bare except for compatibility)
"E713", # test for membership should be `not in`
"F821", # undefined name (type hints and forward references)
# pydocstyle (D) ignores for legacy code
"D100", # Missing docstring in public module
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
"D103", # Missing docstring in public function
"D104", # Missing docstring in public package
"D105", # Missing docstring in magic method
"D106", # Missing docstring in public nested class
"D107", # Missing docstring in __init__
"D200", # One-line docstring should fit on one line with quotes
"D205", # 1 blank line required between summary line and description
"D400", # First line should end with a period
"D401", # First line should be in imperative mood
"D415", # First line should end with a period, question mark, or exclamation point
# pydocstyle additional ignores for legacy code
"D204", # 1 blank line required after class docstring
"D212", # Multi-line docstring summary should start at the first line
"D213", # Multi-line docstring summary should start at the second line
"D413", # Missing blank line after last section
# pyupgrade (UP) ignores for legacy code
"UP035", # deprecated typing imports (List, Dict, etc.)
"UP006", # use built-in types instead of typing (not yet enforced)
"UP015", # unnecessary mode argument in open()
"UP024", # replace IOError with OSError
"UP032", # use f-string instead of format() call
"UP045", # use X | None instead of Optional[X]
"UP017", # use datetime.UTC instead of datetime.now(timezone.utc)
"UP042", # inherit from enum.StrEnum instead of str, Enum
# pydocstyle (D) additional ignores for legacy code
"D202", # no blank lines allowed after function docstring
"D406", # section name should end with a newline
"D409", # section underline should match the length of its name
]

[tool.ruff.lint.per-file-ignores]
Expand Down
12 changes: 3 additions & 9 deletions backend/src/api/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ async def track_event(
This endpoint records user behavior events such as page views,
button clicks, conversion starts, and other interactions.
"""
logger.info(
f"Tracking analytics event: {event.event_type} ({event.event_category})"
)
logger.info(f"Tracking analytics event: {event.event_type} ({event.event_category})")

# Get client information from request
user_agent = request.headers.get("user-agent")
Expand Down Expand Up @@ -141,9 +139,7 @@ async def track_event(
event_category=db_event.event_category,
user_id=db_event.user_id,
session_id=db_event.session_id,
conversion_id=(
str(db_event.conversion_id) if db_event.conversion_id else None
),
conversion_id=(str(db_event.conversion_id) if db_event.conversion_id else None),
event_properties=db_event.event_properties,
created_at=db_event.created_at.isoformat(),
)
Expand Down Expand Up @@ -324,9 +320,7 @@ async def track_page_view(

except Exception as e:
logger.error(f"Failed to track page view: {e}")
raise HTTPException(
status_code=500, detail=f"Failed to track page view: {str(e)}"
)
raise HTTPException(status_code=500, detail=f"Failed to track page view: {str(e)}")


@router.post("/events/conversion")
Expand Down
96 changes: 24 additions & 72 deletions backend/src/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,18 @@ class ConversionJob(Base):
)

# Relationship: one job -> many results and progress
results = relationship(
"ConversionResult", back_populates="job", cascade="all, delete-orphan"
)
results = relationship("ConversionResult", back_populates="job", cascade="all, delete-orphan")
progress = relationship(
"JobProgress", back_populates="job", cascade="all, delete-orphan", uselist=False
)
# Relationship to comparison_results
comparison_results = relationship(
"ComparisonResultDb", back_populates="conversion_job"
)
comparison_results = relationship("ComparisonResultDb", back_populates="conversion_job")
# Relationship to feedback
feedback = relationship(
"ConversionFeedback", back_populates="job", cascade="all, delete-orphan"
)
# Relationship to assets
assets = relationship(
"Asset", back_populates="conversion", cascade="all, delete-orphan"
)
assets = relationship("Asset", back_populates="conversion", cascade="all, delete-orphan")


class ConversionResult(Base):
Expand Down Expand Up @@ -122,9 +116,7 @@ class JobProgress(Base):
nullable=False,
unique=True,
)
progress: Mapped[int] = mapped_column(
Integer, nullable=False, server_default=text("0")
)
progress: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("0"))
last_update: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
Expand Down Expand Up @@ -165,15 +157,9 @@ class Addon(Base):
)

# Relationships
blocks = relationship(
"AddonBlock", back_populates="addon", cascade="all, delete-orphan"
)
assets = relationship(
"AddonAsset", back_populates="addon", cascade="all, delete-orphan"
)
recipes = relationship(
"AddonRecipe", back_populates="addon", cascade="all, delete-orphan"
)
blocks = relationship("AddonBlock", back_populates="addon", cascade="all, delete-orphan")
assets = relationship("AddonAsset", back_populates="addon", cascade="all, delete-orphan")
recipes = relationship("AddonRecipe", back_populates="addon", cascade="all, delete-orphan")


class AddonBlock(Base):
Expand Down Expand Up @@ -224,9 +210,7 @@ class AddonAsset(Base):
addon_id: Mapped[str] = mapped_column(
UUID(as_uuid=True), ForeignKey("addons.id", ondelete="CASCADE"), nullable=False
)
type: Mapped[str] = mapped_column(
String, nullable=False
) # E.g., "texture", "sound", "script"
type: Mapped[str] = mapped_column(String, nullable=False) # E.g., "texture", "sound", "script"
path: Mapped[str] = mapped_column(
String, nullable=False
) # Relative path within the addon structure
Expand Down Expand Up @@ -357,9 +341,7 @@ class ConversionFeedback(Base):
__tablename__ = "conversion_feedback"
__table_args__ = {"extend_existing": True}

id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
job_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("conversion_jobs.id"), nullable=False
)
Expand Down Expand Up @@ -400,9 +382,7 @@ class Asset(Base):
nullable=False,
server_default=text("'pending'"),
) # 'pending', 'processing', 'converted', 'failed'
asset_metadata: Mapped[Optional[dict]] = mapped_column(
JSONType, nullable=True, default={}
)
asset_metadata: Mapped[Optional[dict]] = mapped_column(JSONType, nullable=True, default={})
file_size: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
mime_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
original_filename: Mapped[str] = mapped_column(String, nullable=False)
Expand Down Expand Up @@ -431,9 +411,7 @@ class ComparisonResultDb(Base):
__table_args__ = {"extend_existing": True}

id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
conversion_id = Column(
UUID(as_uuid=True), ForeignKey("conversion_jobs.id"), nullable=False
)
conversion_id = Column(UUID(as_uuid=True), ForeignKey("conversion_jobs.id"), nullable=False)
structural_diff = Column(JSONType)
code_diff = Column(JSONType)
asset_diff = Column(JSONType)
Expand All @@ -454,17 +432,13 @@ class FeatureMappingDb(Base):
__table_args__ = {"extend_existing": True}

id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
comparison_id = Column(
UUID(as_uuid=True), ForeignKey("comparison_results.id"), nullable=False
)
comparison_id = Column(UUID(as_uuid=True), ForeignKey("comparison_results.id"), nullable=False)
java_feature = Column(Text)
bedrock_equivalent = Column(Text)
mapping_type = Column(VARCHAR(50))
confidence_score = Column(DECIMAL(3, 2))

comparison_result = relationship(
"ComparisonResultDb", back_populates="feature_mappings"
)
comparison_result = relationship("ComparisonResultDb", back_populates="feature_mappings")


# Document Embedding Models
Expand All @@ -475,15 +449,11 @@ class DocumentEmbedding(Base):
__table_args__ = {"extend_existing": True}

id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
embedding = Column(
VECTOR(1536), nullable=False
) # Assuming nullable=False for embedding
embedding = Column(VECTOR(1536), nullable=False) # Assuming nullable=False for embedding
document_source = Column(String, nullable=False, index=True)
content_hash = Column(String, nullable=False, unique=True, index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())


# A/B Testing Models
Expand All @@ -493,17 +463,11 @@ class Experiment(Base):
__tablename__ = "experiments"
__table_args__ = {"extend_existing": True}

id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
start_date: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True
)
end_date: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True
)
start_date: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
end_date: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
status: Mapped[str] = mapped_column(
String(20), nullable=False, default="draft"
) # draft, active, paused, completed
Expand Down Expand Up @@ -532,9 +496,7 @@ class ExperimentVariant(Base):
__tablename__ = "experiment_variants"
__table_args__ = {"extend_existing": True}

id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
experiment_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("experiments.id", ondelete="CASCADE"),
Expand Down Expand Up @@ -565,17 +527,13 @@ class ExperimentResult(Base):
__tablename__ = "experiment_results"
__table_args__ = {"extend_existing": True}

id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
variant_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("experiment_variants.id", ondelete="CASCADE"),
nullable=False,
)
session_id: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True), nullable=True
)
session_id: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), nullable=True)
kpi_quality: Mapped[Optional[float]] = mapped_column(
DECIMAL(5, 2), nullable=True
) # Quality score (0.00 to 100.00)
Expand Down Expand Up @@ -618,9 +576,7 @@ class BehaviorTemplate(Base):
tags: Mapped[list] = mapped_column(JSONType, nullable=False, default=list)
is_public: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
version: Mapped[str] = mapped_column(String(20), nullable=False, default="1.0.0")
created_by: Mapped[Optional[UUID]] = mapped_column(
UUID(as_uuid=True), nullable=True
)
created_by: Mapped[Optional[UUID]] = mapped_column(UUID(as_uuid=True), nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
Expand Down Expand Up @@ -652,12 +608,8 @@ class AnalyticsEvent(Base):
event_category: Mapped[str] = mapped_column(
String(50), nullable=False, index=True
) # e.g., "navigation", "conversion", "feedback", "export"
user_id: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, index=True
)
session_id: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, index=True
)
user_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True, index=True)
session_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True, index=True)
conversion_id: Mapped[Optional[str]] = mapped_column(
UUID(as_uuid=True), nullable=True, index=True
) # Link to conversion job if applicable
Expand Down
Loading
Loading