diff --git a/spp_dashboard_base/__init__.py b/spp_dashboard_base/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/spp_dashboard_base/__manifest__.py b/spp_dashboard_base/__manifest__.py
new file mode 100644
index 000000000..282f6ee54
--- /dev/null
+++ b/spp_dashboard_base/__manifest__.py
@@ -0,0 +1,30 @@
+# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
+
+
+{
+ "name": "OpenSPP Dashboard: Base",
+ "category": "OpenSPP",
+ "version": "17.0.1.0.0",
+ "sequence": 1,
+ "author": "OpenSPP.org",
+ "website": "https://github.com/OpenSPP/openspp-modules",
+ "license": "LGPL-3",
+ "development_status": "Beta",
+ "maintainers": ["reichie020212"],
+ "depends": [
+ "base",
+ ],
+ "data": [],
+ "assets": {
+ "web.assets_backend": [
+ "spp_dashboard_base/static/src/dashboard/**/*",
+ "spp_dashboard_base/static/src/chart/**/*",
+ "spp_dashboard_base/static/src/card_board/**/*",
+ ],
+ },
+ "demo": [],
+ "images": [],
+ "application": False,
+ "installable": True,
+ "auto_install": False,
+}
diff --git a/spp_dashboard_base/pyproject.toml b/spp_dashboard_base/pyproject.toml
new file mode 100644
index 000000000..4231d0ccc
--- /dev/null
+++ b/spp_dashboard_base/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/spp_dashboard_base/static/description/icon.png b/spp_dashboard_base/static/description/icon.png
new file mode 100644
index 000000000..35f8fec26
Binary files /dev/null and b/spp_dashboard_base/static/description/icon.png differ
diff --git a/spp_dashboard_base/static/src/card_board/card_board.js b/spp_dashboard_base/static/src/card_board/card_board.js
new file mode 100644
index 000000000..915e2e44f
--- /dev/null
+++ b/spp_dashboard_base/static/src/card_board/card_board.js
@@ -0,0 +1,17 @@
+/** @odoo-module **/
+
+import {Component} from "@odoo/owl";
+
+export class CardBoardComponent extends Component {}
+
+CardBoardComponent.template = "spp_dashboard_base.CardBoardTemplate";
+CardBoardComponent.props = {
+ title: {type: String, optional: true},
+ data: {type: [String, Number], optional: true},
+ size: {type: String, optional: true},
+};
+CardBoardComponent.defaultProps = {
+ title: "Title",
+ data: "Data",
+ size: "col-md-4",
+};
diff --git a/spp_dashboard_base/static/src/card_board/card_board.xml b/spp_dashboard_base/static/src/card_board/card_board.xml
new file mode 100644
index 000000000..c91d34d9f
--- /dev/null
+++ b/spp_dashboard_base/static/src/card_board/card_board.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/spp_dashboard_base/static/src/chart/chart.js b/spp_dashboard_base/static/src/chart/chart.js
new file mode 100644
index 000000000..d170d4ea6
--- /dev/null
+++ b/spp_dashboard_base/static/src/chart/chart.js
@@ -0,0 +1,72 @@
+/** @odoo-module **/
+
+import {Component, onMounted, onWillStart, useRef} from "@odoo/owl";
+import {loadBundle} from "@web/core/assets";
+
+export class ChartComponent extends Component {
+ setup() {
+ onMounted(() => this.renderChart());
+ this.canvasRef = useRef("canvas");
+
+ this.chartTitle = "";
+ const chartTypesWithTitle = ["pie", "doughnut"];
+ if (chartTypesWithTitle.includes(this.props.chart_type)) {
+ this.chartTitle = this.props.data_label;
+ }
+
+ onWillStart(async () => {
+ return Promise.all([
+ // Load external JavaScript and CSS libraries.
+ loadBundle({
+ jsLibs: [
+ // "/awesome_dashboard/static/lib/chart-js.4.4.4/chart.umd.min.js",
+ "https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js",
+ ],
+ }),
+ ]);
+ });
+ }
+
+ renderChart() {
+ const ctx = this.canvasRef.el.getContext("2d");
+
+ new Chart(ctx, {
+ type: this.props.chart_type,
+ data: {
+ labels: this.props.labels,
+ datasets: [
+ {
+ label: this.props.data_label,
+ data: this.props.data,
+ backgroundColor: this.props.backgroundColor,
+ hoverOffset: 2,
+ },
+ ],
+ },
+ options: {...this.props.options},
+ });
+ }
+}
+
+ChartComponent.template = "spp_dashboard_base.ChartComponentTemplate";
+ChartComponent.props = {
+ chart_type: {type: String, optional: true},
+ labels: {type: Array, optional: true},
+ data_label: {type: String, optional: true},
+ data: {type: Array, optional: true},
+ backgroundColor: {type: Array, optional: true},
+ options: {type: Object, optional: true},
+ size: {type: String, optional: true},
+};
+ChartComponent.defaultProps = {
+ chart_type: "pie",
+ labels: ["Red", "Blue", "Yellow"],
+ data_label: "Number of Colors",
+ data: [300, 50, 150],
+ backgroundColor: ["rgb(255, 99, 132)", "rgb(54, 162, 235)", "rgb(255, 205, 86)"],
+ options: {
+ maintainAspectRatio: true,
+ aspectRatio: 2,
+ },
+ size: "col-md-6",
+};
diff --git a/spp_dashboard_base/static/src/chart/chart.xml b/spp_dashboard_base/static/src/chart/chart.xml
new file mode 100644
index 000000000..97973bfc1
--- /dev/null
+++ b/spp_dashboard_base/static/src/chart/chart.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/spp_dashboard_base/static/src/dashboard/dashboard.js b/spp_dashboard_base/static/src/dashboard/dashboard.js
new file mode 100644
index 000000000..a23cde99f
--- /dev/null
+++ b/spp_dashboard_base/static/src/dashboard/dashboard.js
@@ -0,0 +1,28 @@
+/** @odoo-module **/
+import {Component, onWillStart, useState} from "@odoo/owl";
+import {CardBoardComponent} from "../card_board/card_board";
+import {ChartComponent} from "../chart/chart";
+import {registry} from "@web/core/registry";
+import {useService} from "@web/core/utils/hooks";
+
+export class SppDashboard extends Component {
+ setup() {
+ super.setup();
+ this.orm = useService("orm");
+ this.state = useState({hierarchy: []});
+ this.card_board_data = {};
+ onWillStart(this.onWillStart);
+ this.dashboard_title = "Dashboard";
+ }
+
+ async onWillStart() {
+ // Super this function to get data from the server
+ // sample
+ // this.dashboard_data = await this.orm.call("res.partner", "get_data", []);
+ }
+}
+
+SppDashboard.template = "spp_dashboard_base.dashboard_page";
+SppDashboard.components = {ChartComponent, CardBoardComponent};
+
+registry.category("actions").add("spp_dashboard_tag", SppDashboard);
diff --git a/spp_dashboard_base/static/src/dashboard/dashboard.xml b/spp_dashboard_base/static/src/dashboard/dashboard.xml
new file mode 100644
index 000000000..46edcd960
--- /dev/null
+++ b/spp_dashboard_base/static/src/dashboard/dashboard.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/spp_farmer_registry_base/models/agricultural_activity.py b/spp_farmer_registry_base/models/agricultural_activity.py
index eaf6dde3e..abcc3f56f 100644
--- a/spp_farmer_registry_base/models/agricultural_activity.py
+++ b/spp_farmer_registry_base/models/agricultural_activity.py
@@ -31,7 +31,9 @@ class AgriculturalActivity(models.Model):
],
)
- species_id = fields.Many2one("spp.farm.species", string="Species", domain="[('species_type', '=', activity_type)]")
+ species_id = fields.Many2one(
+ "spp.farm.species", ondelete="restrict", string="Species", domain="[('species_type', '=', activity_type)]"
+ )
@api.onchange("crop_farm_id")
def _onchange_farm_id(self):
diff --git a/spp_farmer_registry_demo/models/generate_farmer_data.py b/spp_farmer_registry_demo/models/generate_farmer_data.py
index cfdda75b5..d8a05952a 100644
--- a/spp_farmer_registry_demo/models/generate_farmer_data.py
+++ b/spp_farmer_registry_demo/models/generate_farmer_data.py
@@ -7,6 +7,7 @@
from odoo import Command, api, fields, models
+from odoo.addons.queue_job.delay import group
from odoo.addons.spp_base_demo.locale_providers import create_faker
from .. import tools
@@ -57,16 +58,52 @@ class SPPGenerateFarmerData(models.Model):
required=True,
)
+ locked = fields.Boolean(default=False)
+ locked_reason = fields.Char(readonly=True)
+
+ GROUPS_PER_BATCH = 100
+
def generate_sample_data(self):
- batches = math.ceil(self.num_groups / 1000)
+ batches = math.ceil(self.num_groups / self.GROUPS_PER_BATCH)
+
+ self.locked = True
+ self.locked_reason = "Generating Sample Data"
+ num_groups = self.num_groups
+
+ jobs = []
for _ in range(0, batches):
+ jobs.append(self.delayable()._generate_sample_data(res=self, num_groups=num_groups))
+ batch_num_groups = min(num_groups, self.GROUPS_PER_BATCH)
+ num_groups -= batch_num_groups
# self.with_delay()._generate_sample_data(res_id=self.id)
- self._generate_sample_data(res=self)
+ # self._generate_sample_data(res=self)
+
+ main_job = group(*jobs)
+ main_job.on_done(self.delayable()._mark_done())
+ main_job.delay()
+
+ def _mark_done(self):
+ self.ensure_one()
+ self.locked = False
+ self.locked_reason = ""
+
+ def refresh_page(self):
+ """
+ The function `refresh_page` returns a dictionary with the type and tag values to reload the
+ page.
+ :return: The code is returning a dictionary with two key-value pairs. The "type" key has the
+ value "ir.actions.client" and the "tag" key has the value "reload".
+ """
+ return {
+ "type": "ir.actions.client",
+ "tag": "reload",
+ }
@api.model
def _generate_sample_data(self, **kwargs):
res = kwargs.get("res")
+ num_groups = kwargs.get("num_groups")
kind_farm_id = self.env.ref("spp_farmer_registry_base.kind_farm").id
@@ -77,7 +114,7 @@ def _generate_sample_data(self, **kwargs):
sex_choices = [option.value for option in options]
sex_choice_range = sex_choices * 50
- num_groups = min(res.num_groups, 1000)
+ num_groups = min(num_groups, self.GROUPS_PER_BATCH)
for i in range(0, num_groups):
group_id = res._generate_group_data(i, fake, sex_choice_range, kind_farm_id)
diff --git a/spp_farmer_registry_demo/views/generate_farmer_data_view.xml b/spp_farmer_registry_demo/views/generate_farmer_data_view.xml
index 59e6ccf16..077c66e81 100644
--- a/spp_farmer_registry_demo/views/generate_farmer_data_view.xml
+++ b/spp_farmer_registry_demo/views/generate_farmer_data_view.xml
@@ -24,8 +24,25 @@
+
+
-