diff --git a/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml
new file mode 100644
index 0000000..4fccad1
--- /dev/null
+++ b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml
@@ -0,0 +1,1833 @@
+
+
+
+
+ 3.51.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+ 1
+ 1
+
+
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ window
+
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+ 1
+
+
+
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ window
+
+
+ 1
+ window
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ window
+
+
+ 1
+ window
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ aggregate
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+ 1
+ 1
+
+
+ window
+
+
+ aggregate
+
+
+ 1
+ 1
+
+
+ window
+
+
+ 1
+
+
+ aggregate
+
+
+ window
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+ aggregate
+
+
+
+ 1
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ window
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ aggregate
+
+
+ aggregate
+
+
+ 1
+
+
+ 1
+ 2026-06-05.07:13:27
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+
+ 1
+ TEXT|0s
+
+
+ 2
+ TEXT|0s
+
+
+ 3
+ TEXT|0s
+
+
+ 4
+ INT|0s
+
+
+ 5
+ TEXT|0s
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta
new file mode 100644
index 0000000..8dab49c
--- /dev/null
+++ b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta
@@ -0,0 +1,2 @@
+#n:main
+! [0, 0, null, null, -2147483648, -2147483648]
diff --git a/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba.xml b/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba.xml
new file mode 100644
index 0000000..9fc8217
--- /dev/null
+++ b/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba.xml
@@ -0,0 +1,1833 @@
+
+
+
+
+ 3.51.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+ 1
+ 1
+
+
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ window
+
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+ 1
+
+
+
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ window
+
+
+ 1
+ window
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ window
+
+
+ 1
+ window
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+ 1
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+ window
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ aggregate
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+ 1
+ 1
+
+
+ window
+
+
+ aggregate
+
+
+ 1
+ 1
+
+
+ window
+
+
+ 1
+
+
+ aggregate
+
+
+ window
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+ aggregate
+
+
+
+ 1
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ window
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ window
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ aggregate
+
+
+ aggregate
+
+
+ 1
+
+
+ 1
+ 2026-06-05.07:12:20
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ 2
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+ R
+
+
+ 1
+
+
+
+ 1
+ TEXT|0s
+
+
+ 2
+ TEXT|0s
+
+
+ 3
+ TEXT|0s
+
+
+ 4
+ INT|0s
+
+
+ 5
+ TEXT|0s
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba/storage_v2/_src_/schema/main.uQUzAA.meta b/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba/storage_v2/_src_/schema/main.uQUzAA.meta
new file mode 100644
index 0000000..8dab49c
--- /dev/null
+++ b/.idea/dataSources/a0abcd0a-1d6f-40e4-88be-f442bcb431ba/storage_v2/_src_/schema/main.uQUzAA.meta
@@ -0,0 +1,2 @@
+#n:main
+! [0, 0, null, null, -2147483648, -2147483648]
diff --git a/.idea/dataSources/data_sources_history.xml b/.idea/dataSources/data_sources_history.xml
new file mode 100644
index 0000000..3f0c640
--- /dev/null
+++ b/.idea/dataSources/data_sources_history.xml
@@ -0,0 +1,42 @@
+
+
+
+
+ "
+
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:$PROJECT_DIR$/db/articles
+ master_key
+ no-auth
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
+
+
+ "
+
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:$PROJECT_DIR$/db/articles
+ master_key
+ no-auth
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/db/articles b/db/articles
new file mode 100644
index 0000000..e69de29
diff --git a/php/model/ArticleManagerDAO.php b/php/model/ArticleManagerDAO.php
index d33df23..01826e1 100644
--- a/php/model/ArticleManagerDAO.php
+++ b/php/model/ArticleManagerDAO.php
@@ -20,8 +20,7 @@ interface ArticleManagerDAO
* @param $category string Kategorie des Beitrages
* @param $tags string optionale Schlagworte für eine bessere Suche
*
- * Mögliche Exceptions:
- * TODO: Exceptions implementieren.
+ * @throws InternalServerErrorException
*/
public function addArticle($title, $content, $author, $category, $tags);
@@ -37,16 +36,17 @@ interface ArticleManagerDAO
* @throws InternalServerErrorException
* @throws NotFoundException
* @throws UnauthorizedAccessException
- * /
*/
public function updateArticle($id, $article, $author);
/**
* Löscht einen Beitrag aus übergebener ID.
+ * TODO: sollte auch die Autorisierung prüfen...
* @param $id
* @return void
- *
- * TODO: Exceptions implementieren.
+ * @throws InternalServerErrorException
+ * @throws NotFoundException
+ * @throws UnauthorizedAccessException
*/
public function deleteArticle($id);
@@ -55,16 +55,14 @@ interface ArticleManagerDAO
* $id ID des Beitrags
*
* @return Article
- * Mögliche Exceptions:
- * TODO: Exceptions implementieren.
+ * @throws InternalServerErrorException
*/
public function getArticle($id);
/**
* Alle Beiträge aufrufen.
*
- * Mögliche Exceptions:
- * TODO: Exceptions implementieren.
+ * @throws InternalServerErrorException
*/
public function getAllArticles();
@@ -72,7 +70,7 @@ interface ArticleManagerDAO
* Gibt alle Beiträge eines Nutzer mit einer gegebenen ID aus.
* @param $author
* @return Article[]
- * TODO: Exceptions implementieren.
+ * @throws InternalServerErrorException
*/
public function getArticlesByAuthor($author);
@@ -89,6 +87,7 @@ interface ArticleManagerDAO
* Gibt alle Beiträge einer gegebenen Kategorie aus.
* @param $category
* @return mixed
+ * @throws InternalServerErrorException
*/
public function getArticlesByCategory($category);
diff --git a/php/model/DatabaseArticleManager.php b/php/model/DatabaseArticleManager.php
new file mode 100644
index 0000000..8eb1fed
--- /dev/null
+++ b/php/model/DatabaseArticleManager.php
@@ -0,0 +1,306 @@
+exec("
+ CREATE TABLE articles (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ title TEXT,
+ content TEXT,
+ author TEXT,
+ category TEXT,
+ tags TEXT,
+ created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );");
+ unset($db);
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException($e->getMessage());
+ }
+ }
+ }
+
+ /**
+ * Baut die Verbindung zur Datenbank auf.
+ * @throws InternalServerErrorException
+ */
+ private function getConnection()
+ {
+ try {
+ $user = 'root';
+ $pw = null;
+ $dsn = 'sqlite: ../../db/gaestebuch.db';
+ return new PDO($dsn, $user, $pw);
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException($e->getMessage());
+ }
+ }
+
+ /**
+ * Gibt die DatabaseArticleManager-Instanz zurück.
+ * @return DatabaseArticleManager
+ */
+ public static function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new DatabaseArticleManager();
+ }
+ return self::$instance;
+ }
+
+ public function addArticle($title, $content, $author, $category, $tags)
+ {
+ try {
+ $db = $this->getConnection();
+
+ $sql = "INSERT INTO articles (title, content, author, category, tags)
+ VALUES (:title, :content, :author, :category, :tags);";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ // Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern
+ $success = $command->execute([
+ ":title" => $title,
+ ":content" => $content,
+ ":author" => $author,
+ ":category" => $category,
+ ":tags" => $tags
+ ]);
+
+ if (!$success) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ return intval($db->lastInsertId());
+
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException($e->getMessage());
+ }
+ }
+
+ public function updateArticle($id, $article, $author)
+ {
+ if (empty($article)) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ // Berechtigungsprüfung analog zur lokalen Implementierung:
+ if ($article->getAuthor() !== $author) {
+ throw new UnauthorizedAccessException("unauthorized_access");
+ }
+
+ try {
+ $db = $this->getConnection();
+
+ $sql = "UPDATE articles
+ SET title = :title, content = :content, author = :author, category = :category, tags = :tags
+ WHERE id = :id;";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ $success = $command->execute([
+ ":id" => $id,
+ ":title" => $article->getTitle(),
+ ":content" => $article->getContent(),
+ ":author" => $author,
+ ":category" => $article->getCategory(),
+ ":tags" => $article->getTags()
+ ]);
+
+ // rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte
+ if (!$success || $command->rowCount() === 0) {
+ // Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist
+ if (!$this->getArticle($id)) {
+ throw new NotFoundException("missing_id");
+ }
+ }
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function deleteArticle($id)
+ {
+ // TODO: Sollte auch die Autorisierung prüfen...
+ try {
+ $db = $this->getConnection();
+ $sql = "DELETE FROM articles WHERE id = :id;";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ if (!$command->execute([":id" => $id])) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ } catch (PDOException $exc) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function getArticle($id)
+ {
+ try {
+ $db = $this->getConnection();
+ $sql = "SELECT * FROM articles WHERE id = :id;";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ $command->execute([":id" => $id]);
+ $row = $command->fetch(PDO::FETCH_ASSOC);
+
+ if ($row) {
+ return new Article(
+ intval($row['id']),
+ $row['title'],
+ $row['content'],
+ $row['author'],
+ $row['category'],
+ $row['tags'],
+ $row['created']
+ );
+ }
+
+ return null;
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function getAllArticles()
+ {
+ try {
+ $db = $this->getConnection();
+ $sql = "SELECT * FROM articles;";
+
+ $command = $db->query($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ $rows = $command->fetchAll(PDO::FETCH_ASSOC);
+ $articles = [];
+
+ foreach ($rows as $row) {
+ $articles[] = new Article(
+ intval($row['id']),
+ $row['title'],
+ $row['content'],
+ $row['author'],
+ $row['category'],
+ $row['tags'],
+ $row['created']
+ );
+ }
+
+ return $articles;
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function getArticlesByAuthor($author)
+ {
+ try {
+ $db = $this->getConnection();
+ $sql = "SELECT * FROM articles WHERE author = :author;";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ $command->execute([":author" => $author]);
+ $rows = $command->fetchAll(PDO::FETCH_ASSOC);
+ $filteredArticles = [];
+
+ foreach ($rows as $row) {
+ $filteredArticles[] = new Article(
+ intval($row['id']),
+ $row['title'],
+ $row['content'],
+ $row['author'],
+ $row['category'],
+ $row['tags'],
+ $row['created']
+ );
+ }
+
+ return $filteredArticles;
+ } catch (PDOException $e) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function getArticlesByCategory($category)
+ {
+ try {
+ $db = $this->getConnection();
+ $sql = "SELECT * FROM articles WHERE category = :category;";
+
+ $command = $db->prepare($sql);
+ if (!$command) {
+ throw new InternalServerErrorException("internal_error");
+ }
+
+ $command->execute([":category" => $category]);
+ $rows = $command->fetchAll(PDO::FETCH_ASSOC);
+ $filteredArticles = [];
+
+ foreach ($rows as $row) {
+ $filteredArticles[] = new Article(
+ intval($row['id']),
+ $row['title'],
+ $row['content'],
+ $row['author'],
+ $row['category'],
+ $row['tags'],
+ $row['created']
+ );
+ }
+
+ return $filteredArticles;
+ } catch (PDOException $exc) {
+ throw new InternalServerErrorException("internal_error");
+ }
+ }
+
+ public function search(string $keyword): array
+ {
+ // TODO: implement search()
+ return [];
+ }
+
+}
\ No newline at end of file
diff --git a/php/model/LocalArticleManager.php b/php/model/LocalArticleManager.php
index 2ee8ce9..62879e9 100644
--- a/php/model/LocalArticleManager.php
+++ b/php/model/LocalArticleManager.php
@@ -104,6 +104,7 @@ class LocalArticleManager implements ArticleManagerDAO {
public function deleteArticle($id)
{
+ // TODO: Sollte auch die Autorisierung prüfen...
$articles = $this->getAllArticles();
$articleFound = false;