added examples
This commit is contained in:
175
examples/README.md
Normal file
175
examples/README.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Flalingo Path JSON Structure Documentation
|
||||
|
||||
This directory contains example JSON files that demonstrate the structure of learning paths in the Flalingo language learning application.
|
||||
|
||||
## Overview
|
||||
|
||||
A learning path in Flalingo is a structured sequence of educational content organized into nodes, where each node contains multiple exercises. The JSON structure mirrors the Rust data models used in the application.
|
||||
|
||||
## File Examples
|
||||
|
||||
- **`example_path.json`** - Comprehensive example showing a complete German family vocabulary path
|
||||
- **`simple_path.json`** - Basic example for beginners (German greetings)
|
||||
- **`advanced_path.json`** - Complex business German communication path
|
||||
|
||||
## JSON Structure
|
||||
|
||||
### Root Path Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string", // Unique identifier for the path
|
||||
"title": "string", // Human-readable path title
|
||||
"description": "string", // Detailed description of the path content
|
||||
"metadata": [...], // Array of metadata objects
|
||||
"nodes": [...] // Array of node objects
|
||||
}
|
||||
```
|
||||
|
||||
### Metadata Object
|
||||
|
||||
```json
|
||||
{
|
||||
"path_id": "string", // Reference to parent path ID
|
||||
"version": "string", // Semantic version (e.g., "1.2.0")
|
||||
"created_at": "string", // ISO 8601 UTC timestamp
|
||||
"updated_at": "string" // ISO 8601 UTC timestamp
|
||||
}
|
||||
```
|
||||
|
||||
### Node Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": number, // Unique numeric identifier
|
||||
"title": "string", // Node title/name
|
||||
"description": "string", // Node description
|
||||
"path_id": "string", // Reference to parent path ID
|
||||
"exercises": [...] // Array of exercise objects
|
||||
}
|
||||
```
|
||||
|
||||
### Exercise Object
|
||||
|
||||
```json
|
||||
{
|
||||
"id": number, // Unique numeric identifier
|
||||
"ex_type": "string", // Exercise type (see types below)
|
||||
"content": "string", // JSON-encoded exercise content
|
||||
"node_id": number // Reference to parent node ID
|
||||
}
|
||||
```
|
||||
|
||||
## Exercise Types
|
||||
|
||||
The `ex_type` field defines the type of exercise. Common types include:
|
||||
|
||||
### Basic Types
|
||||
- **`vocabulary`** - Single word/phrase learning
|
||||
- **`multiple_choice`** - Question with multiple answer options
|
||||
- **`fill_blank`** - Complete sentences with missing words
|
||||
- **`translation`** - Translate between languages
|
||||
- **`listening`** - Audio comprehension exercises
|
||||
|
||||
### Interactive Types
|
||||
- **`drag_drop`** - Match items by dragging and dropping
|
||||
- **`conversation`** - Simulated dialogue practice
|
||||
- **`speaking`** - Voice recording and pronunciation
|
||||
- **`role_play`** - Interactive scenario-based exercises
|
||||
|
||||
### Advanced Types
|
||||
- **`grammar_explanation`** - Detailed grammar lessons
|
||||
- **`story_completion`** - Complete narrative texts
|
||||
- **`comprehensive_quiz`** - Multi-format assessment
|
||||
- **`case_study_comprehensive`** - Complex real-world scenarios
|
||||
|
||||
## Exercise Content Structure
|
||||
|
||||
The `content` field contains a JSON-encoded string with exercise-specific data:
|
||||
|
||||
### Vocabulary Exercise
|
||||
```json
|
||||
{
|
||||
"word": "der Vater",
|
||||
"translation": "father",
|
||||
"audio": "/audio/vater.mp3",
|
||||
"example": "Mein Vater ist Arzt."
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Choice Exercise
|
||||
```json
|
||||
{
|
||||
"question": "How do you say 'sister' in German?",
|
||||
"options": ["die Schwester", "der Schwester", "das Schwester", "die Schwestern"],
|
||||
"correct": 0,
|
||||
"explanation": "'Die Schwester' is feminine, so it uses the article 'die'."
|
||||
}
|
||||
```
|
||||
|
||||
### Conversation Exercise
|
||||
```json
|
||||
{
|
||||
"scenario": "Family introduction at a party",
|
||||
"dialogue": [
|
||||
{"speaker": "A", "text": "Ist das deine Familie?"},
|
||||
{"speaker": "B", "text": "Ja, das sind meine Eltern und mein Bruder."}
|
||||
],
|
||||
"vocabulary_focus": ["Familie", "Eltern", "Alter", "Beruf"]
|
||||
}
|
||||
```
|
||||
|
||||
### Listening Exercise
|
||||
```json
|
||||
{
|
||||
"audio_file": "/audio/family_description.mp3",
|
||||
"transcript": "Hallo, ich heiße Anna...",
|
||||
"questions": [
|
||||
{"question": "Wie heißt die Frau?", "answer": "Anna"},
|
||||
{"question": "Ist sie verheiratet?", "answer": "Ja"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Design Principles
|
||||
|
||||
### Progressive Difficulty
|
||||
Paths are structured with increasing complexity:
|
||||
1. **Simple vocabulary introduction**
|
||||
2. **Basic grammar concepts**
|
||||
3. **Practical application**
|
||||
4. **Comprehensive review**
|
||||
|
||||
### Content Organization
|
||||
- **Logical grouping**: Related concepts are grouped within nodes
|
||||
- **Sequential learning**: Nodes build upon previous knowledge
|
||||
- **Mixed exercise types**: Various formats maintain engagement
|
||||
- **Real-world context**: Practical scenarios and authentic language use
|
||||
|
||||
### Metadata Usage
|
||||
- **Version control**: Track content updates and revisions
|
||||
- **Timestamps**: Monitor content freshness and usage patterns
|
||||
- **Path relationships**: Enable content dependencies and prerequisites
|
||||
|
||||
## File Naming Convention
|
||||
|
||||
- `simple_*.json` - Beginner level (A1-A2)
|
||||
- `example_*.json` - Intermediate level (B1-B2)
|
||||
- `advanced_*.json` - Advanced level (C1-C2)
|
||||
- `specialized_*.json` - Domain-specific content (business, academic, etc.)
|
||||
|
||||
## Integration Notes
|
||||
|
||||
These JSON files can be:
|
||||
- **Imported** into the SQLite database using migration scripts
|
||||
- **Exported** from the database for backup or sharing
|
||||
- **Used as templates** for creating new learning paths
|
||||
- **Validated** against the Rust type system for consistency
|
||||
|
||||
## Validation
|
||||
|
||||
All JSON files should be validated for:
|
||||
- **Structure compliance** with the documented schema
|
||||
- **Content consistency** (valid references, proper formatting)
|
||||
- **Educational quality** (appropriate difficulty progression, clear instructions)
|
||||
- **Technical accuracy** (valid audio paths, properly encoded JSON strings)
|
||||
151
examples/advanced_path.json
Normal file
151
examples/advanced_path.json
Normal file
@@ -0,0 +1,151 @@
|
||||
{
|
||||
"id": "path_advanced_001",
|
||||
"title": "German Business Communication - Geschäftskommunikation",
|
||||
"description": "Master advanced German communication skills for professional environments. Learn formal language, business etiquette, and complex grammatical structures used in corporate settings.",
|
||||
"metadata": [
|
||||
{
|
||||
"path_id": "path_advanced_001",
|
||||
"version": "2.1.0",
|
||||
"created_at": "2024-02-01T08:15:00Z",
|
||||
"updated_at": "2024-03-15T16:45:22Z"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Formal Correspondence - Formelle Korrespondenz",
|
||||
"description": "Learn to write professional emails, letters, and formal documents in German business context.",
|
||||
"path_id": "path_advanced_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 101,
|
||||
"ex_type": "formal_writing",
|
||||
"content": "{\"task\": \"Write a formal email requesting a meeting\", \"scenario\": \"You need to schedule a quarterly review with your German business partner\", \"required_elements\": [\"formal salutation\", \"purpose statement\", \"specific time request\", \"polite closing\"], \"vocabulary_bank\": [\"Sehr geehrte Damen und Herren\", \"bezüglich\", \"vereinbaren\", \"Mit freundlichen Grüßen\"]}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"ex_type": "grammar_complex",
|
||||
"content": "{\"topic\": \"Subjunctive II in formal requests\", \"explanation\": \"Use Konjunktiv II for polite requests in business: 'Könnten Sie...', 'Wären Sie so freundlich...', 'Hätten Sie Zeit...'\", \"examples\": [\"Könnten Sie mir bitte den Bericht zusenden?\", \"Wären Sie so freundlich, das zu überprüfen?\"], \"exercise\": \"Transform direct requests into polite subjunctive forms\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"ex_type": "vocabulary_advanced",
|
||||
"content": "{\"category\": \"Business correspondence\", \"terms\": [{\"word\": \"die Anlage\", \"translation\": \"attachment/enclosure\", \"context\": \"formal letters\"}, {\"word\": \"bezüglich\", \"translation\": \"regarding/concerning\", \"context\": \"subject lines\"}, {\"word\": \"unverzüglich\", \"translation\": \"immediately/without delay\", \"context\": \"urgent requests\"}]}",
|
||||
"node_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Meeting and Presentation Language - Besprechungs- und Präsentationssprache",
|
||||
"description": "Develop skills for participating in and leading business meetings, giving presentations, and facilitating discussions.",
|
||||
"path_id": "path_advanced_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 201,
|
||||
"ex_type": "presentation_skills",
|
||||
"content": "{\"scenario\": \"Quarterly sales presentation\", \"structure\": [\"Eröffnung\", \"Agenda\", \"Hauptpunkte\", \"Schlussfolgerung\"], \"phrases\": [\"Darf ich Ihre Aufmerksamkeit haben?\", \"Lassen Sie mich mit... beginnen\", \"Das bringt mich zu meinem nächsten Punkt\", \"Zusammenfassend kann man sagen...\"], \"task\": \"Present Q3 results using formal presentation language\"}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"ex_type": "debate_simulation",
|
||||
"content": "{\"topic\": \"Remote work policies\", \"positions\": [\"Pro remote work\", \"Pro office work\"], \"required_skills\": [\"expressing opinions formally\", \"countering arguments\", \"finding compromises\"], \"vocabulary\": [\"meiner Ansicht nach\", \"hingegen\", \"allerdings\", \"andererseits\", \"einen Kompromiss finden\"]}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 203,
|
||||
"ex_type": "listening_complex",
|
||||
"content": "{\"audio_file\": \"/audio/board_meeting.mp3\", \"duration\": 300, \"complexity\": \"high\", \"accents\": [\"Standard German\", \"Austrian\", \"Swiss German\"], \"task\": \"Extract key decisions and action items from board meeting\", \"questions\": [{\"type\": \"inference\", \"question\": \"What is the underlying concern about the merger?\"}, {\"type\": \"detail\", \"question\": \"When is the deadline for the feasibility study?\"}]}",
|
||||
"node_id": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Negotiation and Conflict Resolution - Verhandlung und Konfliktlösung",
|
||||
"description": "Master the art of negotiating and resolving conflicts in German business environments using diplomatic language and cultural awareness.",
|
||||
"path_id": "path_advanced_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 301,
|
||||
"ex_type": "negotiation_simulation",
|
||||
"content": "{\"scenario\": \"Contract renewal negotiation\", \"your_role\": \"Supplier representative\", \"partner_role\": \"Corporate buyer\", \"objectives\": [\"Maintain current pricing\", \"Extend contract duration\", \"Add performance bonuses\"], \"constraints\": [\"Maximum 5% price increase acceptable\", \"Must maintain quality standards\"], \"diplomatic_phrases\": [\"Ich verstehe Ihre Position, jedoch...\", \"Könnten wir einen Mittelweg finden?\", \"Was wäre, wenn wir...\", \"Das ist durchaus verhandelbar\"]}",
|
||||
"node_id": 3
|
||||
},
|
||||
{
|
||||
"id": 302,
|
||||
"ex_type": "cultural_competence",
|
||||
"content": "{\"situation\": \"German business hierarchy and decision-making\", \"cultural_notes\": [\"Germans value directness but maintain formality\", \"Decision-making can be slow and consensus-based\", \"Punctuality is crucial\", \"Small talk is minimal in business settings\"], \"scenarios\": [{\"context\": \"Disagreeing with a senior colleague\", \"appropriate_response\": \"Mit Verlaub, ich sehe das etwas anders...\"}, {\"context\": \"Requesting urgent action\", \"appropriate_response\": \"Es wäre wichtig, dass wir das zeitnah klären...\"}]}",
|
||||
"node_id": 3
|
||||
},
|
||||
{
|
||||
"id": 303,
|
||||
"ex_type": "conflict_mediation",
|
||||
"content": "{\"scenario\": \"Mediating between two departments with conflicting priorities\", \"techniques\": [\"Active listening\", \"Reframing issues\", \"Finding common ground\", \"Proposing win-win solutions\"], \"language_tools\": [\"Wenn ich Sie richtig verstehe...\", \"Beide Seiten haben berechtigte Anliegen...\", \"Könnten wir das Problem von einer anderen Seite betrachten?\", \"Was wäre für alle Beteiligten akzeptabel?\"]}",
|
||||
"node_id": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Advanced Grammar in Context - Fortgeschrittene Grammatik im Kontext",
|
||||
"description": "Master complex grammatical structures essential for sophisticated business communication, including advanced subordinate clauses and modal constructions.",
|
||||
"path_id": "path_advanced_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 401,
|
||||
"ex_type": "complex_grammar",
|
||||
"content": "{\"topic\": \"Extended participial constructions\", \"explanation\": \"Partizipialkonstruktionen allow complex ideas to be expressed concisely in formal German\", \"examples\": [\"Die im letzten Quartal erzielten Ergebnisse übertreffen unsere Erwartungen.\", \"Der von unserem Team entwickelte Vorschlag wurde angenommen.\"], \"practice\": \"Transform full relative clauses into participial constructions\"}",
|
||||
"node_id": 4
|
||||
},
|
||||
{
|
||||
"id": 402,
|
||||
"ex_type": "modal_constructions",
|
||||
"content": "{\"focus\": \"haben/sein + zu + infinitive vs modal verbs\", \"rules\": [\"haben + zu = müssen (active)\", \"sein + zu = können/müssen (passive)\"], \"examples\": [\"Das ist zu bedenken. (Das muss bedacht werden.)\", \"Wir haben das zu berücksichtigen. (Wir müssen das berücksichtigen.)\"], \"business_context\": \"Formal instructions and obligations\"}",
|
||||
"node_id": 4
|
||||
},
|
||||
{
|
||||
"id": 403,
|
||||
"ex_type": "register_analysis",
|
||||
"content": "{\"task\": \"Identify and correct register mismatches\", \"text_samples\": [{\"text\": \"Hi, könnten Sie mal eben den Vertrag checken?\", \"issue\": \"Mixed informal/formal register\", \"correction\": \"Sehr geehrte/r..., könnten Sie bitte den Vertrag überprüfen?\"}, {\"text\": \"Das ist total wichtig für unser Meeting.\", \"issue\": \"Colloquial intensifier in formal context\", \"correction\": \"Das ist von größter Bedeutung für unsere Besprechung.\"}]}",
|
||||
"node_id": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "Comprehensive Business Case Study - Umfassende Geschäftsfallstudie",
|
||||
"description": "Apply all learned skills in a complex, multi-faceted business scenario requiring advanced German communication across various professional contexts.",
|
||||
"path_id": "path_advanced_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 501,
|
||||
"ex_type": "case_study_comprehensive",
|
||||
"content": "{\"scenario\": \"German manufacturing company considering expansion into renewable energy sector\", \"your_role\": \"External consultant\", \"deliverables\": [\"Market analysis presentation\", \"Risk assessment report\", \"Stakeholder negotiation\", \"Board recommendation\"], \"timeline\": \"4 weeks\", \"complications\": [\"Regulatory changes\", \"Competitor actions\", \"Internal resistance\", \"Budget constraints\"]}",
|
||||
"node_id": 5
|
||||
},
|
||||
{
|
||||
"id": 502,
|
||||
"ex_type": "multi_stakeholder_communication",
|
||||
"content": "{\"stakeholders\": [{\"role\": \"CEO\", \"communication_style\": \"Direct, results-focused\", \"priorities\": [\"ROI\", \"Timeline\", \"Risk mitigation\"]}, {\"role\": \"HR Director\", \"communication_style\": \"Collaborative, people-focused\", \"priorities\": [\"Employee impact\", \"Training needs\", \"Change management\"]}, {\"role\": \"Technical Lead\", \"communication_style\": \"Detail-oriented, analytical\", \"priorities\": [\"Technical feasibility\", \"Quality standards\", \"Implementation challenges\"]}], \"task\": \"Adapt your communication style and content for each stakeholder\"}",
|
||||
"node_id": 5
|
||||
},
|
||||
{
|
||||
"id": 503,
|
||||
"ex_type": "crisis_communication",
|
||||
"content": "{\"crisis\": \"Major supplier bankruptcy affects production schedule\", \"immediate_actions\": [\"Inform key stakeholders\", \"Assess impact\", \"Develop contingency plan\", \"Manage media relations\"], \"communication_channels\": [\"Internal memo\", \"Client notification\", \"Press statement\", \"Investor update\"], \"tone_requirements\": [\"Transparent but reassuring\", \"Professional under pressure\", \"Solution-focused\"]}",
|
||||
"node_id": 5
|
||||
},
|
||||
{
|
||||
"id": 504,
|
||||
"ex_type": "final_assessment",
|
||||
"content": "{\"format\": \"Comprehensive evaluation\", \"components\": [{\"skill\": \"Written communication\", \"task\": \"Draft complete business proposal\", \"criteria\": [\"Formal register\", \"Complex grammar\", \"Persuasive argumentation\", \"Cultural appropriateness\"]}, {\"skill\": \"Oral communication\", \"task\": \"Deliver presentation and handle Q&A\", \"criteria\": [\"Clear articulation\", \"Professional demeanor\", \"Spontaneous responses\", \"Cultural sensitivity\"]}, {\"skill\": \"Interactive communication\", \"task\": \"Lead negotiation simulation\", \"criteria\": [\"Diplomatic language\", \"Conflict resolution\", \"Win-win solutions\", \"Cultural awareness\"]}]}",
|
||||
"node_id": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
149
examples/example_path.json
Normal file
149
examples/example_path.json
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"id": "path_001",
|
||||
"title": "German Basics - Family & Relationships",
|
||||
"description": "Learn essential German vocabulary and grammar related to family members, relationships, and basic social interactions. Perfect for beginners who want to talk about their personal life in German.",
|
||||
"metadata": [
|
||||
{
|
||||
"path_id": "path_001",
|
||||
"version": "1.2.0",
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"updated_at": "2024-03-10T14:22:33Z"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Family Members - Die Familie",
|
||||
"description": "Learn the basic vocabulary for family members and how to introduce your family in German.",
|
||||
"path_id": "path_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 101,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"der Vater\", \"translation\": \"father\", \"audio\": \"/audio/vater.mp3\", \"example\": \"Mein Vater ist Arzt.\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"die Mutter\", \"translation\": \"mother\", \"audio\": \"/audio/mutter.mp3\", \"example\": \"Meine Mutter kocht gern.\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"der Bruder\", \"translation\": \"brother\", \"audio\": \"/audio/bruder.mp3\", \"example\": \"Ich habe einen Bruder.\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"ex_type": "multiple_choice",
|
||||
"content": "{\"question\": \"How do you say 'sister' in German?\", \"options\": [\"die Schwester\", \"der Schwester\", \"das Schwester\", \"die Schwestern\"], \"correct\": 0, \"explanation\": \"'Die Schwester' is feminine, so it uses the article 'die'.\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"ex_type": "fill_blank",
|
||||
"content": "{\"sentence\": \"Meine ____ ist sehr nett.\", \"answer\": \"Schwester\", \"hint\": \"female sibling\", \"translation\": \"My sister is very nice.\"}",
|
||||
"node_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Possessive Pronouns - Possessivpronomen",
|
||||
"description": "Master the use of possessive pronouns (mein, dein, sein, ihr) when talking about family and relationships.",
|
||||
"path_id": "path_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 201,
|
||||
"ex_type": "grammar_explanation",
|
||||
"content": "{\"topic\": \"Possessive Pronouns\", \"explanation\": \"German possessive pronouns change based on the gender and case of the noun they modify. 'Mein' (my), 'dein' (your), 'sein' (his), 'ihr' (her).\", \"examples\": [\"mein Vater\", \"meine Mutter\", \"dein Bruder\", \"ihre Schwester\"]}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"ex_type": "drag_drop",
|
||||
"content": "{\"instruction\": \"Match the possessive pronoun with the correct family member\", \"pairs\": [{\"left\": \"mein\", \"right\": \"Vater\"}, {\"left\": \"meine\", \"right\": \"Mutter\"}, {\"left\": \"sein\", \"right\": \"Bruder\"}, {\"left\": \"ihre\", \"right\": \"Schwester\"}]}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 203,
|
||||
"ex_type": "translation",
|
||||
"content": "{\"english\": \"My father works in Berlin.\", \"german\": \"Mein Vater arbeitet in Berlin.\", \"hints\": [\"possessive pronoun\", \"verb conjugation\", \"preposition\"]}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 204,
|
||||
"ex_type": "multiple_choice",
|
||||
"content": "{\"question\": \"Complete: '_____ Tochter ist 5 Jahre alt.' (His daughter is 5 years old)\", \"options\": [\"Sein\", \"Seine\", \"Ihrer\", \"Ihre\"], \"correct\": 1, \"explanation\": \"'Tochter' is feminine, so 'sein' becomes 'seine'.\"}",
|
||||
"node_id": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Describing Relationships - Beziehungen beschreiben",
|
||||
"description": "Learn how to describe family relationships, marital status, and social connections in German.",
|
||||
"path_id": "path_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 301,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"verheiratet\", \"translation\": \"married\", \"audio\": \"/audio/verheiratet.mp3\", \"example\": \"Sie ist verheiratet.\"}",
|
||||
"node_id": 3
|
||||
},
|
||||
{
|
||||
"id": 302,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"ledig\", \"translation\": \"single/unmarried\", \"audio\": \"/audio/ledig.mp3\", \"example\": \"Er ist noch ledig.\"}",
|
||||
"node_id": 3
|
||||
},
|
||||
{
|
||||
"id": 303,
|
||||
"ex_type": "conversation",
|
||||
"content": "{\"scenario\": \"Introducing your family at a party\", \"dialogue\": [{\"speaker\": \"A\", \"text\": \"Ist das deine Familie?\"}, {\"speaker\": \"B\", \"text\": \"Ja, das sind meine Eltern und mein Bruder.\"}, {\"speaker\": \"A\", \"text\": \"Wie alt ist dein Bruder?\"}, {\"speaker\": \"B\", \"text\": \"Er ist 25 Jahre alt und arbeitet als Lehrer.\"}], \"vocabulary_focus\": [\"Familie\", \"Eltern\", \"Alter\", \"Beruf\"]}",
|
||||
"node_id": 3
|
||||
},
|
||||
{
|
||||
"id": 304,
|
||||
"ex_type": "listening",
|
||||
"content": "{\"audio_file\": \"/audio/family_description.mp3\", \"transcript\": \"Hallo, ich heiße Anna. Ich bin verheiratet und habe zwei Kinder. Mein Mann arbeitet als Ingenieur und meine Tochter geht noch zur Schule.\", \"questions\": [{\"question\": \"Wie heißt die Frau?\", \"answer\": \"Anna\"}, {\"question\": \"Ist sie verheiratet?\", \"answer\": \"Ja\"}, {\"question\": \"Was ist ihr Mann von Beruf?\", \"answer\": \"Ingenieur\"}]}",
|
||||
"node_id": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Practice & Review - Übung und Wiederholung",
|
||||
"description": "Comprehensive review of all concepts learned in this path through mixed exercises and real-world scenarios.",
|
||||
"path_id": "path_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 401,
|
||||
"ex_type": "story_completion",
|
||||
"content": "{\"story\": \"Maria stellt ihre Familie vor. Sie sagt: 'Das ist ___ Familie. ___ Vater ist Arzt und ___ Mutter ist Lehrerin. Ich habe auch einen ___ und eine ___. Wir sind eine große und glückliche Familie.'\", \"blanks\": [\"meine\", \"Mein\", \"meine\", \"Bruder\", \"Schwester\"], \"context\": \"Family introduction story\"}",
|
||||
"node_id": 4
|
||||
},
|
||||
{
|
||||
"id": 402,
|
||||
"ex_type": "speaking",
|
||||
"content": "{\"prompt\": \"Describe your family in German. Include at least 3 family members and use possessive pronouns.\", \"expected_elements\": [\"possessive pronouns\", \"family vocabulary\", \"complete sentences\"], \"time_limit\": 60}",
|
||||
"node_id": 4
|
||||
},
|
||||
{
|
||||
"id": 403,
|
||||
"ex_type": "comprehensive_quiz",
|
||||
"content": "{\"questions\": [{\"type\": \"multiple_choice\", \"question\": \"How do you say 'my parents' in German?\", \"options\": [\"meine Eltern\", \"mein Eltern\", \"meinen Eltern\", \"meiner Eltern\"], \"correct\": 0}, {\"type\": \"translation\", \"english\": \"Her husband is very kind.\", \"german\": \"Ihr Mann ist sehr nett.\"}, {\"type\": \"vocabulary\", \"definition\": \"A female parent\", \"answer\": \"die Mutter\"}]}",
|
||||
"node_id": 4
|
||||
},
|
||||
{
|
||||
"id": 404,
|
||||
"ex_type": "role_play",
|
||||
"content": "{\"scenario\": \"You're at a family gathering. Practice introducing different family members to a friend.\", \"roles\": [\"You\", \"Friend\"], \"objectives\": [\"Use correct possessive pronouns\", \"Introduce at least 4 family members\", \"Ask questions about the friend's family\"], \"vocabulary_bank\": [\"Großmutter\", \"Großvater\", \"Onkel\", \"Tante\", \"Cousin\", \"Cousine\"]}",
|
||||
"node_id": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
67
examples/simple_path.json
Normal file
67
examples/simple_path.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "path_beginner_001",
|
||||
"title": "German Greetings - Erste Begrüßungen",
|
||||
"description": "Learn basic German greetings and polite expressions. Your first steps into the German language!",
|
||||
"metadata": [
|
||||
{
|
||||
"path_id": "path_beginner_001",
|
||||
"version": "1.0.0",
|
||||
"created_at": "2024-01-10T09:00:00Z",
|
||||
"updated_at": "2024-01-10T09:00:00Z"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Hello & Goodbye - Hallo & Tschüss",
|
||||
"description": "Learn the most common ways to say hello and goodbye in German.",
|
||||
"path_id": "path_beginner_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 101,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"Hallo\", \"translation\": \"Hello\", \"audio\": \"/audio/hallo.mp3\", \"example\": \"Hallo, wie geht's?\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"Tschüss\", \"translation\": \"Bye\", \"audio\": \"/audio/tschuess.mp3\", \"example\": \"Tschüss, bis bald!\"}",
|
||||
"node_id": 1
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"ex_type": "multiple_choice",
|
||||
"content": "{\"question\": \"How do you say 'Hello' in German?\", \"options\": [\"Hallo\", \"Danke\", \"Bitte\", \"Tschüss\"], \"correct\": 0, \"explanation\": \"'Hallo' is the most common way to say hello in German.\"}",
|
||||
"node_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Please & Thank You - Bitte & Danke",
|
||||
"description": "Master the magic words of politeness in German.",
|
||||
"path_id": "path_beginner_001",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 201,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"Danke\", \"translation\": \"Thank you\", \"audio\": \"/audio/danke.mp3\", \"example\": \"Danke schön!\"}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"Bitte\", \"translation\": \"Please/You're welcome\", \"audio\": \"/audio/bitte.mp3\", \"example\": \"Bitte schön!\"}",
|
||||
"node_id": 2
|
||||
},
|
||||
{
|
||||
"id": 203,
|
||||
"ex_type": "conversation",
|
||||
"content": "{\"scenario\": \"Simple polite exchange\", \"dialogue\": [{\"speaker\": \"A\", \"text\": \"Danke!\"}, {\"speaker\": \"B\", \"text\": \"Bitte schön!\"}], \"vocabulary_focus\": [\"Danke\", \"Bitte\"]}",
|
||||
"node_id": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
486
examples/test_repository_functions.rs
Normal file
486
examples/test_repository_functions.rs
Normal file
@@ -0,0 +1,486 @@
|
||||
use std::path::Path;
|
||||
|
||||
// This is a test/example file demonstrating how to use the new repository functions
|
||||
// Note: This requires the database to be set up and proper imports
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::Utc;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::models::{
|
||||
exercise::Exercise,
|
||||
node::Node,
|
||||
path::{Metadata, Path},
|
||||
};
|
||||
use crate::repositories::{
|
||||
path_json_utils::PathJsonUtils,
|
||||
repository_manager::RepositoryManager,
|
||||
};
|
||||
|
||||
async fn setup_test_database() -> SqlitePool {
|
||||
// This would normally connect to a test database
|
||||
// For demonstration purposes only
|
||||
todo!("Setup test database connection")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_save_and_retrieve_path() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create a test path
|
||||
let test_path = create_sample_path();
|
||||
|
||||
// Save the path
|
||||
let saved_path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(test_path.clone())
|
||||
.await
|
||||
.expect("Failed to save path");
|
||||
|
||||
println!("Saved path with ID: {}", saved_path_id);
|
||||
|
||||
// Retrieve the path
|
||||
let path_id_int = saved_path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let retrieved_path = repo_manager
|
||||
.paths()
|
||||
.get_path_by_id(path_id_int)
|
||||
.await
|
||||
.expect("Failed to retrieve path");
|
||||
|
||||
// Verify the data
|
||||
assert_eq!(retrieved_path.id, test_path.id);
|
||||
assert_eq!(retrieved_path.title, test_path.title);
|
||||
assert_eq!(retrieved_path.nodes.len(), test_path.nodes.len());
|
||||
|
||||
println!("✅ Successfully saved and retrieved path!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_path() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save initial path
|
||||
let mut test_path = create_sample_path();
|
||||
let path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(test_path.clone())
|
||||
.await
|
||||
.expect("Failed to save path");
|
||||
|
||||
// Modify the path
|
||||
test_path.title = "Updated Path Title".to_string();
|
||||
test_path.description = "Updated description with new content".to_string();
|
||||
|
||||
// Add a new node
|
||||
let new_node = Node {
|
||||
id: 999, // This will be auto-assigned
|
||||
title: "New Node".to_string(),
|
||||
description: "A newly added node".to_string(),
|
||||
path_id: test_path.id.clone(),
|
||||
exercises: vec![Exercise {
|
||||
id: 999,
|
||||
ex_type: "vocabulary".to_string(),
|
||||
content: r#"{"word": "neu", "translation": "new", "example": "Das ist neu."}"#
|
||||
.to_string(),
|
||||
node_id: 999,
|
||||
}],
|
||||
};
|
||||
test_path.nodes.push(new_node);
|
||||
|
||||
// Update the path
|
||||
repo_manager
|
||||
.paths()
|
||||
.update_path(test_path.clone())
|
||||
.await
|
||||
.expect("Failed to update path");
|
||||
|
||||
// Retrieve and verify
|
||||
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let updated_path = repo_manager
|
||||
.paths()
|
||||
.get_path_by_id(path_id_int)
|
||||
.await
|
||||
.expect("Failed to retrieve updated path");
|
||||
|
||||
assert_eq!(updated_path.title, "Updated Path Title");
|
||||
assert_eq!(updated_path.nodes.len(), 3); // Original 2 + 1 new
|
||||
|
||||
println!("✅ Successfully updated path!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_clone_path() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save original path
|
||||
let original_path = create_sample_path();
|
||||
let original_path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(original_path.clone())
|
||||
.await
|
||||
.expect("Failed to save original path");
|
||||
|
||||
// Clone the path
|
||||
let original_id_int = original_path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let cloned_path_id = repo_manager
|
||||
.clone_path_complete(
|
||||
original_id_int,
|
||||
"cloned_path_001",
|
||||
"Cloned German Basics",
|
||||
)
|
||||
.await
|
||||
.expect("Failed to clone path");
|
||||
|
||||
// Retrieve the cloned path
|
||||
let cloned_id_int = cloned_path_id.parse::<i32>().unwrap_or(0);
|
||||
let cloned_path = repo_manager
|
||||
.paths()
|
||||
.get_path_by_id(cloned_id_int)
|
||||
.await
|
||||
.expect("Failed to retrieve cloned path");
|
||||
|
||||
// Verify clone
|
||||
assert_eq!(cloned_path.id, "cloned_path_001");
|
||||
assert_eq!(cloned_path.title, "Cloned German Basics");
|
||||
assert_eq!(cloned_path.nodes.len(), original_path.nodes.len());
|
||||
|
||||
println!("✅ Successfully cloned path!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_json_import_export() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create sample JSON
|
||||
let json_content = r#"
|
||||
{
|
||||
"id": "test_json_path",
|
||||
"title": "JSON Test Path",
|
||||
"description": "Testing JSON import/export functionality",
|
||||
"metadata": [
|
||||
{
|
||||
"path_id": "test_json_path",
|
||||
"version": "1.0.0",
|
||||
"created_at": "2024-01-01T12:00:00Z",
|
||||
"updated_at": "2024-01-01T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "JSON Test Node",
|
||||
"description": "Testing node from JSON",
|
||||
"path_id": "test_json_path",
|
||||
"exercises": [
|
||||
{
|
||||
"id": 1,
|
||||
"ex_type": "vocabulary",
|
||||
"content": "{\"word\": \"Test\", \"translation\": \"Test\", \"example\": \"This is a test.\"}",
|
||||
"node_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"#;
|
||||
|
||||
// Import from JSON
|
||||
let imported_path_id = repo_manager
|
||||
.import_path_from_json(json_content)
|
||||
.await
|
||||
.expect("Failed to import path from JSON");
|
||||
|
||||
println!("Imported path ID: {}", imported_path_id);
|
||||
|
||||
// Export back to JSON
|
||||
let path_id_int = imported_path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let exported_json = repo_manager
|
||||
.export_path_to_json(path_id_int)
|
||||
.await
|
||||
.expect("Failed to export path to JSON");
|
||||
|
||||
println!("Exported JSON length: {} characters", exported_json.len());
|
||||
|
||||
// Verify the exported JSON contains expected content
|
||||
assert!(exported_json.contains("JSON Test Path"));
|
||||
assert!(exported_json.contains("test_json_path"));
|
||||
|
||||
println!("✅ Successfully imported and exported JSON!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_path_statistics() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save path
|
||||
let test_path = create_sample_path();
|
||||
let path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(test_path)
|
||||
.await
|
||||
.expect("Failed to save path");
|
||||
|
||||
// Get statistics
|
||||
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let stats = repo_manager
|
||||
.get_path_statistics(path_id_int)
|
||||
.await
|
||||
.expect("Failed to get path statistics");
|
||||
|
||||
// Print statistics
|
||||
stats.print_detailed_summary();
|
||||
|
||||
// Verify statistics
|
||||
assert_eq!(stats.node_count, 2);
|
||||
assert_eq!(stats.total_exercises, 3);
|
||||
assert!(stats.exercise_types.contains_key("vocabulary"));
|
||||
assert!(stats.exercise_types.contains_key("multiple_choice"));
|
||||
|
||||
println!("✅ Successfully generated path statistics!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_path_validation() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save path
|
||||
let test_path = create_sample_path();
|
||||
let path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(test_path)
|
||||
.await
|
||||
.expect("Failed to save path");
|
||||
|
||||
// Validate path integrity
|
||||
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let issues = repo_manager
|
||||
.validate_path_integrity(path_id_int)
|
||||
.await
|
||||
.expect("Failed to validate path");
|
||||
|
||||
if issues.is_empty() {
|
||||
println!("✅ Path validation passed - no issues found!");
|
||||
} else {
|
||||
println!("⚠️ Path validation found issues:");
|
||||
for issue in &issues {
|
||||
println!(" - {}", issue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_functionality() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save multiple paths for searching
|
||||
let path1 = create_sample_path();
|
||||
let mut path2 = create_sample_path();
|
||||
path2.id = "search_test_002".to_string();
|
||||
path2.title = "Advanced German Grammar".to_string();
|
||||
path2.description = "Complex grammatical structures and advanced vocabulary".to_string();
|
||||
|
||||
repo_manager
|
||||
.paths()
|
||||
.save_path(path1)
|
||||
.await
|
||||
.expect("Failed to save path1");
|
||||
repo_manager
|
||||
.paths()
|
||||
.save_path(path2)
|
||||
.await
|
||||
.expect("Failed to save path2");
|
||||
|
||||
// Search for paths
|
||||
let search_results = repo_manager
|
||||
.search_paths("German")
|
||||
.await
|
||||
.expect("Failed to search paths");
|
||||
|
||||
println!("Search results for 'German':");
|
||||
for result in &search_results {
|
||||
result.print_summary();
|
||||
println!();
|
||||
}
|
||||
|
||||
assert!(!search_results.is_empty());
|
||||
println!("✅ Search functionality working correctly!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_operations() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Create and save path
|
||||
let test_path = create_sample_path();
|
||||
let path_id = repo_manager
|
||||
.paths()
|
||||
.save_path(test_path)
|
||||
.await
|
||||
.expect("Failed to save path");
|
||||
|
||||
// Verify path exists
|
||||
let path_id_int = path_id.parse::<i32>().expect("Invalid path ID");
|
||||
let path_exists = repo_manager
|
||||
.paths()
|
||||
.path_exists(path_id_int)
|
||||
.await
|
||||
.expect("Failed to check path existence");
|
||||
assert!(path_exists);
|
||||
|
||||
// Delete the path
|
||||
repo_manager
|
||||
.paths()
|
||||
.delete_path(path_id_int)
|
||||
.await
|
||||
.expect("Failed to delete path");
|
||||
|
||||
// Verify path no longer exists
|
||||
let path_still_exists = repo_manager
|
||||
.paths()
|
||||
.path_exists(path_id_int)
|
||||
.await
|
||||
.expect("Failed to check path existence after deletion");
|
||||
assert!(!path_still_exists);
|
||||
|
||||
println!("✅ Successfully deleted path and verified removal!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_statistics() {
|
||||
let pool = setup_test_database().await;
|
||||
let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Get database statistics
|
||||
let stats = repo_manager
|
||||
.get_stats()
|
||||
.await
|
||||
.expect("Failed to get database statistics");
|
||||
|
||||
println!("=== Database Statistics ===");
|
||||
println!("Total paths: {}", stats.path_count);
|
||||
println!("Total nodes: {}", stats.node_count);
|
||||
println!("Total exercises: {}", stats.exercise_count);
|
||||
println!("Total metadata records: {}", stats.metadata_count);
|
||||
println!("Total records: {}", stats.total_records());
|
||||
println!("Database empty: {}", stats.is_empty());
|
||||
|
||||
println!("✅ Successfully retrieved database statistics!");
|
||||
}
|
||||
|
||||
// Helper function to create a sample path for testing
|
||||
fn create_sample_path() -> Path {
|
||||
let now = Utc::now();
|
||||
|
||||
let metadata = vec![Metadata {
|
||||
path_id: "test_path_001".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}];
|
||||
|
||||
let exercises1 = vec![
|
||||
Exercise {
|
||||
id: 1,
|
||||
ex_type: "vocabulary".to_string(),
|
||||
content: r#"{"word": "Hallo", "translation": "Hello", "audio": "/audio/hallo.mp3", "example": "Hallo, wie geht's?"}"#.to_string(),
|
||||
node_id: 1,
|
||||
},
|
||||
Exercise {
|
||||
id: 2,
|
||||
ex_type: "multiple_choice".to_string(),
|
||||
content: r#"{"question": "How do you say 'goodbye' in German?", "options": ["Tschüss", "Hallo", "Bitte", "Danke"], "correct": 0, "explanation": "Tschüss is the informal way to say goodbye."}"#.to_string(),
|
||||
node_id: 1,
|
||||
}
|
||||
];
|
||||
|
||||
let exercises2 = vec![Exercise {
|
||||
id: 3,
|
||||
ex_type: "vocabulary".to_string(),
|
||||
content: r#"{"word": "Danke", "translation": "Thank you", "audio": "/audio/danke.mp3", "example": "Danke schön!"}"#.to_string(),
|
||||
node_id: 2,
|
||||
}];
|
||||
|
||||
let nodes = vec![
|
||||
Node {
|
||||
id: 1,
|
||||
title: "Basic Greetings".to_string(),
|
||||
description: "Learn essential German greetings".to_string(),
|
||||
path_id: "test_path_001".to_string(),
|
||||
exercises: exercises1,
|
||||
},
|
||||
Node {
|
||||
id: 2,
|
||||
title: "Politeness".to_string(),
|
||||
description: "Learn polite expressions".to_string(),
|
||||
path_id: "test_path_001".to_string(),
|
||||
exercises: exercises2,
|
||||
},
|
||||
];
|
||||
|
||||
Path {
|
||||
id: "test_path_001".to_string(),
|
||||
title: "German Basics Test".to_string(),
|
||||
description: "A test path for demonstrating repository functionality".to_string(),
|
||||
metadata,
|
||||
nodes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage functions (not tests)
|
||||
pub mod examples {
|
||||
use super::*;
|
||||
|
||||
/// Example: How to use the repository manager in your application
|
||||
pub async fn example_basic_usage() {
|
||||
println!("=== Basic Repository Usage Example ===");
|
||||
|
||||
// This would normally use your actual database connection
|
||||
// let pool = get_database_pool().await;
|
||||
// let repo_manager = RepositoryManager::new(&pool);
|
||||
|
||||
// Example operations:
|
||||
println!("1. Create repository manager");
|
||||
println!("2. Save a new path");
|
||||
println!("3. Retrieve and display path");
|
||||
println!("4. Update path content");
|
||||
println!("5. Search for paths");
|
||||
println!("6. Generate statistics");
|
||||
println!("7. Export to JSON");
|
||||
println!("8. Cleanup/delete if needed");
|
||||
}
|
||||
|
||||
/// Example: How to work with JSON imports
|
||||
pub async fn example_json_workflow() {
|
||||
println!("=== JSON Import/Export Workflow ===");
|
||||
|
||||
// Steps for JSON workflow:
|
||||
println!("1. Validate JSON file structure");
|
||||
println!("2. Import path from JSON");
|
||||
println!("3. Verify import success");
|
||||
println!("4. Make modifications if needed");
|
||||
println!("5. Export updated version");
|
||||
println!("6. Backup all paths to JSON files");
|
||||
}
|
||||
|
||||
/// Example: How to perform bulk operations
|
||||
pub async fn example_bulk_operations() {
|
||||
println!("=== Bulk Operations Example ===");
|
||||
|
||||
// Bulk operation examples:
|
||||
println!("1. Import multiple paths from directory");
|
||||
println!("2. Validate all paths in database");
|
||||
println!("3. Generate statistics for all paths");
|
||||
println!("4. Search across all content");
|
||||
println!("5. Export all paths to backup directory");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user