36:    subject_set_ids: list = []
47:    subject_rotation: str = "round_robin"
63:    "subject_rotation": {"round_robin", "random", "fixed_first"},
73:        "subject_set_ids": c.subject_set_ids or [],
83:        "subject_rotation": c.subject_rotation,
118:    if data.subject_rotation not in VALID_ENUMS["subject_rotation"]:
119:        errors.append("subject_rotation must be round_robin|random|fixed_first")
138:    for sid in data.subject_set_ids or []:
142:    if not (data.subject_set_ids or []):
143:        errors.append("Select at least one subject line")
291:    the actual messages (subject + HTML + substituted variables). NOTHING is sent."""
324:        subj = await db.get(SubjectLine, item["subject_id"]) if item.get("subject_id") else None
336:        subject = ""
342:            subject = subj.subject if subj else (tpl.subject_hint or tpl.name)
348:            res_s = engine.substitute(subj.subject, rec, g,
350:            subject = res_s.text
352:                warn_list.append(f"Unknown variable in subject: {u}")
354:                warn_list.append(f"Missing data in subject: {m_}")
359:            "subject": subject,
553:    """Monitor table: per-recipient status, assigned account, subject/template, errors."""
570:            if cr.generated_subject_id and cr.generated_subject_id not in subj_map:
571:                s = await db.get(SubjectLine, cr.generated_subject_id)
572:                subj_map[cr.generated_subject_id] = (s.subject[:80] if s else "?")
584:            "subject": subj_map.get(cr.generated_subject_id, ""),
608:        w.writerow(["seq", "email", "status", "account", "subject", "template",
615:                        cr.generated_subject_id, cr.generated_template_id,
631:# Wizard options: what lists/subjects/templates/accounts/phones are selectable
640:    subjects = (await db.execute(select(SubjectLine).where(SubjectLine.user_id == user.id)
664:        "subjects": [{"id": s.id, "subject": s.subject[:160]} for s in subjects],
674:                   "max_subjects": cfg.MAX_SUBJECTS},
