diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 896bb4f..1fe1dcd 100644 Binary files a/config/__pycache__/__init__.cpython-311.pyc and b/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index d79d6a7..97325ee 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 8cf22af..8df0882 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index a1b4aa7..2202a48 100644 Binary files a/config/__pycache__/wsgi.cpython-311.pyc and b/config/__pycache__/wsgi.cpython-311.pyc differ diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 3f553f6..fb20891 100644 Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 5e8987a..8f482c6 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 2fa4a49..49d5f62 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf223..e1dde4f 100644 Binary files a/core/__pycache__/context_processors.cpython-311.pyc and b/core/__pycache__/context_processors.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..fe54f61 Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index a251b5f..cffc914 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index f705988..821d1f4 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2f0989c..733da46 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 8c38f3f..0b4c1a6 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,3 +1,43 @@ from django.contrib import admin -# Register your models here. +from .models import ActionPlanStep, ProblemCase, RootCause, SolutionOption + + +class RootCauseInline(admin.TabularInline): + model = RootCause + extra = 0 + + +class SolutionOptionInline(admin.TabularInline): + model = SolutionOption + extra = 0 + fields = ("rank", "title", "impact", "efficiency", "speed", "low_risk", "decision_score", "success_rate") + readonly_fields = ("decision_score",) + + +@admin.register(ProblemCase) +class ProblemCaseAdmin(admin.ModelAdmin): + list_display = ("title", "business_area", "urgency", "priority_score", "financial_impact", "status", "created_at") + list_filter = ("business_area", "status", "urgency") + search_fields = ("title", "description") + inlines = [RootCauseInline, SolutionOptionInline] + + +@admin.register(SolutionOption) +class SolutionOptionAdmin(admin.ModelAdmin): + list_display = ("title", "problem", "rank", "decision_score", "success_rate") + list_filter = ("rank",) + search_fields = ("title", "problem__title") + + +@admin.register(ActionPlanStep) +class ActionPlanStepAdmin(admin.ModelAdmin): + list_display = ("solution", "day_index", "title", "is_done") + list_filter = ("is_done",) + search_fields = ("title", "task", "solution__title") + + +@admin.register(RootCause) +class RootCauseAdmin(admin.ModelAdmin): + list_display = ("factor", "problem", "contribution_score", "parent") + search_fields = ("factor", "why_chain", "problem__title") diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..ae1e584 --- /dev/null +++ b/core/forms.py @@ -0,0 +1,40 @@ +from django import forms + +from .models import ProblemCase + + +class ProblemCaseForm(forms.ModelForm): + class Meta: + model = ProblemCase + fields = ["description", "urgency"] + labels = { + "description": "Masukkan masalah, target, atau hambatan Anda:", + "urgency": "Tingkat Urgensi", + } + widgets = { + "description": forms.Textarea(attrs={ + "class": "form-control problem-textarea", + "rows": 9, + "placeholder": "Contoh: Kuliah di Singapura atau Jepang, dana 200 juta, ingin 3 tahun selesai.", + }), + "urgency": forms.TextInput(attrs={ + "type": "range", + "class": "form-range urgency-slider", + "min": 1, + "max": 5, + "step": 1, + "oninput": "document.getElementById('urgency-output').value=this.value", + }), + } + + def clean_description(self): + description = self.cleaned_data["description"].strip() + if len(description) < 20: + raise forms.ValidationError("Tuliskan minimal 20 karakter agar analisis lebih bermakna.") + return description + + def clean_urgency(self): + urgency = self.cleaned_data["urgency"] + if urgency < 1 or urgency > 5: + raise forms.ValidationError("Urgensi harus berada pada skala 1 sampai 5.") + return urgency diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..08cde5a --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,82 @@ +# Generated by Django 5.2.7 on 2026-06-19 14:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='ProblemCase', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=160, verbose_name='judul kasus')), + ('description', models.TextField(verbose_name='deskripsi masalah')), + ('business_area', models.CharField(choices=[('sales', 'Penjualan'), ('operations', 'Operasional'), ('finance', 'Keuangan'), ('marketing', 'Marketing'), ('product', 'Produk/Layanan'), ('people', 'Tim & SDM'), ('other', 'Lainnya')], default='sales', max_length=32, verbose_name='area bisnis')), + ('urgency', models.PositiveSmallIntegerField(default=3, verbose_name='urgensi')), + ('priority_score', models.PositiveSmallIntegerField(default=0, verbose_name='skor prioritas')), + ('financial_impact', models.CharField(default='Sedang', max_length=32, verbose_name='dampak finansial')), + ('status', models.CharField(choices=[('draft', 'Draft'), ('analyzed', 'Sudah dianalisis')], default='draft', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Problem Case', + 'verbose_name_plural': 'Problem Cases', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='RootCause', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('factor', models.CharField(max_length=120)), + ('contribution_score', models.PositiveSmallIntegerField(default=70)), + ('why_chain', models.TextField()), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='core.rootcause')), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='root_causes', to='core.problemcase')), + ], + options={ + 'ordering': ['-contribution_score', 'factor'], + }, + ), + migrations.CreateModel( + name='SolutionOption', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=160)), + ('impact', models.PositiveSmallIntegerField(default=70)), + ('efficiency', models.PositiveSmallIntegerField(default=70)), + ('speed', models.PositiveSmallIntegerField(default=70)), + ('low_risk', models.PositiveSmallIntegerField(default=70)), + ('decision_score', models.DecimalField(decimal_places=2, default=0, max_digits=5)), + ('success_rate', models.PositiveSmallIntegerField(default=70)), + ('rank', models.PositiveSmallIntegerField(default=1)), + ('rationale', models.TextField()), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='core.problemcase')), + ], + options={ + 'ordering': ['rank', '-decision_score'], + }, + ), + migrations.CreateModel( + name='ActionPlanStep', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('day_index', models.PositiveSmallIntegerField()), + ('title', models.CharField(max_length=120)), + ('task', models.TextField()), + ('is_done', models.BooleanField(default=False)), + ('solution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_steps', to='core.solutionoption')), + ], + options={ + 'ordering': ['day_index'], + }, + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..773b101 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 7995815..7b257e2 100644 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and b/core/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..9fe3bc3 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,107 @@ from django.db import models +from django.urls import reverse -# Create your models here. + +class ProblemCase(models.Model): + AREA_SALES = "sales" + AREA_OPERATIONS = "operations" + AREA_FINANCE = "finance" + AREA_MARKETING = "marketing" + AREA_PRODUCT = "product" + AREA_PEOPLE = "people" + AREA_OTHER = "other" + + BUSINESS_AREA_CHOICES = [ + (AREA_SALES, "Penjualan"), + (AREA_OPERATIONS, "Operasional"), + (AREA_FINANCE, "Keuangan"), + (AREA_MARKETING, "Marketing"), + (AREA_PRODUCT, "Produk/Layanan"), + (AREA_PEOPLE, "Tim & SDM"), + (AREA_OTHER, "Lainnya"), + ] + + STATUS_DRAFT = "draft" + STATUS_ANALYZED = "analyzed" + STATUS_CHOICES = [ + (STATUS_DRAFT, "Draft"), + (STATUS_ANALYZED, "Sudah dianalisis"), + ] + + title = models.CharField("judul kasus", max_length=160) + description = models.TextField("deskripsi masalah") + business_area = models.CharField( + "area bisnis", max_length=32, choices=BUSINESS_AREA_CHOICES, default=AREA_SALES + ) + urgency = models.PositiveSmallIntegerField("urgensi", default=3) + priority_score = models.PositiveSmallIntegerField("skor prioritas", default=0) + financial_impact = models.CharField("dampak finansial", max_length=32, default="Sedang") + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_DRAFT) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ["-created_at"] + verbose_name = "Problem Case" + verbose_name_plural = "Problem Cases" + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("case_detail", kwargs={"pk": self.pk}) + + +class RootCause(models.Model): + problem = models.ForeignKey( + ProblemCase, related_name="root_causes", on_delete=models.CASCADE + ) + parent = models.ForeignKey( + "self", related_name="children", null=True, blank=True, on_delete=models.CASCADE + ) + factor = models.CharField(max_length=120) + contribution_score = models.PositiveSmallIntegerField(default=70) + why_chain = models.TextField() + + class Meta: + ordering = ["-contribution_score", "factor"] + + def __str__(self): + return f"{self.factor} ({self.contribution_score}%)" + + +class SolutionOption(models.Model): + problem = models.ForeignKey( + ProblemCase, related_name="solutions", on_delete=models.CASCADE + ) + title = models.CharField(max_length=160) + impact = models.PositiveSmallIntegerField(default=70) + efficiency = models.PositiveSmallIntegerField(default=70) + speed = models.PositiveSmallIntegerField(default=70) + low_risk = models.PositiveSmallIntegerField(default=70) + decision_score = models.DecimalField(max_digits=5, decimal_places=2, default=0) + success_rate = models.PositiveSmallIntegerField(default=70) + rank = models.PositiveSmallIntegerField(default=1) + rationale = models.TextField() + + class Meta: + ordering = ["rank", "-decision_score"] + + def __str__(self): + return self.title + + +class ActionPlanStep(models.Model): + solution = models.ForeignKey( + SolutionOption, related_name="action_steps", on_delete=models.CASCADE + ) + day_index = models.PositiveSmallIntegerField() + title = models.CharField(max_length=120) + task = models.TextField() + is_done = models.BooleanField(default=False) + + class Meta: + ordering = ["day_index"] + + def __str__(self): + return f"Hari {self.day_index}: {self.title}" diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..734e268 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,11 +1,13 @@ +{% load static %} - + - {% block title %}Knowledge Base{% endblock %} + + {% block title %}{{ page_title|default:"OPTEMA AI" }}{% endblock %} + {% if project_description %} - {% endif %} @@ -13,13 +15,17 @@ {% endif %} - {% load static %} + + + + {% block head %}{% endblock %} {% block content %}{% endblock %} + diff --git a/core/templates/core/_nav.html b/core/templates/core/_nav.html new file mode 100644 index 0000000..c3b431c --- /dev/null +++ b/core/templates/core/_nav.html @@ -0,0 +1,17 @@ + diff --git a/core/templates/core/case_detail.html b/core/templates/core/case_detail.html new file mode 100644 index 0000000..8bb6269 --- /dev/null +++ b/core/templates/core/case_detail.html @@ -0,0 +1,124 @@ +{% extends "base.html" %} +{% block title %}{{ page_title }}{% endblock %} +{% block content %} +{% include "core/_nav.html" %} +
+
+
+ {% if messages %} +
+ {% for message in messages %} + + {% endfor %} +
+ {% endif %} +
+
+

Analisis Tersimpan · {{ problem.get_business_area_display }}

+

{{ problem.title }}

+

{{ problem.description }}

+
+
+
+
✅ Analisis Berhasil Disimulasikan!
+

Case ini tersimpan di database lokal dan bisa dibuka ulang dari Riwayat Case.

+
+
+
+
+
+ +
+
+
Problem Detection
+

1. Problem Detection

+
+
Prioritas Skor{{ problem.priority_score }}/100
+
Dampak Finansial{{ problem.financial_impact }}
+
+
+ +
+
+
+
Root Cause Analysis
+

2. Root Cause (Akar Masalah)

+
+ {% for cause in problem.root_causes.all %} +
+
{{ cause.factor }}{{ cause.contribution_score }}%
+
+
+
+

{{ cause.why_chain }}

+
+ {% endfor %} +
+
+
+
+
+
Solution & Decision Scoring
+

3. Rekomendasi Solusi & Prediksi Sukses

+

Rumus: Decision Score = Impact×0.4 + Efficiency×0.3 + Speed×0.2 + LowRisk×0.1

+
+ + + + + + + + + + + + + + + {% for solution in problem.solutions.all %} + + + + + + + + + + + {% endfor %} + +
RankSolusiImpactEfficiencySpeedLow RiskSukses RateDecision Score
#{{ solution.rank }} + {{ solution.title }} +

{{ solution.rationale }}

+
{{ solution.impact }}{{ solution.efficiency }}{{ solution.speed }}{{ solution.low_risk }}{{ solution.success_rate }}%{{ solution.decision_score|floatformat:1 }}
+
+
+ + {% if top_solution %} +
+
Action Plan
+

4. Action Plan 5 Hari: {{ top_solution.title }}

+
+ {% for step in top_solution.action_steps.all %} +
+ Hari {{ step.day_index }} +
+

{{ step.title }}

+

{{ step.task }}

+
+
+ {% endfor %} +
+
+ {% endif %} +
+
+ +
+
+{% endblock %} diff --git a/core/templates/core/case_list.html b/core/templates/core/case_list.html new file mode 100644 index 0000000..e1f9949 --- /dev/null +++ b/core/templates/core/case_list.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block title %}{{ page_title }}{% endblock %} +{% block content %} +{% include "core/_nav.html" %} +
+
+
+
+

Case Library

+

Daftar Kasus OPTEMA

+

Histori masalah yang sudah diproses menjadi keputusan dan action plan.

+
+ Tambah Kasus Baru +
+
+ {% for case in cases %} + + {% empty %} +
+
+

Case library masih kosong.

+

Buat analisis pertama untuk mulai membangun histori keputusan.

+ Mulai Analisis +
+
+ {% endfor %} +
+
+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..b4b9bdd 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,201 @@ {% extends "base.html" %} +{% load static %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% block title %}{{ page_title }}{% endblock %} {% block content %} +{% include "core/_nav.html" %} +
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+ + +
+
+
+

Mesin Pemecah Masalah Universal & Decision Intelligence Platform

+

💡 OPTEMA AI

+

Optimal, Effective, Efficient Management Assistant

+

MVP single-user tanpa login: masukkan masalah bisnis, keuangan, karier, logistik, teknologi, atau pendidikan; atur urgensi, lalu dapatkan simulasi Problem Detection, Root Cause, Decision Scoring, dan Action Plan yang tersimpan otomatis.

+ +
+ Problem Detection + Root Cause + Decision Score +
+
+
+
+
+ Simulasi MVP + Prioritas 95/100 +
+
+
Keterbatasan Dana96%
+
Target 3 Tahun92%
+
Biaya Hidup88%
+
Bahasa & Admission76%
+
+
+

Solusi Teratas

+

Prioritaskan Jepang + Beasiswa

+

Decision Score 83.8 · Sukses Rate 84%

+
+
+
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
+ + +
+
+
MVP Workspace
+

Input kiri, output analisis kanan — seperti prototype Streamlit.

+ + {% if messages %} +
+ {% for message in messages %} + + {% endfor %} +
+ {% endif %} + +
+
+
+
+ 📥 +
+

Input Masalah

+

Masukkan masalah, target, atau hambatan Anda. Contoh: kuliah Singapura/Jepang dengan budget terbatas.

+
+
+
+ {% csrf_token %} + {{ form.non_field_errors }} +
+ + {{ form.description }} + {% for error in form.description.errors %}
{{ error }}
{% endfor %} +
+
+
+ + {{ form.urgency.value|default:3 }} +
+ {{ form.urgency }} +
135
+ {% for error in form.urgency.errors %}
{{ error }}
{% endfor %} +
+ +

Hasil akan disimpan otomatis ke Riwayat Case.

+
+
+
+ +
+
+
✅ Analisis Berhasil Disimulasikan!
+ +
+

1. Problem Detection

+
+
Prioritas Skor95/100
+
Dampak / ConstraintBudget ketat
+
+
+ +
+

2. Root Cause (Akar Masalah)

+
+
+
Keterbatasan Dana96%
+
+
+
+
Target Lulus 3 Tahun92%
+
+
+
+
Biaya Hidup Negara88%
+
+
+
+
Bahasa & Admission76%
+
+
+
+
+ +
+

3. Rekomendasi Solusi & Prediksi Sukses

+

Decision Score = Impact×0.4 + Efficiency×0.3 + Speed×0.2 + LowRisk×0.1

+
+ + + + + + + + + + + + + + + + + +
SolusiImpactEfficiencySpeedLow RiskSukses RateDecision Score
Prioritaskan Jepang + Beasiswa/Part-time Legal8886727884%83.8
Pathway Hemat: Lokal lalu Transfer7688688680%78.4
Singapura Jika Scholarship Besar8258865566%72.9
+
+
+
+
+
+
+
+ +
+
+
+
+
Histori Keputusan
+

Kasus terbaru

+
+ Lihat semua kasus → +
+
+ {% for case in recent_cases %} + + {% empty %} +
+
+

Belum ada kasus tersimpan.

+

Gunakan form di atas untuk membuat analisis OPTEMA AI pertama Anda.

+
+
+ {% endfor %} +
+
+
-