#!/usr/bin/env python3 """ Generate QuestionSeed.kt from the v2 JSON question file. Usage: python seed_generator.py /path/to/questions.json > /path/to/QuestionSeed.kt """ import json import sys from pathlib import Path def parse_options(options_list): """Parse options array for single_choice, multi_choice, this_or_that.""" if not options_list: return "" opts = [] for opt in options_list: opt_id = opt.get("id", "") opt_text = opt.get("text", "") opts.append(f' ChoiceOption(id = "{opt_id}", text = "{opt_text}")') return ",\n".join(opts) def parse_answer_config(question): """Generate AnswerConfig code for a question.""" answer_config = question.get("answer_config", {}) qtype = question.get("type", "written") if qtype == "written": min_len = answer_config.get("min_length", 1) max_len = answer_config.get("max_length", 1000) placeholder = answer_config.get("placeholder", "Write your answer...") return f"WrittenAnswerConfig(\n minLength = {min_len},\n maxLength = {max_len},\n placeholder = \"{placeholder}\"\n )" elif qtype == "single_choice": options = question.get("options", []) opts = parse_options(options) return f"ChoiceAnswerConfig(\n options = listOf(\n{opts}\n )\n )" elif qtype == "scale": min_scale = answer_config.get("min_scale", 1) max_scale = answer_config.get("max_scale", 5) min_label = answer_config.get("min_label", "") max_label = answer_config.get("max_label", "") step = answer_config.get("scale_step", 1) return f"ScaleAnswerConfig(\n minScale = {min_scale},\n maxScale = {max_scale},\n minLabel = \"{min_label}\",\n maxLabel = \"{max_label}\",\n scaleStep = {step}\n )" elif qtype == "this_or_that": options = question.get("options", []) if len(options) >= 2: opt_a = options[0] opt_b = options[1] return f"ThisOrThatAnswerConfig(\n optionA = ChoiceOption(id = \"{opt_a.get('id', '')}\", text = \"{opt_a.get('text', '')}\"),\n optionB = ChoiceOption(id = \"{opt_b.get('id', '')}\", text = \"{opt_b.get('text', '')}\")\n )" return "" elif qtype == "multi_choice": options = question.get("options", []) opts = parse_options(options) return f"ChoiceAnswerConfig(\n options = listOf(\n{opts}\n )\n )" return "" def generate_question_code(question): """Generate a Question constructor call for a single question.""" qid = question.get("id", "") text = question.get("text", "").replace('"', '\\"') category = question.get("category_id", "") depth = question.get("depth", 1) access = question.get("access", "free") is_premium = access == "premium" qtype = question.get("type", "written") tags = question.get("tags", []) tags_str = ", ".join(f'"{t}"' for t in tags) if tags else "" answer_config_code = parse_answer_config(question) lines = [] lines.append(f'Question(') lines.append(f' id = "{qid}",') lines.append(f' text = "{text}",') lines.append(f' category = "{category}",') lines.append(f' depthLevel = {depth},') lines.append(f' isPremium = {str(is_premium).lower()},') lines.append(f' type = "{qtype}",') lines.append(f' tags = listOf({tags_str}),') if answer_config_code: if qtype == "written": lines.append(f' answerConfig = WrittenAnswerConfigImpl(config = {answer_config_code})') elif qtype == "single_choice": lines.append(f' answerConfig = ChoiceAnswerConfigImpl(config = {answer_config_code})') elif qtype == "scale": lines.append(f' answerConfig = ScaleAnswerConfigImpl(config = {answer_config_code})') elif qtype == "this_or_that": lines.append(f' answerConfig = ThisOrThatAnswerConfigImpl(config = {answer_config_code})') elif qtype == "multi_choice": lines.append(f' answerConfig = ChoiceAnswerConfigImpl(type = "multi_choice", config = {answer_config_code})') lines.append(')') return "\n".join(lines) def generate_seed_file(json_data): """Generate the complete QuestionSeed.kt file content.""" category = json_data.get("category", {}) questions = json_data.get("questions", []) # Category info for comments cat_id = category.get("id", "") cat_name = category.get("display_name", "") total = category.get("total_questions", 0) free_q = category.get("free_questions", 0) premium_q = category.get("premium_questions", 0) # Generate question list question_code_list = [] for q in questions: question_code_list.append(generate_question_code(q)) question_list = ",\n\n".join(question_code_list) kt_content = """package com.couplesconnect.app.data.questions import com.couplesconnect.app.domain.model.* object QuestionSeed { val questions: List = listOf( """ kt_content += question_list kt_content += """ ) // ── Helpers ───────────────────────────────────────────────── fun getDailyQuestion(): Question = questions.filter { it.category in mvpCategoryIds }.random() fun getQuestionsByCategory(categoryId: String): List = questions.filter { it.category == categoryId } fun getPremiumQuestions(): List = questions.filter { it.isPremium } fun getFreeQuestions(): List = questions.filter { !it.isPremium } fun getMvpQuestions(): List = questions.filter { it.category in mvpCategoryIds } fun getRandomQuestionsByCategory(categoryId: String, count: Int): List = questions.filter { it.category == categoryId }.shuffled().take(count) fun getQuestionById(id: String): Question? = questions.find { it.id == id } /** MVP categories available in version 0.1 */ private val mvpCategoryIds = setOf( "communication", "fun", "gratitude", "stress", "trust", "conflict", "future", "emotional_intimacy", "date_night", "values" ) } """ return kt_content def main(): if len(sys.argv) < 2: print("Usage: python seed_generator.py /path/to/questions.json", file=sys.stderr) sys.exit(1) json_path = sys.argv[1] if not Path(json_path).exists(): print(f"Error: File not found: {json_path}", file=sys.stderr) sys.exit(1) try: with open(json_path, 'r', encoding='utf-8') as f: json_data = json.load(f) except Exception as e: print(f"Error reading JSON: {e}", file=sys.stderr) sys.exit(1) kt_content = generate_seed_file(json_data) print(kt_content) if __name__ == "__main__": main()