Improve tasks and align page elements
This commit is contained in:
@@ -41,6 +41,7 @@ from core.models import (
|
||||
Message,
|
||||
MessageEvent,
|
||||
PatternMitigationPlan,
|
||||
ChatTaskSource,
|
||||
Person,
|
||||
PersonIdentifier,
|
||||
PlatformChatLink,
|
||||
@@ -1787,9 +1788,66 @@ def _command_options_for_channel(user, service: str, identifier: str) -> list[di
|
||||
"profile_enabled": bool(profile.enabled),
|
||||
}
|
||||
)
|
||||
task_announce_enabled = False
|
||||
if variants:
|
||||
source = (
|
||||
ChatTaskSource.objects.filter(
|
||||
user=user,
|
||||
service=service_key,
|
||||
channel_identifier__in=list(variants),
|
||||
enabled=True,
|
||||
)
|
||||
.order_by("-updated_at")
|
||||
.first()
|
||||
)
|
||||
settings_row = dict(getattr(source, "settings", {}) or {}) if source else {}
|
||||
task_announce_enabled = str(settings_row.get("announce_task_id", "")).strip().lower() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
"on",
|
||||
}
|
||||
options.append(
|
||||
{
|
||||
"slug": "task_announce",
|
||||
"name": "Announce Task IDs",
|
||||
"trigger_token": "",
|
||||
"enabled_here": bool(task_announce_enabled),
|
||||
"profile_enabled": True,
|
||||
}
|
||||
)
|
||||
return options
|
||||
|
||||
|
||||
def _toggle_task_announce_for_channel(
|
||||
*,
|
||||
user,
|
||||
service: str,
|
||||
identifier: str,
|
||||
enabled: bool,
|
||||
) -> tuple[bool, str]:
|
||||
service_key = _default_service(service)
|
||||
canonical_identifier = _canonical_command_channel_identifier(service_key, identifier)
|
||||
if not canonical_identifier:
|
||||
return (False, "missing_identifier")
|
||||
variants = _command_channel_identifier_variants(service_key, canonical_identifier)
|
||||
rows = list(
|
||||
ChatTaskSource.objects.filter(
|
||||
user=user,
|
||||
service=service_key,
|
||||
channel_identifier__in=list(variants),
|
||||
).order_by("-updated_at")
|
||||
)
|
||||
if not rows:
|
||||
return (False, "task_source_mapping_missing")
|
||||
for row in rows:
|
||||
settings_row = dict(row.settings or {})
|
||||
settings_row["announce_task_id"] = bool(enabled)
|
||||
row.settings = settings_row
|
||||
row.save(update_fields=["settings", "updated_at"])
|
||||
return (True, "")
|
||||
|
||||
|
||||
def _compose_urls(service, identifier, person_id):
|
||||
service_key = _default_service(service)
|
||||
identifier_value = str(identifier or "").strip()
|
||||
@@ -3314,13 +3372,21 @@ class ComposeToggleCommand(LoginRequiredMixin, View):
|
||||
"yes",
|
||||
"on",
|
||||
}
|
||||
ok, error = _toggle_command_for_channel(
|
||||
user=request.user,
|
||||
service=service,
|
||||
identifier=channel_identifier,
|
||||
slug=slug,
|
||||
enabled=enabled,
|
||||
)
|
||||
if slug == "task_announce":
|
||||
ok, error = _toggle_task_announce_for_channel(
|
||||
user=request.user,
|
||||
service=service,
|
||||
identifier=channel_identifier,
|
||||
enabled=enabled,
|
||||
)
|
||||
else:
|
||||
ok, error = _toggle_command_for_channel(
|
||||
user=request.user,
|
||||
service=service,
|
||||
identifier=channel_identifier,
|
||||
slug=slug,
|
||||
enabled=enabled,
|
||||
)
|
||||
if not ok:
|
||||
return JsonResponse(
|
||||
{
|
||||
@@ -3347,7 +3413,11 @@ class ComposeToggleCommand(LoginRequiredMixin, View):
|
||||
"slug": slug,
|
||||
"enabled": bool(enabled),
|
||||
"command_options": command_options,
|
||||
"settings_url": reverse("command_routing"),
|
||||
"settings_url": (
|
||||
f"{reverse('tasks_settings')}?{urlencode({'service': service, 'identifier': channel_identifier})}"
|
||||
if slug == "task_announce"
|
||||
else reverse("command_routing")
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -26,6 +26,17 @@ from core.models import (
|
||||
)
|
||||
from core.tasks.providers.mock import get_provider
|
||||
|
||||
SAFE_TASK_FLAGS_DEFAULTS = {
|
||||
"derive_enabled": True,
|
||||
"match_mode": "strict",
|
||||
"require_prefix": True,
|
||||
"allowed_prefixes": ["task:", "todo:"],
|
||||
"completion_enabled": True,
|
||||
"ai_title_enabled": True,
|
||||
"announce_task_id": False,
|
||||
"min_chars": 3,
|
||||
}
|
||||
|
||||
|
||||
def _to_bool(raw, default=False) -> bool:
|
||||
if raw is None:
|
||||
@@ -50,32 +61,73 @@ def _parse_prefixes(value: str) -> list[str]:
|
||||
return rows or ["task:", "todo:"]
|
||||
|
||||
|
||||
def _looks_like_old_risky_defaults(raw: dict) -> bool:
|
||||
row = dict(raw or {})
|
||||
mode = str(row.get("match_mode") or "").strip().lower()
|
||||
require_prefix = _to_bool(row.get("require_prefix"), False)
|
||||
prefixes = _parse_prefixes(",".join(list(row.get("allowed_prefixes") or [])))
|
||||
min_chars = int(row.get("min_chars") or 8)
|
||||
return (
|
||||
mode in {"", "balanced"}
|
||||
and (not require_prefix)
|
||||
and prefixes == ["task:", "todo:", "action:"]
|
||||
and min_chars >= 8
|
||||
)
|
||||
|
||||
|
||||
def _normalized_safe_flags(raw: dict | None) -> dict:
|
||||
row = dict(raw or {})
|
||||
defaults = dict(SAFE_TASK_FLAGS_DEFAULTS)
|
||||
if _looks_like_old_risky_defaults(row):
|
||||
return defaults
|
||||
merged = dict(defaults)
|
||||
merged.update(
|
||||
{
|
||||
"derive_enabled": _to_bool(row.get("derive_enabled"), defaults["derive_enabled"]),
|
||||
"match_mode": str(row.get("match_mode") or defaults["match_mode"]).strip().lower() or defaults["match_mode"],
|
||||
"require_prefix": _to_bool(row.get("require_prefix"), defaults["require_prefix"]),
|
||||
"allowed_prefixes": _parse_prefixes(",".join(list(row.get("allowed_prefixes") or defaults["allowed_prefixes"]))),
|
||||
"completion_enabled": _to_bool(row.get("completion_enabled"), defaults["completion_enabled"]),
|
||||
"ai_title_enabled": _to_bool(row.get("ai_title_enabled"), defaults["ai_title_enabled"]),
|
||||
"announce_task_id": _to_bool(row.get("announce_task_id"), defaults["announce_task_id"]),
|
||||
"min_chars": max(1, int(row.get("min_chars") or defaults["min_chars"])),
|
||||
}
|
||||
)
|
||||
return merged
|
||||
|
||||
|
||||
def _apply_safe_defaults_for_user(user) -> None:
|
||||
projects = list(TaskProject.objects.filter(user=user).only("id", "settings"))
|
||||
for row in projects:
|
||||
normalized = _normalized_safe_flags(row.settings)
|
||||
if dict(row.settings or {}) != normalized:
|
||||
row.settings = normalized
|
||||
row.save(update_fields=["settings", "updated_at"])
|
||||
sources = list(ChatTaskSource.objects.filter(user=user).only("id", "settings"))
|
||||
for row in sources:
|
||||
normalized = _normalized_safe_flags(row.settings)
|
||||
if dict(row.settings or {}) != normalized:
|
||||
row.settings = normalized
|
||||
row.save(update_fields=["settings", "updated_at"])
|
||||
|
||||
|
||||
def _flags_from_post(request, prefix: str = "") -> dict:
|
||||
key = lambda name: f"{prefix}{name}" if prefix else name
|
||||
defaults = dict(SAFE_TASK_FLAGS_DEFAULTS)
|
||||
return {
|
||||
"derive_enabled": _to_bool(request.POST.get(key("derive_enabled")), True),
|
||||
"match_mode": str(request.POST.get(key("match_mode")) or "strict").strip().lower() or "strict",
|
||||
"require_prefix": _to_bool(request.POST.get(key("require_prefix")), True),
|
||||
"allowed_prefixes": _parse_prefixes(str(request.POST.get(key("allowed_prefixes")) or "")),
|
||||
"completion_enabled": _to_bool(request.POST.get(key("completion_enabled")), True),
|
||||
"ai_title_enabled": _to_bool(request.POST.get(key("ai_title_enabled")), True),
|
||||
"announce_task_id": _to_bool(request.POST.get(key("announce_task_id")), True),
|
||||
"min_chars": max(1, int(str(request.POST.get(key("min_chars")) or "3").strip() or "3")),
|
||||
"derive_enabled": _to_bool(request.POST.get(key("derive_enabled")), defaults["derive_enabled"]),
|
||||
"match_mode": str(request.POST.get(key("match_mode")) or defaults["match_mode"]).strip().lower() or defaults["match_mode"],
|
||||
"require_prefix": _to_bool(request.POST.get(key("require_prefix")), defaults["require_prefix"]),
|
||||
"allowed_prefixes": _parse_prefixes(str(request.POST.get(key("allowed_prefixes")) or ",".join(defaults["allowed_prefixes"]))),
|
||||
"completion_enabled": _to_bool(request.POST.get(key("completion_enabled")), defaults["completion_enabled"]),
|
||||
"ai_title_enabled": _to_bool(request.POST.get(key("ai_title_enabled")), defaults["ai_title_enabled"]),
|
||||
"announce_task_id": _to_bool(request.POST.get(key("announce_task_id")), defaults["announce_task_id"]),
|
||||
"min_chars": max(1, int(str(request.POST.get(key("min_chars")) or str(defaults["min_chars"])).strip() or str(defaults["min_chars"]))),
|
||||
}
|
||||
|
||||
|
||||
def _flags_with_defaults(raw: dict | None) -> dict:
|
||||
row = dict(raw or {})
|
||||
return {
|
||||
"derive_enabled": _to_bool(row.get("derive_enabled"), True),
|
||||
"match_mode": str(row.get("match_mode") or "strict").strip().lower() or "strict",
|
||||
"require_prefix": _to_bool(row.get("require_prefix"), True),
|
||||
"allowed_prefixes": _parse_prefixes(",".join(list(row.get("allowed_prefixes") or []))),
|
||||
"completion_enabled": _to_bool(row.get("completion_enabled"), True),
|
||||
"ai_title_enabled": _to_bool(row.get("ai_title_enabled"), True),
|
||||
"announce_task_id": _to_bool(row.get("announce_task_id"), True),
|
||||
"min_chars": max(1, int(row.get("min_chars") or 3)),
|
||||
}
|
||||
return _normalized_safe_flags(raw)
|
||||
|
||||
|
||||
def _settings_redirect(request):
|
||||
@@ -277,6 +329,7 @@ class TaskSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/tasks-settings.html"
|
||||
|
||||
def _context(self, request):
|
||||
_apply_safe_defaults_for_user(request.user)
|
||||
prefill_service = str(request.GET.get("service") or "").strip().lower()
|
||||
prefill_identifier = str(request.GET.get("identifier") or "").strip()
|
||||
projects = list(TaskProject.objects.filter(user=request.user).order_by("name"))
|
||||
|
||||
Reference in New Issue
Block a user