Tower: upload ks_dashboard_ninja 18.0.1.1.7 (was 18.0.1.1.7, via marketplace)

This commit is contained in:
2026-05-08 21:13:33 +00:00
parent 63c62699f5
commit 888f87d8ec
334 changed files with 26628 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class KpSendMail(models.Model):
_name = 'ks_dashboard_ninja.kpi_mail'
_description = 'Dashboard Ninja Kpi mail'
name = fields.Char(string="Email To:")

View File

@@ -0,0 +1,17 @@
from . import ks_dashboard_ninja
from . import ks_dashboard_ninja_items
from . import ks_item_action
from . import ks_child_dashboard
from . import ks_dashboard_filters
from . import ks_dashboard_templates
from . import ks_dn_to_do_item
from . import ks_import_dashboard
from . import Kpi_mail
from . import res_settings
from . import ks_ai_ninja_dashboard
from . import ks_ai_whole_dashboard
from . import ks_key_fetch
from . import ks_chat_channel
from . import base_model_extend

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from odoo import models, api
class BaseExtend(models.AbstractModel):
_inherit = 'base'
@api.model_create_multi
def create(self, vals):
recs = super(BaseExtend, self).create(vals)
if 'ir.' not in self._name and 'bus.' not in self._name and self.env.user.has_group('base.group_user'):
# items = self.env['ks_dashboard_ninja.item'].search(
# [['ks_model_id.model', '=', self._name]])
# if items:
# online_partners = self.env["bus.presence"].sudo().search([('status', '=', 'online')]).mapped('user_id.partner_id').ids
# updates = [ for partner_id in online_partners]
self.env['bus.bus']._sendone('ks_notification', 'Update: Dashboard Items', {'model': self._name})
return recs
def write(self, vals):
recs = super(BaseExtend, self).write(vals)
if 'ir.' not in self._name and 'bus.' not in self._name and self.env.user.has_group('base.group_user') and 'res.partner' not in self._name:
# items = self.env['ks_dashboard_ninja.item'].search(
# [['ks_model_id.model', '=', self._name]])
# if items:
# online_partner = self.env["bus.presence"].search([('status', '=', 'online')]).mapped('user_id.partner_id').ids
# updates = [[
# (self._cr.dbname, 'res.partner', partner_id),
# {'type': 'ks_notification', 'model': self._name},
# {'id': self.id}
# ] for partner_id in online_partner]
self.env['bus.bus']._sendone('ks_notification', 'Update: Dashboard Items', {'model': self._name})
return recs

View File

@@ -0,0 +1,402 @@
# -*- coding: utf-8 -*-
import base64
import io
import json
import logging
from urllib.parse import quote
import pandas as pd
import requests
from gtts import gTTS
from odoo.exceptions import ValidationError
from odoo.tools import config
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class KsDashboardNInjaAI(models.TransientModel):
_name = 'ks_dashboard_ninja.arti_int'
_description = 'AI Dashboard'
ks_type = fields.Selection([('ks_model', 'Model'), ('ks_keyword', 'Keywords')],
string="Ks AI Type", default='ks_model')
ks_import_model_id = fields.Many2one('ir.model', string='Model ID',
domain="[('access_ids','!=',False),('transient','=',False),"
"('model','not ilike','base_import%'),'|',('model','not ilike','ir.%'),('model','=ilike','_%ir.%'),"
"('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%'),"
"('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]",
help="Data source to fetch and read the data for the creation of dashboard items. ")
ks_import_model = fields.Many2one('ir.model', string='Model',
domain="[('access_ids','!=',False),('transient','=',False),"
"('model','not ilike','base_import%'),('model','not ilike','ir.%'),"
"('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%'),"
"('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]",
help="Data source to fetch and read the data for the creation of dashboard items. ")
ks_input_keywords = fields.Char("Ks Keywords")
ks_model_show = fields.Boolean(default = False, compute='_compute_show_model')
@api.onchange('ks_input_keywords')
def _compute_show_model(self):
if self.ks_input_keywords and self.ks_type=="ks_keyword":
api_key = self.env['ir.config_parameter'].sudo().get_param('ks_dashboard_ninja.dn_api_key')
url = self.env['ir.config_parameter'].sudo().get_param('ks_dashboard_ninja.url')
if api_key and url:
json_data = {'name': api_key,
'type': self.ks_type,
'keyword': self.ks_input_keywords
}
url = url + "/api/v1/ks_dn_keyword_gen"
ks_response = requests.post(url, data=json_data)
if json.loads(ks_response.text) == False:
self.ks_model_show = True
else:
self.ks_model_show = False
else:
self.ks_model_show = False
else:
self.ks_model_show = False
@api.model
def ks_get_keywords(self):
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if url:
url = url + "/api/v1/ks_dn_get_keyword"
ks_response = requests.post(url)
if ks_response.status_code == 200:
return json.loads(ks_response.text)
else:
return []
def ks_do_action(self):
headers = {"Content-Type": "application/json",
"Accept": "application/json",
"Catch-Control": "no-cache",
}
if self.ks_import_model_id:
ks_model_name = self.ks_import_model_id.model
ks_fields = self.env[ks_model_name].fields_get()
ks_filtered_fields = {key: val for key, val in ks_fields.items() if val['type'] not in ['many2many', 'one2many', 'binary'] and'name' in val and val['name'] != 'id' and val['name'] != 'sequence' and val['store'] == True}
ks_fields_name = {val['name']:val['type'] for val in ks_filtered_fields.values()}
question = ("columns: "+ f"{ks_fields_name}")
api_key = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.dn_api_key')
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if api_key and url:
json_data = {'name': api_key,
'question':question,
'type': self.ks_type,
'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
'db_name': self.env.cr.dbname
}
url = url+"/api/v1/ks_dn_main_api"
ks_ai_response = requests.post(url, data=json_data)
if ks_ai_response.status_code == 200:
ks_ai_response = json.loads(ks_ai_response.text)
# create dummy dash to create items on the dashboard, later deleted it.
ks_create_record = self.env['ks_dashboard_ninja.board'].create({
'name': 'AI dashboard',
'ks_dashboard_menu_name': 'AI menu',
'ks_dashboard_default_template': self.env.ref('ks_dashboard_ninja.ks_blank', False).id,
'ks_dashboard_top_menu_id': self.env['ir.ui.menu'].search([('name', '=', 'My Dashboards')])[0].id,
})
ks_dash_id = ks_create_record.id
ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id,
ks_model_name)
context = {'ks_dash_id': self._context['ks_dashboard_id'],
'ks_dash_name': self.env['ks_dashboard_ninja.board'].search([
('id','=',self._context['ks_dashboard_id'])]).name,'ks_delete_dash_id':ks_dash_id }
# return client action created through js for AI dashboard to render items on dummy dashboard
if (ks_result == "success"):
return {
'type': 'ir.actions.client',
'name': 'Generate items with AI',
'params': {'ks_dashboard_id': ks_create_record.id, 'explain_ai_whole': True},
'tag': 'ks_ai_dashboard_ninja',
'context': context,
'target':'new'
}
else:
self.env['ks_dashboard_ninja.board'].browse(ks_dash_id).unlink()
raise ValidationError(_("Items didn't render because AI provides invalid response for this model.Please try again"))
else:
raise ValidationError(_("AI Responds with the following status:- %s") % ks_ai_response.text)
else:
raise ValidationError(_("Please enter URL and API Key in General Settings"))
else:
raise ValidationError(_("Please enter the Model"))
def ks_generate_item(self):
if self.ks_input_keywords:
api_key = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.dn_api_key')
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if api_key and url:
json_data = {'name': api_key,
'type': self.ks_type,
'keyword':self.ks_input_keywords
}
url = url + "/api/v1/ks_dn_keyword_gen"
ks_response = requests.post(url, data=json_data)
else:
raise ValidationError(_("Please put API key and URL"))
if json.loads(ks_response.text) != False and ks_response.status_code==200 :
ks_ai_response = json.loads(ks_response.text)
ks_dash_id = self._context['ks_dashboard_id']
ks_model_name = ks_ai_response[0]['model']
ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id,
ks_model_name)
if ks_result == "success":
return{
'type': 'ir.actions.client',
'tag': 'reload',
}
else:
raise ValidationError(_("Items didn't render, please try again!"))
else:
ks_model_name = self.ks_import_model.model
ks_fields = self.env[ks_model_name].fields_get()
ks_filtered_fields = {key: val for key, val in ks_fields.items() if
val['type'] not in ['many2many', 'one2many', 'binary'] and 'name' in val and val[
'name'] != 'id' and val['name'] != 'sequence' and val['store'] == True}
ks_fields_name = {val['name']: val['type'] for val in ks_filtered_fields.values()}
question = ("schema: " + f"{ks_fields_name}")
model =("model:" + f"{ks_model_name}")
api_key = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.dn_api_key')
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if api_key and url:
json_data = {'name': api_key,
'question': self.ks_input_keywords,
'type':self.ks_type,
'schema':question,
'model':model,
'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
'db_name': self.env.cr.dbname
}
url = url + "/api/v1/ks_dn_main_api"
ks_ai_response = requests.post(url, data=json_data)
if ks_ai_response.status_code == 200:
ks_ai_response = json.loads(ks_ai_response.text)
ks_dash_id = self._context['ks_dashboard_id']
ks_model_name = (ks_ai_response[0]['model']).lower()
if self.env['ir.model'].search([('model','=',ks_model_name)]).id or self.env['ir.model'].search([('name','=',ks_model_name)]).id:
if self.env['ir.model'].search([('name','=',ks_model_name)]).id:
ks_model_name = self.env['ir.model'].search([('name','=',ks_model_name)]).model
else:
ks_model_name = (ks_ai_response[0]['model']).lower()
ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id,ks_model_name)
if ks_result == "success":
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
else:
raise ValidationError(_("Items didn't render, please try again!"))
else:
raise ValidationError(_("%s model does not exist.Please install")% ks_model_name)
else:
raise ValidationError(
_("AI Responds with the following status:- %s") % ks_ai_response.text)
else:
raise ValidationError(_("Please enter URL and API Key in General Settings"))
else:
raise ValidationError(_("Enter the input keywords to render the item"))
@api.model
def ks_generate_analysis(self,ks_items_explain,ks_rest_items,dashboard_id):
if ks_items_explain:
result = []
api_key = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.dn_api_key')
ks_url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
words = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.ks_analysis_word_length')
url = ks_url + "/api/v1/ks_dn_main_api"
for i in range(0,len(ks_items_explain)):
if api_key and url :
json_data = {'name': api_key,
'items':json.dumps(ks_items_explain[i]),
'type':'ks_ai_explain',
'url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
'db_name': self.env.cr.dbname,
'words': words if words else 100
}
ks_response = requests.post(url, data=json_data)
if ks_response.status_code == 200 and json.loads(ks_response.text):
ks_ai_response = json.loads(ks_response.text)
item = ks_ai_response[0]
if item['analysis'] or item['insights']:
try:
self.env['ks_dashboard_ninja.item'].browse(item['id']).write({
'ks_ai_analysis': item['analysis']+'ks_gap'+item['insights']
})
result.append(True)
except:
result
else:
result
else:
result
else:
raise ValidationError(_("Please put API key and URL"))
if len(result): #len(result)
if self.env.context.get('explain_items_with_ai', False):
self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({
'ks_ai_explain_dash': False
})
else:
self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({
'ks_ai_explain_dash': True
})
return True
else:
raise ValidationError(_("AI Responds with the wrong analysis. Please try again "))
elif ks_rest_items:
if self.env.context.get('explain_items_with_ai', False):
self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({
'ks_ai_explain_dash': False
})
else:
self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({
'ks_ai_explain_dash': True
})
return True
else:
return False
def get_ai_explain(self, item_id):
print(item_id)
res = self.env['ks_dashboard_ninja.item'].browse(item_id).ks_ai_analysis
return res
@api.model
def ks_switch_default_dashboard(self,dashboard_id):
self.env['ks_dashboard_ninja.board'].browse(dashboard_id).write({
'ks_ai_explain_dash':False
})
return True
@api.model
def ks_generatetext_to_speech(self,item_id):
if (item_id):
try:
ks_text = self.env['ks_dashboard_ninja.item'].browse(item_id).ks_ai_analysis
if ks_text:
language = 'en'
ks_myobj = gTTS(text=ks_text, lang=language, slow=False)
audio_data = io.BytesIO()
ks_myobj.write_to_fp(audio_data)
audio_data.seek(0)
binary_data = audio_data.read()
wav_file = base64.b64encode( binary_data).decode('UTF-8')
data = {"snd": wav_file}
return json.dumps(data)
else:
return False
except Exception as e:
_logger.error(e)
raise ValidationError(_("Some problem in audio generation."))
else:
return False
@api.model
def ks_gen_chat_res(self,**kwargs):
ks_question = kwargs.get('ks_question')
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url') + "/api/v1/get_sql_query"
data = {
"question": ks_question,
}
try:
ks_response = requests.post(url,data=data)
if (ks_response.status_code == 200):
ks_response = json.loads(ks_response.text)['response']['Query']
return self.ks_gen_dataframe(ks_response,ks_question)
else:
_logger.error('Unexpected error occurs')
return False
except Exception as e:
_logger.error(e)
return False
def ks_gen_dataframe(self,ks_query,question):
host = config.get('db_host', False)
user = quote(config.get('db_user', False))
port = config.get('db_port', False) or 5432
password = quote(config.get('db_password', False))
db = config.get('db_name', False) or self.env.cr.dbname
if not all([host, user, port, password, db]):
_logger.error('some credentials are missing')
return False
else:
sql_uri = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{db}"
ks_fixed_url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url') + "/api/v1/get_fixed_query"
try:
df = pd.read_sql(ks_query, sql_uri)
except Exception as e:
ks_query_data = {
'query':ks_query,
'error':e
}
fixed_query = requests.post(ks_fixed_url, data=ks_query_data)
if fixed_query.status_code == 200:
ks_corrected_query = fixed_query.text
df = pd.read_sql(ks_corrected_query, sql_uri)
else:
_logger.error('Error in generating Dataframe')
return False
if any(df.dtypes == 'datetime64[ns]'):
datetime_columns = [col for col in df.columns if df[col].dtype == 'datetime64[ns]']
df[datetime_columns] = df[datetime_columns].astype(str)
# Convert DataFrame to JSON
if len(df) >= 100:
df = df.head(100)
partial_data = True
df_json = df.to_json(orient='records')
ans = "As dataframe having more data to analyse we are not showing dataframe summary"
# Generate answer
if len(df) < 13:
ks_ans_url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url') + "/api/v1/get_answer"
ks_ans_data = {'df':df.to_dict(orient='records'),'question':question}
ans = requests.post(ks_ans_url, json = ks_ans_data)
if ans.status_code == 200:
ans = ans.text
response_json = {
"Dataframe": df_json,
"Answer": ans,
}
else:
_logger.error('Error in generating answer')
return False
else:
response_json = {
"Dataframe": df_json,
"Answer": ans,
}
return response_json

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
import json
import logging
import requests
from odoo.exceptions import ValidationError
from odoo import fields, models, _
_logger = logging.getLogger(__name__)
class KsAIDashboardninja(models.TransientModel):
_name = 'ks_dashboard_ninja.ai_dashboard'
_description = 'AI Dashboard'
ks_import_model_id = fields.Many2one('ir.model', string='Model',
domain="[('access_ids','!=',False),('transient','=',False),"
"('model','not ilike','base_import%'),('model','not ilike','ir.%'),"
"('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%'),"
"('model','!=','mail.thread'),('model','not ilike','ks_dash%'),('model','not ilike','ks_to%')]",
help="Data source to fetch and read the data for the creation of dashboard items. ", required=True)
ks_dash_name = fields.Char(string="Dashboard Name", required=True, size=35)
ks_menu_name = fields.Char(string="Menu Name", required=True, size=35)
ks_top_menu_id = fields.Many2one('ir.ui.menu',
domain="[('parent_id','=',False)]",
string="Show Under Menu", required=True,
default=lambda self: self.env['ir.ui.menu'].search(
[('name', '=', 'My Dashboards')])[0])
ks_template = fields.Many2one('ks_dashboard_ninja.board_template',
default=lambda self: self.env.ref('ks_dashboard_ninja.ks_blank',
False),
string="Dashboard Template")
def ks_do_action(self):
headers = {"Content-Type": "application/json",
"Accept": "application/json",
"Catch-Control": "no-cache",
}
if self.ks_import_model_id:
ks_model_name = self.ks_import_model_id.model
ks_fields = self.env[ks_model_name].fields_get()
ks_filtered_fields = {key: val for key, val in ks_fields.items() if val['type'] not in ['many2many', 'one2many', 'binary'] and'name' in val and val['name'] != 'id' and val['name'] != 'sequence' and val['store'] == True}
ks_fields_name = {val['name']:val['type'] for val in ks_filtered_fields.values()}
question = ("columns: "+ f"{ks_fields_name}")
api_key = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.dn_api_key')
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if api_key and url:
json_data = {'name': api_key,
'question':question,
'url':self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
'db_name':self.env.cr.dbname
}
url = url+"/api/v1/ks_dn_main_api"
ks_ai_response = requests.post(url, data=json_data)
if ks_ai_response.status_code == 200:
ks_ai_response = json.loads(ks_ai_response.text)
ks_create_record = self.env['ks_dashboard_ninja.board'].create({
'name': self.ks_dash_name,
'ks_dashboard_menu_name': self.ks_menu_name,
'ks_dashboard_default_template': self.ks_template.id,
'ks_dashboard_top_menu_id': self.ks_top_menu_id.id,
})
ks_dash_id = ks_create_record.id
ks_result = self.env['ks_dashboard_ninja.item'].create_ai_dash(ks_ai_response, ks_dash_id,
ks_model_name)
if (ks_result == "success"):
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
else:
self.env['ks_dashboard_ninja.board'].browse(ks_dash_id).unlink()
raise ValidationError(_("Items didn't render, please try again!"))
else:
raise ValidationError(_("AI Responds with the following status:- %s") % ks_ai_response.text)
else:
raise ValidationError(_("Please enter URL and API Key in General Settings"))
else:
raise ValidationError(_("Please enter the Model"))

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from markupsafe import Markup
from odoo import models, fields, _
class ChatChannel(models.Model):
_inherit = 'discuss.channel'
ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board')
ks_dashboard_item_id = fields.Many2one('ks_dashboard_ninja.item')
def ks_chat_wizard_channel_id(self, **kwargs):
item_id = kwargs.get('item_id')
dashboard_id = kwargs.get('dashboard_id')
item_name = kwargs.get('item_name')
dashboard_name = kwargs.get('dashboard_name')
channel = self.search([('ks_dashboard_item_id', '=', item_id)], limit=1)
if not channel:
users = self.env['res.users'].search([('groups_id', 'in', self.env.ref('base.group_user').ids)]).mapped('partner_id.id')
channel = self.create({
'name': f"{dashboard_name} - {item_name}",
'ks_dashboard_board_id': dashboard_id,
'ks_dashboard_item_id': item_id,
'channel_member_ids': [(0, 0, {'partner_id': partner_id}) for partner_id in users]
})
notification = Markup('<div class="o_mail_notification">%s</div>') % _("created this channel.")
channel.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
self.env.user._bus_send_store(channel)
return channel.id if channel else None

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class KsDashboardNinjaBoardItemAction(models.Model):
_name = 'ks_dashboard_ninja.child_board'
_description = 'Dashboard Ninja Child Board'
name = fields.Char()
ks_dashboard_ninja_id = fields.Many2one("ks_dashboard_ninja.board", string="Select Dashboard")
ks_gridstack_config = fields.Char('Item Configurations')
# ks_board_active_user_ids = fields.Many2many('res.users')
ks_active = fields.Boolean("Is Selected")
ks_dashboard_menu_name = fields.Char(string="Menu Name", related='ks_dashboard_ninja_id.ks_dashboard_menu_name', store=True)
board_type = fields.Selection([('default', 'Default'), ('child', 'Child')])
company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company)
ks_computed_group_access = fields.Many2many('res.groups', compute='_compute_ks_computed_group_access', store=True)
@api.depends('ks_dashboard_ninja_id', 'ks_dashboard_ninja_id.ks_dashboard_group_access')
def _compute_ks_computed_group_access(self):
for record in self:
record.ks_computed_group_access = record.ks_dashboard_ninja_id.ks_dashboard_group_access
def write(self,vals):
return super(KsDashboardNinjaBoardItemAction, self).write(vals)

View File

@@ -0,0 +1,182 @@
country = {
'AF': ('Afghanistan', (60.5284298033, 29.318572496, 75.1580277851, 38.4862816432)),
'AO': ('Angola', (11.6400960629, -17.9306364885, 24.0799052263, -4.43802336998)),
'AL': ('Albania', (19.3044861183, 39.624997667, 21.0200403175, 42.6882473822)),
'AE': ('United Arab Emirates', (51.5795186705, 22.4969475367, 56.3968473651, 26.055464179)),
'AR': ('Argentina', (-73.4154357571, -55.25, -53.628348965, -21.8323104794)),
'AM': ('Armenia', (43.5827458026, 38.7412014837, 46.5057198423, 41.2481285671)),
'AQ': ('Antarctica', (-180.0, -90.0, 180.0, -63.2706604895)),
'TF': ('Fr. S. and Antarctic Lands', (68.72, -49.775, 70.56, -48.625)),
'AU': ('Australia', (113.338953078, -43.6345972634, 153.569469029, -10.6681857235)),
'AT': ('Austria', (9.47996951665, 46.4318173285, 16.9796667823, 49.0390742051)),
'AZ': ('Azerbaijan', (44.7939896991, 38.2703775091, 50.3928210793, 41.8606751572)),
'BI': ('Burundi', (29.0249263852, -4.49998341229, 30.752262811, -2.34848683025)),
'BE': ('Belgium', (2.51357303225, 49.5294835476, 6.15665815596, 51.4750237087)),
'BJ': ('Benin', (0.772335646171, 6.14215770103, 3.79711225751, 12.2356358912)),
'BF': ('Burkina Faso', (-5.47056494793, 9.61083486576, 2.17710778159, 15.1161577418)),
'BD': ('Bangladesh', (88.0844222351, 20.670883287, 92.6727209818, 26.4465255803)),
'BG': ('Bulgaria', (22.3805257504, 41.2344859889, 28.5580814959, 44.2349230007)),
'BS': ('Bahamas', (-78.98, 23.71, -77.0, 27.04)),
'BA': ('Bosnia and Herz.', (15.7500260759, 42.65, 19.59976, 45.2337767604)),
'BY': ('Belarus', (23.1994938494, 51.3195034857, 32.6936430193, 56.1691299506)),
'BZ': ('Belize', (-89.2291216703, 15.8869375676, -88.1068129138, 18.4999822047)),
'BO': ('Bolivia', (-69.5904237535, -22.8729187965, -57.4983711412, -9.76198780685)),
'BR': ('Brazil', (-73.9872354804, -33.7683777809, -34.7299934555, 5.24448639569)),
'BN': ('Brunei', (114.204016555, 4.007636827, 115.450710484, 5.44772980389)),
'BT': ('Bhutan', (88.8142484883, 26.7194029811, 92.1037117859, 28.2964385035)),
'BW': ('Botswana', (19.8954577979, -26.8285429827, 29.4321883481, -17.6618156877)),
'CF': ('Central African Rep.', (14.4594071794, 2.2676396753, 27.3742261085, 11.1423951278)),
'CA': ('Canada', (-140.99778, 41.6751050889, -52.6480987209, 83.23324)),
'CH': ('Switzerland', (6.02260949059, 45.7769477403, 10.4427014502, 47.8308275417)),
'CL': ('Chile', (-75.6443953112, -55.61183, -66.95992, -17.5800118954)),
'CN': ('China', (73.6753792663, 18.197700914, 135.026311477, 53.4588044297)),
'CI': ('Ivory Coast', (-8.60288021487, 4.33828847902, -2.56218950033, 10.5240607772)),
'CM': ('Cameroon', (8.48881554529, 1.72767263428, 16.0128524106, 12.8593962671)),
'CD': ('Congo (Kinshasa)', (12.1823368669, -13.2572266578, 31.1741492042, 5.25608775474)),
'CG': ('Congo (Brazzaville)', (11.0937728207, -5.03798674888, 18.4530652198, 3.72819651938)),
'CO': ('Colombia', (-78.9909352282, -4.29818694419, -66.8763258531, 12.4373031682)),
'CR': ('Costa Rica', (-85.94172543, 8.22502798099, -82.5461962552, 11.2171192489)),
'CU': ('Cuba', (-84.9749110583, 19.8554808619, -74.1780248685, 23.1886107447)),
'CY': ('Cyprus', (32.2566671079, 34.5718694118, 34.0048808123, 35.1731247015)),
'CZ': ('Czech Rep.', (12.2401111182, 48.5553052842, 18.8531441586, 51.1172677679)),
'DE': ('Germany', (5.98865807458, 47.3024876979, 15.0169958839, 54.983104153)),
'DJ': ('Djibouti', (41.66176, 10.9268785669, 43.3178524107, 12.6996385767)),
'DK': ('Denmark', (8.08997684086, 54.8000145534, 12.6900061378, 57.730016588)),
'DO': ('Dominican Rep.', (-71.9451120673, 17.598564358, -68.3179432848, 19.8849105901)),
'DZ': ('Algeria', (-8.68439978681, 19.0573642034, 11.9995056495, 37.1183806422)),
'EC': ('Ecuador', (-80.9677654691, -4.95912851321, -75.2337227037, 1.3809237736)),
'EG': ('Egypt', (24.70007, 22.0, 36.86623, 31.58568)),
'ER': ('Eritrea', (36.3231889178, 12.4554157577, 43.0812260272, 17.9983074)),
'ES': ('Spain', (-9.39288367353, 35.946850084, 3.03948408368, 43.7483377142)),
'EE': ('Estonia', (23.3397953631, 57.4745283067, 28.1316992531, 59.6110903998)),
'ET': ('Ethiopia', (32.95418, 3.42206, 47.78942, 14.95943)),
'FI': ('Finland', (20.6455928891, 59.846373196, 31.5160921567, 70.1641930203)),
'FJ': ('Fiji', (-180.0, -18.28799, 180.0, -16.0208822567)),
'FK': ('Falkland Is.', (-61.2, -52.3, -57.75, -51.1)),
'FR': ('France', (-54.5247541978, 2.05338918702, 9.56001631027, 51.1485061713)),
'GA': ('Gabon', (8.79799563969, -3.97882659263, 14.4254557634, 2.32675751384)),
'GB': ('United Kingdom', (-7.57216793459, 49.959999905, 1.68153079591, 58.6350001085)),
'GE': ('Georgia', (39.9550085793, 41.0644446885, 46.6379081561, 43.553104153)),
'GH': ('Ghana', (-3.24437008301, 4.71046214438, 1.0601216976, 11.0983409693)),
'GN': ('Guinea', (-15.1303112452, 7.3090373804, -7.83210038902, 12.5861829696)),
'GM': ('Gambia', (-16.8415246241, 13.1302841252, -13.8449633448, 13.8764918075)),
'GW': ('Guinea Bissau', (-16.6774519516, 11.0404116887, -13.7004760401, 12.6281700708)),
'GQ': ('Eq. Guinea', (9.3056132341, 1.01011953369, 11.285078973, 2.28386607504)),
'GR': ('Greece', (20.1500159034, 34.9199876979, 26.6041955909, 41.8269046087)),
'GL': ('Greenland', (-73.297, 60.03676, -12.20855, 83.64513)),
'GT': ('Guatemala', (-92.2292486234, 13.7353376327, -88.2250227526, 17.8193260767)),
'GY': ('Guyana', (-61.4103029039, 1.26808828369, -56.5393857489, 8.36703481692)),
'HN': ('Honduras', (-89.3533259753, 12.9846857772, -83.147219001, 16.0054057886)),
'HR': ('Croatia', (13.6569755388, 42.47999136, 19.3904757016, 46.5037509222)),
'HT': ('Haiti', (-74.4580336168, 18.0309927434, -71.6248732164, 19.9156839055)),
'HU': ('Hungary', (16.2022982113, 45.7594811061, 22.710531447, 48.6238540716)),
'ID': ('Indonesia', (95.2930261576, -10.3599874813, 141.03385176, 5.47982086834)),
'IN': ('India', (68.1766451354, 7.96553477623, 97.4025614766, 35.4940095078)),
'IE': ('Ireland', (-9.97708574059, 51.6693012559, -6.03298539878, 55.1316222195)),
'IR': ('Iran', (44.1092252948, 25.0782370061, 63.3166317076, 39.7130026312)),
'IQ': ('Iraq', (38.7923405291, 29.0990251735, 48.5679712258, 37.3852635768)),
'IS': ('Iceland', (-24.3261840479, 63.4963829617, -13.609732225, 66.5267923041)),
'IL': ('Israel', (34.2654333839, 29.5013261988, 35.8363969256, 33.2774264593)),
'IT': ('Italy', (6.7499552751, 36.619987291, 18.4802470232, 47.1153931748)),
'JM': ('Jamaica', (-78.3377192858, 17.7011162379, -76.1996585761, 18.5242184514)),
'JO': ('Jordan', (34.9226025734, 29.1974946152, 39.1954683774, 33.3786864284)),
'JP': ('Japan', (129.408463169, 31.0295791692, 145.543137242, 45.5514834662)),
'KZ': ('Kazakhstan', (46.4664457538, 40.6623245306, 87.3599703308, 55.3852501491)),
'KE': ('Kenya', (33.8935689697, -4.67677, 41.8550830926, 5.506)),
'KG': ('Kyrgyzstan', (69.464886916, 39.2794632025, 80.2599902689, 43.2983393418)),
'KH': ('Cambodia', (102.3480994, 10.4865436874, 107.614547968, 14.5705838078)),
'KR': ('S. Korea', (126.117397903, 34.3900458847, 129.468304478, 38.6122429469)),
'KW': ('Kuwait', (46.5687134133, 28.5260627304, 48.4160941913, 30.0590699326)),
'LA': ('Laos', (100.115987583, 13.88109101, 107.564525181, 22.4647531194)),
'LB': ('Lebanon', (35.1260526873, 33.0890400254, 36.6117501157, 34.6449140488)),
'LR': ('Liberia', (-11.4387794662, 4.35575511313, -7.53971513511, 8.54105520267)),
'LY': ('Libya', (9.31941084152, 19.58047, 25.16482, 33.1369957545)),
'LK': ('Sri Lanka', (79.6951668639, 5.96836985923, 81.7879590189, 9.82407766361)),
'LS': ('Lesotho', (26.9992619158, -30.6451058896, 29.3251664568, -28.6475017229)),
'LT': ('Lithuania', (21.0558004086, 53.9057022162, 26.5882792498, 56.3725283881)),
'LU': ('Luxembourg', (5.67405195478, 49.4426671413, 6.24275109216, 50.1280516628)),
'LV': ('Latvia', (21.0558004086, 55.61510692, 28.1767094256, 57.9701569688)),
'MA': ('Morocco', (-17.0204284327, 21.4207341578, -1.12455115397, 35.7599881048)),
'MD': ('Moldova', (26.6193367856, 45.4882831895, 30.0246586443, 48.4671194525)),
'MG': ('Madagascar', (43.2541870461, -25.6014344215, 50.4765368996, -12.0405567359)),
'MX': ('Mexico', (-117.12776, 14.5388286402, -86.811982388, 32.72083)),
'MK': ('Macedonia', (20.46315, 40.8427269557, 22.9523771502, 42.3202595078)),
'ML': ('Mali', (-12.1707502914, 10.0963607854, 4.27020999514, 24.9745740829)),
'MM': ('Myanmar', (92.3032344909, 9.93295990645, 101.180005324, 28.335945136)),
'ME': ('Montenegro', (18.45, 41.87755, 20.3398, 43.52384)),
'MN': ('Mongolia', (87.7512642761, 41.5974095729, 119.772823928, 52.0473660345)),
'MZ': ('Mozambique', (30.1794812355, -26.7421916643, 40.7754752948, -10.3170960425)),
'MR': ('Mauritania', (-17.0634232243, 14.6168342147, -4.92333736817, 27.3957441269)),
'MW': ('Malawi', (32.6881653175, -16.8012997372, 35.7719047381, -9.23059905359)),
'MY': ('Malaysia', (100.085756871, 0.773131415201, 119.181903925, 6.92805288332)),
'NA': ('Namibia', (11.7341988461, -29.045461928, 25.0844433937, -16.9413428687)),
'NC': ('New Caledonia', (164.029605748, -22.3999760881, 167.120011428, -20.1056458473)),
'NE': ('Niger', (0.295646396495, 11.6601671412, 15.9032466977, 23.4716684026)),
'NG': ('Nigeria', (2.69170169436, 4.24059418377, 14.5771777686, 13.8659239771)),
'NI': ('Nicaragua', (-87.6684934151, 10.7268390975, -83.147219001, 15.0162671981)),
'NL': ('Netherlands', (3.31497114423, 50.803721015, 7.09205325687, 53.5104033474)),
'NO': ('Norway', (4.99207807783, 58.0788841824, 31.29341841, 80.6571442736)),
'NP': ('Nepal', (80.0884245137, 26.3978980576, 88.1748043151, 30.4227169866)),
'NZ': ('New Zealand', (166.509144322, -46.641235447, 178.517093541, -34.4506617165)),
'OM': ('Oman', (52.0000098, 16.6510511337, 59.8080603372, 26.3959343531)),
'PK': ('Pakistan', (60.8742484882, 23.6919650335, 77.8374507995, 37.1330309108)),
'PA': ('Panama', (-82.9657830472, 7.2205414901, -77.2425664944, 9.61161001224)),
'PE': ('Peru', (-81.4109425524, -18.3479753557, -68.6650797187, -0.0572054988649)),
'PH': ('Philippines', (117.17427453, 5.58100332277, 126.537423944, 18.5052273625)),
'PG': ('Papua New Guinea', (141.000210403, -10.6524760881, 156.019965448, -2.50000212973)),
'PL': ('Poland', (14.0745211117, 49.0273953314, 24.0299857927, 54.8515359564)),
'PR': ('Puerto Rico', (-67.2424275377, 17.946553453, -65.5910037909, 18.5206011011)),
'KP': ('N. Korea', (124.265624628, 37.669070543, 130.780007359, 42.9853868678)),
'PT': ('Portugal', (-9.52657060387, 36.838268541, -6.3890876937, 42.280468655)),
'PY': ('Paraguay', (-62.6850571357, -27.5484990374, -54.2929595608, -19.3427466773)),
'QA': ('Qatar', (50.7439107603, 24.5563308782, 51.6067004738, 26.1145820175)),
'RO': ('Romania', (20.2201924985, 43.6884447292, 29.62654341, 48.2208812526)),
'RU': ('Russia', (-180.0, 41.151416124, 180.0, 81.2504)),
'RW': ('Rwanda', (29.0249263852, -2.91785776125, 30.8161348813, -1.13465911215)),
'SA': ('Saudi Arabia', (34.6323360532, 16.3478913436, 55.6666593769, 32.161008816)),
'SD': ('Sudan', (21.93681, 8.61972971293, 38.4100899595, 22.0)),
'SS': ('S. Sudan', (23.8869795809, 3.50917, 35.2980071182, 12.2480077571)),
'SN': ('Senegal', (-17.6250426905, 12.332089952, -11.4678991358, 16.5982636581)),
'SB': ('Solomon Is.', (156.491357864, -10.8263672828, 162.398645868, -6.59933847415)),
'SL': ('Sierra Leone', (-13.2465502588, 6.78591685631, -10.2300935531, 10.0469839543)),
'SV': ('El Salvador', (-90.0955545723, 13.1490168319, -87.7235029772, 14.4241327987)),
'SO': ('Somalia', (40.98105, -1.68325, 51.13387, 12.02464)),
'RS': ('Serbia', (18.82982, 42.2452243971, 22.9860185076, 46.1717298447)),
'SR': ('Suriname', (-58.0446943834, 1.81766714112, -53.9580446031, 6.0252914494)),
'SK': ('Slovakia', (16.8799829444, 47.7584288601, 22.5581376482, 49.5715740017)),
'SI': ('Slovenia', (13.6981099789, 45.4523163926, 16.5648083839, 46.8523859727)),
'SE': ('Sweden', (11.0273686052, 55.3617373725, 23.9033785336, 69.1062472602)),
'SZ': ('Swaziland', (30.6766085141, -27.2858794085, 32.0716654803, -25.660190525)),
'SY': ('Syria', (35.7007979673, 32.312937527, 42.3495910988, 37.2298725449)),
'TD': ('Chad', (13.5403935076, 7.42192454674, 23.88689, 23.40972)),
'TG': ('Togo', (-0.0497847151599, 5.92883738853, 1.86524051271, 11.0186817489)),
'TH': ('Thailand', (97.3758964376, 5.69138418215, 105.589038527, 20.4178496363)),
'TJ': ('Tajikistan', (67.4422196796, 36.7381712916, 74.9800024759, 40.9602133245)),
'TM': ('Turkmenistan', (52.5024597512, 35.2706639674, 66.5461503437, 42.7515510117)),
'TL': ('East Timor', (124.968682489, -9.39317310958, 127.335928176, -8.27334482181)),
'TT': ('Trinidad and Tobago', (-61.95, 10.0, -60.895, 10.89)),
'TN': ('Tunisia', (7.52448164229, 30.3075560572, 11.4887874691, 37.3499944118)),
'TR': ('Turkey', (26.0433512713, 35.8215347357, 44.7939896991, 42.1414848903)),
'TW': ('Taiwan', (120.106188593, 21.9705713974, 121.951243931, 25.2954588893)),
'TZ': ('Tanzania', (29.3399975929, -11.7209380022, 40.31659, -0.95)),
'UG': ('Uganda', (29.5794661801, -1.44332244223, 35.03599, 4.24988494736)),
'UA': ('Ukraine', (22.0856083513, 44.3614785833, 40.0807890155, 52.3350745713)),
'UY': ('Uruguay', (-58.4270741441, -34.9526465797, -53.209588996, -30.1096863746)),
'US': ('United States', (-171.791110603, 18.91619, -66.96466, 71.3577635769)),
'UZ': ('Uzbekistan', (55.9289172707, 37.1449940049, 73.055417108, 45.5868043076)),
'VE': ('Venezuela', (-73.3049515449, 0.724452215982, -59.7582848782, 12.1623070337)),
'VN': ('Vietnam', (102.170435826, 8.59975962975, 109.33526981, 23.3520633001)),
'VU': ('Vanuatu', (166.629136998, -16.5978496233, 167.844876744, -14.6264970842)),
'PS': ('West Bank', (34.9274084816, 31.3534353704, 35.5456653175, 32.5325106878)),
'YE': ('Yemen', (42.6048726743, 12.5859504257, 53.1085726255, 19.0000033635)),
'ZA': ('South Africa', (16.3449768409, -34.8191663551, 32.830120477, -22.0913127581)),
'ZM': ('Zambia', (21.887842645, -17.9612289364, 33.4856876971, -8.23825652429)),
'ZW': ('Zimbabwe', (25.2642257016, -22.2716118303, 32.8498608742, -15.5077869605)),
}
def get_country_code(country_id):
if country_id in country.keys():
return country.get(country_id)
else:
return {}

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
from odoo.addons.ks_dashboard_ninja.common_lib.filter_tools import replace_company_domain
from odoo.exceptions import ValidationError
from odoo.tools.safe_eval import safe_eval
from odoo import models, fields, api, _
class KsDashboardNinjaTemplate(models.Model):
_name = 'ks_dashboard_ninja.board_defined_filters'
_description = 'Dashboard Ninja Defined Filters'
name = fields.Char('Filter Label')
ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board', string="Dashboard")
ks_model_id = fields.Many2one('ir.model', string='Model',
domain="[('access_ids','!=',False),('transient','=',False),"
"('model','not ilike','base_import%'),'|',('model','not ilike','ir.%'),('model','=ilike','_%ir.%'),"
"('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%'),"
"('model','!=','mail.thread'),('model','not ilike','ks_dash%'), ('model','not ilike','ks_to%')]",
help="Data source to fetch and read the data for the creation of dashboard items. ")
ks_domain = fields.Char(string="Domain", help="Define conditions for filter. ")
ks_domain_temp = fields.Char(string="Domain Substitute")
ks_model_name = fields.Char(related='ks_model_id.model', string="Model Name")
display_type = fields.Selection([
('line_section', "Section")], default=False, help="Technical field for UX purpose.")
sequence = fields.Integer(default=10,
help="Gives the sequence order when displaying a list of payment terms lines.")
ks_is_active = fields.Boolean(string="Active")
@api.onchange('ks_domain')
def ks_domain_onchange(self):
for rec in self:
if rec.ks_model_id:
try:
ks_domain = rec.ks_domain
if ks_domain and "%UID" in ks_domain:
ks_domain = ks_domain.replace('"%UID"', str(self.env.user.id))
if ks_domain and "%MYCOMPANY" in ks_domain:
ks_domain = replace_company_domain(ks_domain, self.env.company.id, self.env.companies.ids)
self.env[rec.ks_model_id.model].search_count(safe_eval(ks_domain))
except Exception as e:
raise ValidationError(_("Something went wrong . Possibly it is due to wrong input type for domain"))
@api.constrains('ks_domain', 'ks_model_id')
def ks_domain_check(self):
for rec in self:
if rec.ks_model_id and not rec.ks_domain:
raise ValidationError(_("Domain can not be empty"))
class KsDashboardNinjaTemplate(models.Model):
_name = 'ks_dashboard_ninja.board_custom_filters'
_description = 'Dashboard Ninja Custom Filters'
name = fields.Char("Filter Label")
ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board', string="Dashboard")
ks_model_id = fields.Many2one('ir.model', string='Model',
domain="[('access_ids','!=',False),('transient','=',False),"
"('model','not ilike','base_import%'),'|',('model','not ilike','ir.%'),('model','=ilike','_%ir.%'),"
"('model','not ilike','web_editor.%'),('model','not ilike','web_tour.%'),"
"('model','!=','mail.thread'),('model','not ilike','ks_dash%'), ('model','not ilike','ks_to%')]",
help="Data source to fetch and read the data for the creation of dashboard items. ")
ks_domain_field_id = fields.Many2one('ir.model.fields',
domain="[('model_id','=',ks_model_id),"
"('name','!=','id'),('store','=',True),"
"('ttype', 'in', ['boolean', 'char', "
"'date', 'datetime', 'float', 'integer', 'html', 'many2many', "
"'many2one', 'monetary', 'one2many', 'text', 'selection'])]",
string="Domain Field")
@api.onchange('ks_model_id')
def on_change_ks_model_id(self):
self.ks_domain_field_id = False
class KsDashboardNinjaTemplateFilters(models.Model):
_name = 'ks_dashboard_ninja.favourite_filters'
_description = 'Dashboard Ninja Favourite Filters'
name = fields.Char("Filter Label")
ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board', string="Dashboard")
ks_filter = fields.Char("Filter")
ks_access_id = fields.Integer("Access Id")
ks_filter_type = fields.Char(default='favourite')
_sql_constraints = [
('name_uniq', 'UNIQUE (name)', 'The name of the filter must be unique!'),
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class KsDashboardNinjaTemplate(models.Model):
_name = 'ks_dashboard_ninja.board_template'
_description = 'Dashboard Ninja Template'
name = fields.Char()
ks_gridstack_config = fields.Char()
ks_item_count = fields.Integer()
ks_template_type = fields.Selection([('ks_default', 'Predefined'), ('ks_custom', 'Custom')],
string="Template Format")
ks_dashboard_item_ids = fields.One2many('ks_dashboard_ninja.item', 'ks_dashboard_board_template_id',
string="Template Type")
ks_dashboard_board_id = fields.Many2one('ks_dashboard_ninja.board', string="Dashboard", help="""
Items Configuration and their position in the dashboard will be copied from the selected dashboard
and will be saved as template.
""")
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if val.get('ks_template_type', False) and val.get('ks_dashboard_board_id', False):
dashboard_id = self.env['ks_dashboard_ninja.board'].browse(val.get('ks_dashboard_board_id'))
val['ks_gridstack_config'] = dashboard_id.ks_gridstack_config
val['ks_item_count'] = len(dashboard_id.ks_dashboard_items_ids)
val['ks_dashboard_item_ids'] = [(4, x.copy({'ks_dashboard_ninja_board_id': False}).id) for x in
dashboard_id.ks_dashboard_items_ids]
recs = super(KsDashboardNinjaTemplate, self).create(vals_list)
return recs
def write(self, val):
if val.get('ks_dashboard_board_id', False):
dashboard_id = self.env['ks_dashboard_ninja.board'].browse(val.get('ks_dashboard_board_id'))
val['ks_gridstack_config'] = dashboard_id.ks_gridstack_config
val['ks_item_count'] = len(dashboard_id.ks_dashboard_items_ids)
val['ks_dashboard_item_ids'] = [(6, 0,
[x.copy({'ks_dashboard_ninja_board_id': False}).id for x in
dashboard_id.ks_dashboard_items_ids])]
recs = super(KsDashboardNinjaTemplate, self).write(val)
return recs

View File

@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
import json
import re
from odoo.exceptions import ValidationError
from odoo import models, fields, api, _
class KsDashboardNinjaItems(models.Model):
_inherit = 'ks_dashboard_ninja.item'
ks_to_do_preview = fields.Char("To Do Preview", default="To Do Preview")
ks_dn_header_lines = fields.One2many('ks_to.do.headers', 'ks_dn_item_id')
ks_to_do_data = fields.Char(string="To Do Data in JSon", compute='ks_get_to_do_view_data', compute_sudo=False)
ks_header_bg_color = fields.Char(string="Header Background Color", default="#8e24aa,0.99",
help=' Select the background color with transparency. ')
@api.depends('ks_dn_header_lines', 'ks_dashboard_item_type')
def ks_get_to_do_view_data(self):
for rec in self:
ks_to_do_data = rec._ksGetToDOData()
rec.ks_to_do_data = ks_to_do_data
def _ksGetToDOData(self):
ks_to_do_data = {
'label': [],
'ks_link': [],
'ks_href_id': [],
'ks_section_id': [],
'ks_content': {},
'ks_content_record_id': {},
'ks_content_active': {}
}
if self.ks_dn_header_lines:
for ks_dn_header_line in self.ks_dn_header_lines:
ks_to_do_header_label = ks_dn_header_line.ks_to_do_header[:]
ks_to_do_data['label'].append(ks_to_do_header_label)
ks_dn_header_line_id = str(ks_dn_header_line.id)
if type(ks_dn_header_line.id).__name__ != 'int' and ks_dn_header_line.id.ref != None:
ks_dn_header_line_id = ks_dn_header_line.id.ref
if ' ' in ks_dn_header_line.ks_to_do_header:
ks_temp = ks_dn_header_line.ks_to_do_header.replace(" ", "")
ks_to_do_data['ks_link'].append('#' + ks_temp + ks_dn_header_line_id)
ks_to_do_data['ks_href_id'].append(ks_temp + str(ks_dn_header_line.id))
elif ks_dn_header_line.ks_to_do_header[0].isdigit():
ks_temp = ks_dn_header_line.ks_to_do_header.replace(
ks_dn_header_line.ks_to_do_header[0], 'z')
ks_to_do_data['ks_link'].append('#' + ks_temp + ks_dn_header_line_id)
ks_to_do_data['ks_href_id'].append(ks_temp + str(ks_dn_header_line.id))
else:
ks_to_do_data['ks_link'].append('#' + ks_dn_header_line.ks_to_do_header + ks_dn_header_line_id)
ks_to_do_data['ks_href_id'].append(ks_dn_header_line.ks_to_do_header + str(ks_dn_header_line.id))
ks_to_do_data['ks_section_id'].append(str(ks_dn_header_line.id))
if len(ks_dn_header_line.ks_to_do_description_lines):
for ks_to_do_description_line in ks_dn_header_line.ks_to_do_description_lines:
if ' ' in ks_dn_header_line.ks_to_do_header or ks_dn_header_line.ks_to_do_header[0].isdigit():
if ks_to_do_data['ks_content'].get(ks_temp +
str(ks_dn_header_line.id), False):
ks_to_do_data['ks_content'][ks_temp +
str(ks_dn_header_line.id)].append(
ks_to_do_description_line.ks_description)
ks_to_do_data['ks_content_record_id'][ks_temp +
str(ks_dn_header_line.id)].append(
str(ks_to_do_description_line.id))
ks_to_do_data['ks_content_active'][ks_temp +
str(ks_dn_header_line.id)].append(
str(ks_to_do_description_line.ks_active))
else:
ks_to_do_data['ks_content'][ks_temp +
str(ks_dn_header_line.id)] = [
ks_to_do_description_line.ks_description]
ks_to_do_data['ks_content_record_id'][ks_temp +
str(ks_dn_header_line.id)] = [
str(ks_to_do_description_line.id)]
ks_to_do_data['ks_content_active'][ks_temp +
str(ks_dn_header_line.id)] = [
str(ks_to_do_description_line.ks_active)]
else:
if ks_to_do_data['ks_content'].get(ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id), False):
ks_to_do_data['ks_content'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)].append(
ks_to_do_description_line.ks_description)
ks_to_do_data['ks_content_record_id'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)].append(
str(ks_to_do_description_line.id))
ks_to_do_data['ks_content_active'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)].append(
str(ks_to_do_description_line.ks_active))
else:
ks_to_do_data['ks_content'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)] = [
ks_to_do_description_line.ks_description]
ks_to_do_data['ks_content_record_id'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)] = [
str(ks_to_do_description_line.id)]
ks_to_do_data['ks_content_active'][ks_dn_header_line.ks_to_do_header +
str(ks_dn_header_line.id)] = [
str(ks_to_do_description_line.ks_active)]
ks_to_do_data = json.dumps(ks_to_do_data)
else:
ks_to_do_data = False
return ks_to_do_data
class KsToDoheaders(models.Model):
_name = 'ks_to.do.headers'
_description = "to do headers"
ks_dn_item_id = fields.Many2one('ks_dashboard_ninja.item')
ks_to_do_header = fields.Char('Header')
ks_to_do_description_lines = fields.One2many('ks_to.do.description', 'ks_to_do_header_id')
@api.constrains('ks_to_do_header')
def ks_to_do_header_check(self):
for rec in self:
if rec.ks_to_do_header:
ks_check = bool(re.match('^[A-Z, a-z,0-9,_]+$', rec.ks_to_do_header))
if not ks_check:
raise ValidationError(_("Special characters are not allowed only string and digits allow for section header"))
@api.onchange('ks_to_do_header')
def ks_to_do_header_onchange(self):
for rec in self:
if rec.ks_to_do_header:
ks_check = bool(re.match('^[A-Z, a-z,0-9,_]+$', rec.ks_to_do_header))
if not ks_check:
raise ValidationError(_("Special characters are not allowed only string and digits allow for section header"))
class KsToDODescription(models.Model):
_name = 'ks_to.do.description'
_description = 'to do description'
ks_to_do_header_id = fields.Many2one('ks_to.do.headers')
ks_description = fields.Text('Description')
ks_active = fields.Boolean('Active Description', default=True)

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
import base64
import logging
from odoo.exceptions import ValidationError
from odoo import fields, models, _
_logger = logging.getLogger(__name__)
class KsDashboardNInjaImport(models.TransientModel):
_name = 'ks_dashboard_ninja.import'
_description = 'Import Dashboard'
ks_import_dashboard = fields.Binary(string="Upload Dashboard", attachment=True)
ks_top_menu_id = fields.Many2one('ir.ui.menu', string="Show Under Menu", domain="[('parent_id','=',False)]",
required=True,
default=lambda self: self.env['ir.ui.menu'].search(
[('name', '=', 'My Dashboards')]))
def ks_do_action(self):
for rec in self:
try:
ks_result = base64.b64decode(rec.ks_import_dashboard)
self.env['ks_dashboard_ninja.board'].ks_import_dashboard(ks_result, self.ks_top_menu_id)
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
except Exception as E:
_logger.warning(E)
raise ValidationError(_(str(E)))

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class KsDashboardNinjaBoardItemAction(models.TransientModel):
_name = 'ks_ninja_dashboard.item_action'
_description = 'Dashboard Ninja Item Actions'
name = fields.Char()
ks_dashboard_item_ids = fields.Many2many("ks_dashboard_ninja.item", string="Dashboard Items")
ks_action = fields.Selection([('move', 'Move'),
('duplicate', 'Duplicate'),
], string="Action")
ks_dashboard_ninja_id = fields.Many2one("ks_dashboard_ninja.board", string="Select Dashboard")
ks_dashboard_ninja_ids = fields.Many2many("ks_dashboard_ninja.board", string="Select Dashboards")
# Move or Copy item to another dashboard action
def action_item_move_copy_action(self):
if self.ks_action == 'move':
for item in self.ks_dashboard_item_ids:
item.ks_dashboard_ninja_board_id = self.ks_dashboard_ninja_id
elif self.ks_action == 'duplicate':
# Using sudo here to allow creating same item without any security error
for dashboard_id in self.ks_dashboard_ninja_ids:
for item in self.ks_dashboard_item_ids:
item.sudo().copy({'ks_dashboard_ninja_board_id': dashboard_id.id})

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
import json
import logging
import requests
from odoo.exceptions import ValidationError
from odoo import fields, models, _
_logger = logging.getLogger(__name__)
class KsAIDashboardFetch(models.TransientModel):
_name = 'ks_dashboard_ninja.fetch_key'
_description = 'Fetch API key'
ks_email_id = fields.Char(string="Email ID")
ks_api_key =fields.Char(string="Generated AI API Key")
ks_show_api_key = fields.Boolean(string="Show key",default=False)
def ks_fetch_details(self):
url = self.env['ir.config_parameter'].sudo().get_param(
'ks_dashboard_ninja.url')
if url and self.ks_email_id:
url = url + "/api/v1/ks_dn_fetch_api"
json_data = {'email':self.ks_email_id}
ks_ai_response = requests.post(url,data=json_data)
if ks_ai_response.status_code == 200:
ks_ai_response = json.loads(ks_ai_response.text)
self.ks_api_key = ks_ai_response
self.ks_show_api_key = True
else:
raise ValidationError(_("Error generates with following status %s"),ks_ai_response.status_code)

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
import json
import requests
from odoo.exceptions import ValidationError
from odoo import fields, models, _
class ResConfig(models.TransientModel):
_inherit = "res.config.settings"
dn_api_key = fields.Char(string="Dashboard AI API Key",store=True,
config_parameter='ks_dashboard_ninja.dn_api_key')
enable_chart_zoom = fields.Boolean(string="Enable Zooming for charts", store=True,
config_parameter='ks_dashboard_ninja.enable_chart_zoom')
url = fields.Char(string="URL", store=True,
config_parameter="ks_dashboard_ninja.url")
ks_email_id = fields.Char(string="Email ID",store=True,config_parameter="ks_dashboard_ninja.ks_email_id")
ks_analysis_word_length = fields.Selection([("50","50 words"),("100","100 words"),("150","150 words"),("200","200 words"),],default ="100", string="AI Analysis length", store=True,config_parameter="ks_dashboard_ninja.ks_analysis_word_length")
def Open_wizard(self):
if self.url and self.ks_email_id:
try:
url = self.url + "/api/v1/ks_dn_fetch_api"
json_data = {'email':self.ks_email_id,
'url':self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
'db_name':self.env.cr.dbname
}
ks_ai_response = requests.post(url,data=json_data)
except Exception as e:
raise ValidationError(_("Please enter correct URL"))
if ks_ai_response.status_code == 200:
try:
ks_ai_response = json.loads(ks_ai_response.text)
except Exception as e:
ks_ai_response = False
if ks_ai_response == "success":
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Success'),
'message': 'API key sent on Email ID',
'sticky': False,
}
}
elif ks_ai_response == 'key already generated':
raise ValidationError(
_("key already generated.If you need assistance, feel free to contact at sales@ksolves.com"))
else:
raise ValidationError(_("Either you have entered wrong URL path or there is some problem in sending request. If you need assistance, feel free to contact at sales@ksolves.com"))
else:
raise ValidationError(_("Some problem in sending request.Please contact at sales@ksolves.com"))
else:
raise ValidationError(_("Please enter URL and Email ID"))