diff --git a/app.py b/app.py
index 1a39b27abd20b68d14d38d0e388a49f902a82691..bc2cb9d96bdba26e8638e62cb2e81e6586d9bd16 100644
--- a/app.py
+++ b/app.py
@@ -9,6 +9,7 @@ from modules.admin import admin_route
 from modules.page import page_route
 from modules.apbd import apbd_route
 from modules.tools.token import validate_login_token
+from migrate.normalize import normalize
 
 app = Flask(__name__)
 app.config.from_pyfile("config.cfg")
@@ -27,6 +28,11 @@ cors = CORS(app)
 def hello():
     return "Backend server dari proyek VIS-MASY"
 
+@app.route("/api/normalize")
+def call_normalize_mongo():
+	normalize()
+	return "Done! don't call this again, please"
+
 @app.route("/api/test-unauthorized")
 @validate_login_token
 def access():
diff --git a/database/database.py b/database/database.py
index 2bc707dd763d5e8011b494b5c140f8dcabe2fdde..3cf5e36931bc5ec3c033ba40269a356e8478fde4 100644
--- a/database/database.py
+++ b/database/database.py
@@ -37,6 +37,7 @@ class VizData(mongo.Document):
     year = mongo.IntField(required=True)
     subdata = mongo.ListField(mongo.ReferenceField("VizData"))
     value = mongo.IntField(required = True)
+    percentage = mongo.FloatField()
     categories = mongo.ListField(mongo.StringField(), default=[])
     detail = mongo.StringField()
 
diff --git a/migrate/Readme.md b/migrate/Readme.md
index efaab144938d1660fb75ee8879095cddcb16d815..6f6812b16b5e7c2c886d9893c5f5bb22dc8dc2d7 100644
--- a/migrate/Readme.md
+++ b/migrate/Readme.md
@@ -1,3 +1,5 @@
 # __Dummy data APBD JABAR__
 
-Gunakan perintah `mongorestore` untuk menambahkan __data dummy__ ke basisdata viz-masy
\ No newline at end of file
+Gunakan perintah `mongorestore` untuk menambahkan __data dummy__ ke basisdata viz-masy
+
+Untuk data yang suadah normalized ke dalam collection vizdata, gunakan `mongorestore` di dalam folder normalized_dump
\ No newline at end of file
diff --git a/migrate/__init__.py b/migrate/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/migrate/normalize.py b/migrate/normalize.py
new file mode 100644
index 0000000000000000000000000000000000000000..395b30214a1f362d9de96023b8ae7437ef98c53c
--- /dev/null
+++ b/migrate/normalize.py
@@ -0,0 +1,118 @@
+import mongoengine
+
+mongo = mongoengine
+
+mongo.connect('viz-masy', host='localhost', port=27017)
+
+# normalize tha many apbd tables into one table: VizData
+class VizData(mongo.Document):
+    name = mongo.StringField(required = True)
+    level = mongo.IntField(required= True)
+    value = mongo.LongField(required = True)
+    year = mongo.IntField(required=True)
+    subdata = mongo.ListField(mongo.ReferenceField("VizData"))
+    value = mongo.IntField(required = True)
+    percentage = mongo.FloatField()
+    categories = mongo.ListField(mongo.StringField(), default=[])
+    detail = mongo.StringField()
+
+class Page(mongo.Document):
+    level = mongo.IntField(required=True)
+    data = mongo.ListField(mongo.ReferenceField("Data", required=True))
+    comments = mongo.ListField(mongo.ReferenceField("Comment"), default=[])
+
+# APBD Data
+class Rekening(mongo.Document):
+    id_kegiatan = mongo.IntField(required=True)
+    kode_rekening = mongo.StringField(required=True)
+    nama_rekening = mongo.StringField(required=True)
+    jumlah_rincian = mongo.IntField(required=True)
+    persentase = mongo.FloatField()
+
+class Kegiatan(mongo.Document):
+    id_program = mongo.IntField(required=True)
+    id_kegiatan = mongo.IntField(required=True)
+    nama_kegiatan = mongo.StringField(required=True)
+    rekening = mongo.ListField(mongo.StringField(), default=[], required=True)
+    jumlah_rincian = mongo.IntField(default = 0)
+    persentase = mongo.FloatField()
+
+class Program(mongo.Document):
+    id_skpd = mongo.IntField(required=True)
+    id_program = mongo.IntField(required=True)
+    nama_program = mongo.StringField(required=True)
+    kegiatan = mongo.ListField(mongo.IntField(), default=[], required=True)
+    jumlah_rincian = mongo.IntField(default = 0)
+    persentase = mongo.FloatField()
+
+class SKPD(mongo.Document):
+    id_urusan = mongo.IntField(required=True)
+    id_skpd = mongo.IntField(required=True)
+    nama_skpd = mongo.StringField(required=True)
+    program = mongo.ListField(mongo.IntField(), default=[], required=True)
+    jumlah_rincian = mongo.IntField(default = 0)
+    persentase = mongo.FloatField()
+
+class Urusan(mongo.Document):
+    tahun = mongo.IntField(required=True)
+    id_urusan = mongo.IntField(required=True)
+    nama_urusan = mongo.StringField(required=True)
+    skpd = mongo.ListField(mongo.IntField(), default=[], required=True)
+    jumlah_rincian = mongo.IntField(default = 0)
+    persentase = mongo.FloatField()
+
+
+def normalize():
+    for urusan in Urusan.objects:
+        year = urusan.tahun
+        norm_urusan = VizData(
+            name = urusan.nama_urusan,
+            level = 1,
+            value = int(urusan.jumlah_rincian),
+            percentage = urusan.persentase,
+            year = year,
+        )
+        for skpd in SKPD.objects(id_urusan=urusan.id_urusan):
+            norm_skpd = VizData(
+                name = skpd.nama_skpd,
+                level = 2,
+                value = int(skpd.jumlah_rincian),
+                percentage = skpd.persentase,
+                year = year
+            )
+            for program in Program.objects(id_skpd=skpd.id_skpd):
+                norm_program = VizData(
+                    name = program.nama_program,
+                    level = 3,
+                    value = int(program.jumlah_rincian),
+                    percentage = program.persentase,
+                    year = year
+                )
+                for kegiatan in Kegiatan.objects(id_program=program.id_program):
+                    norm_keg = VizData(
+                        name = kegiatan.nama_kegiatan,
+                        level = 4,
+                        value = int(kegiatan.jumlah_rincian),
+                        percentage = kegiatan.persentase,
+                        year= year
+                    )
+                    for rekening in Rekening.objects(id_kegiatan=kegiatan.id_kegiatan):
+                        norm_rek = VizData(
+                            name = rekening.nama_rekening,
+                            level = 5,
+                            value = int(rekening.jumlah_rincian),
+                            percentage = kegiatan.persentase,
+                            year = year
+                        )
+                        norm_rek.save()
+                        norm_keg.subdata.append(norm_rek)
+                    norm_keg.save()
+                    norm_program.subdata.append(norm_keg)
+                norm_program.save()
+                norm_skpd.subdata.append(norm_program)
+            norm_skpd.save()
+            norm_urusan.subdata.append(norm_skpd)
+        norm_urusan.save()
+
+if __name__ == "__main__":
+    normalize()
\ No newline at end of file
diff --git a/migrate/normalized_dump/dump/viz-masy/comment.bson b/migrate/normalized_dump/dump/viz-masy/comment.bson
new file mode 100644
index 0000000000000000000000000000000000000000..63201829bf4b6e890352d4bd62507bc1f1d5d5ca
Binary files /dev/null and b/migrate/normalized_dump/dump/viz-masy/comment.bson differ
diff --git a/migrate/normalized_dump/dump/viz-masy/comment.metadata.json b/migrate/normalized_dump/dump/viz-masy/comment.metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..906c82458d3da5ba85285a270accf9c17733b564
--- /dev/null
+++ b/migrate/normalized_dump/dump/viz-masy/comment.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"viz-masy.comment"}],"uuid":"b6d4e3d50c47414ab4a64edfc27cc500"}
\ No newline at end of file
diff --git a/migrate/normalized_dump/dump/viz-masy/user.bson b/migrate/normalized_dump/dump/viz-masy/user.bson
new file mode 100644
index 0000000000000000000000000000000000000000..5171f41cd266e8eb019a65bbe98f90cf9ce1f959
Binary files /dev/null and b/migrate/normalized_dump/dump/viz-masy/user.bson differ
diff --git a/migrate/normalized_dump/dump/viz-masy/user.metadata.json b/migrate/normalized_dump/dump/viz-masy/user.metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..b9f25fb5eb05cdb9ef9e41c94b70ab53666f55fe
--- /dev/null
+++ b/migrate/normalized_dump/dump/viz-masy/user.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"viz-masy.user"},{"v":2,"unique":true,"key":{"username":1},"name":"username_1","ns":"viz-masy.user","background":false,"sparse":false}],"uuid":"326e22581b384261b8cb7960f7d2df7d"}
\ No newline at end of file
diff --git a/migrate/normalized_dump/dump/viz-masy/viz_data.bson b/migrate/normalized_dump/dump/viz-masy/viz_data.bson
new file mode 100644
index 0000000000000000000000000000000000000000..6abe0244a54a5f233e77c7df9a2e12ce94f7bd52
Binary files /dev/null and b/migrate/normalized_dump/dump/viz-masy/viz_data.bson differ
diff --git a/migrate/normalized_dump/dump/viz-masy/viz_data.metadata.json b/migrate/normalized_dump/dump/viz-masy/viz_data.metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..3ba1bbce71e959e6fe872af1d7012097c3a2fea0
--- /dev/null
+++ b/migrate/normalized_dump/dump/viz-masy/viz_data.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"viz-masy.viz_data"}],"uuid":"06a0762f22a64a8cb1a2f1ef6f1d294e"}
\ No newline at end of file
diff --git a/normalize.py b/normalize.py
new file mode 100644
index 0000000000000000000000000000000000000000..44dab7621625e8ea289d7177872b6908778f172b
--- /dev/null
+++ b/normalize.py
@@ -0,0 +1,3 @@
+from migrate.normalize import normalize
+
+normalize()
\ No newline at end of file
diff --git a/readme.md b/readme.md
index df26e7c0318d9f96cb5bce06e65e5cf007239d0b..e1e7c84b75d27ca98bc9e10c7a1f3d6805d8eeb6 100644
--- a/readme.md
+++ b/readme.md
@@ -65,7 +65,7 @@ Berikut adalah API endpoints dari backend server VIS-MASY:
     Response: status, message  
     Menghapus pengguna dengan user_id jika authorization header untuk admin, atau menghapus pengguna dalam authorization header jika user_id tidak diberikan.
 
-### Page
+### Page (High Level)
 
 * #### /api/page/get-top
     Method: POST