Skip to content

Commit

Permalink
Merge pull request #942 from ChildMindInstitute/release-20231215.6
Browse files Browse the repository at this point in the history
Release 20231215.6
  • Loading branch information
vshvechko authored Dec 20, 2023
2 parents 026ac1e + 5b3b7b8 commit a83a1b5
Show file tree
Hide file tree
Showing 61 changed files with 3,336 additions and 1,212 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ repos:
entry: mypy
language: system
types: [python]
exclude: "apps/girderformindlogger"
exclude: "apps/girderformindlogger|apps/migrate"

- id: git-secrets
name: git-secrets
entry: git secrets --scan
Expand Down
3 changes: 3 additions & 0 deletions src/apps/activities/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from apps.activities.commands.reindex_items import app as activities

__all__ = ["activities"]
215 changes: 215 additions & 0 deletions src/apps/activities/commands/reindex_items.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import asyncio
import uuid
from functools import wraps

import typer
from rich import print
from rich.style import Style
from rich.table import Table
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from apps.activities.domain.activity_full import ActivityFull
from apps.activities.domain.response_values import (
MultiSelectionValues,
SingleSelectionValues,
)
from apps.activity_flows.domain.flow_update import (
ActivityFlowItemUpdate,
FlowUpdate,
)
from apps.applets.domain.applet_create_update import AppletUpdate
from apps.applets.domain.applet_full import AppletFull
from apps.applets.service.applet import AppletService
from apps.workspaces.crud.user_applet_access import UserAppletAccessCRUD
from infrastructure.database import atomic, session_manager

app = typer.Typer()


def coro(f):
@wraps(f)
def wrapper(*args, **kwargs):
return asyncio.run(f(*args, **kwargs))

return wrapper


def print_results(applets: list[tuple[uuid.UUID, str]]):
table = Table(
show_header=True,
title="Fixed applets",
title_style=Style(bold=True),
)
table.add_column("#")
table.add_column("id")
table.add_column("display name")
i = 1
for applet in applets:
table.add_row(str(i), str(applet[0]), applet[1])
i += 1
print(table)


async def get_applets_with_problem(session: AsyncSession) -> list[uuid.UUID]:
sql = text(
"""
with broken_activity_ids as (
select distinct activity_id as id
from activity_items
where exists (
select 1
from
jsonb_array_elements(response_values->'options') as opt1,
jsonb_array_elements(response_values->'options') as opt2
where opt1->>'value' = opt2->>'value'
and opt1->>'id' <> opt2->>'id'
)
)
select a.applet_id
from broken_activity_ids ba
join activities a on ba.id = a.id
"""
)
result = await session.execute(sql)
return result.scalars().all()


async def is_need_to_override(
values: SingleSelectionValues | MultiSelectionValues,
) -> bool:
unique_count = len(set(map(lambda v: v.value, values.options)))
return unique_count != len(values.options)


async def override_indexes(
values: SingleSelectionValues | MultiSelectionValues,
) -> SingleSelectionValues | MultiSelectionValues:
for i in range(len(values.options)):
values.options[i].value = i
return values


async def _reindex_items(activities: list[ActivityFull]) -> list[ActivityFull]:
for activity in activities:
for item in activity.items:
if isinstance(
item.response_values, SingleSelectionValues
) or isinstance(item.response_values, MultiSelectionValues):
fix = await is_need_to_override(item.response_values)
if fix:
item.response_values = await override_indexes(
item.response_values
)
return activities


async def reindex_applet(
session: AsyncSession, applet_id: uuid.UUID
) -> AppletFull:
fake_user_id = uuid.uuid4()
applet = await AppletService(session, fake_user_id).get_full_applet(
applet_id
)
role = await UserAppletAccessCRUD(session).get_applet_owner(applet_id)
fixed_activities = await _reindex_items(applet.activities)
activity_flows = []
for flow in applet.activity_flows:
items = []
for flow_item in flow.items:
activity = next(
filter(
lambda a: a.id == flow_item.activity_id, fixed_activities
),
None,
)
if not activity:
raise Exception(f"Activity {flow_item.activity_id} not found")
item = ActivityFlowItemUpdate(
id=flow_item.id, activity_key=activity.key
)
items.append(item)
flow_update = FlowUpdate(
id=flow.id,
name=flow.name,
description=flow.description,
is_single_report=flow.is_single_report,
hide_badge=flow.hide_badge,
report_included_activity_name=flow.report_included_activity_name,
report_included_item_name=flow.report_included_item_name,
is_hidden=flow.is_hidden,
items=items,
)
activity_flows.append(flow_update)
update_values = AppletUpdate(
display_name=applet.display_name,
description=applet.description,
about=applet.about,
image=applet.image,
watermark=applet.watermark,
theme_id=applet.theme_id,
link=applet.link,
require_login=applet.require_login,
pinned_at=applet.pinned_at,
retention_period=applet.retention_period,
retention_type=applet.retention_type,
stream_enabled=applet.stream_enabled,
encryption=applet.encryption,
activities=fixed_activities,
activity_flows=activity_flows,
)
async with atomic(session):
await AppletService(session, role.owner_id).update(
applet.id, update_values
)
return applet


@app.command(short_help="Find and fix activity_items automatically")
@coro
async def reindex_auto():
s_maker = session_manager.get_session()
try:
async with s_maker() as session:
result = []
applet_ids = await get_applets_with_problem(session)
count_all = len(applet_ids)
count = 0
for applet_id in applet_ids:
count += 1
print(f"Processing {count}/{count_all}")
applet = await reindex_applet(session, applet_id)
result.append(
(
applet.id,
applet.display_name,
)
)
print_results(result)
except Exception as ex:
print(f"[bold red] {ex}")
finally:
await s_maker.remove()


@app.command(short_help="Fix indexation of activity items")
@coro
async def reindex(
applet_id: uuid.UUID = typer.Argument(..., help="Applet id"),
):
s_maker = session_manager.get_session()
try:
async with s_maker() as session:
applet = await reindex_applet(session, applet_id)
print_results(
[
(
applet.id,
applet.display_name,
)
]
)
except Exception as ex:
print(f"[bold red] {ex}")
finally:
await s_maker.remove()
Loading

0 comments on commit a83a1b5

Please sign in to comment.