Custom Reports
NopeSight's Custom Report builder empowers users to create tailored reports that meet specific business needs. With an intuitive query builder, AI assistance, and flexible visualization options, you can transform your CMDB data into meaningful insights without requiring technical expertise.
Custom Report Builder
Visual Query Builder
class VisualQueryBuilder {
constructor() {
this.query = {
select: [],
from: [],
joins: [],
where: [],
groupBy: [],
orderBy: [],
limit: null
};
this.canvas = new QueryCanvas();
}
addDataSource(source) {
// Visual representation of data source
const sourceNode = this.canvas.createNode({
type: 'datasource',
id: source.id,
label: source.name,
fields: source.fields,
position: this.canvas.getNextPosition()
});
this.query.from.push({
table: source.table,
alias: source.alias,
type: source.type
});
// Enable field selection
sourceNode.fields.forEach(field => {
field.onClick = () => this.toggleFieldSelection(source, field);
});
return sourceNode;
}
createJoin(source1, source2, joinType = 'inner') {
// Visual join creation
const joinConnection = this.canvas.createConnection({
from: source1.id,
to: source2.id,
type: joinType,
label: 'Configure Join'
});
// Join configuration dialog
joinConnection.onConfigure = () => {
const config = this.showJoinDialog(source1, source2);
this.query.joins.push({
type: joinType,
table: source2.table,
on: {
left: `${source1.alias}.${config.leftField}`,
operator: config.operator || '=',
right: `${source2.alias}.${config.rightField}`
}
});
};
return joinConnection;
}
addFilter(field, operator, value) {
const filterNode = this.canvas.createFilterNode({
field: field,
operator: operator,
value: value,
editable: true
});
this.query.where.push({
field: field.fullPath,
operator: operator,
value: this.parseValue(value, field.type)
});
return filterNode;
}
buildSQL() {
return this.sqlGenerator.generate(this.query);
}
}
Natural Language Query Interface
class NaturalLanguageQueryBuilder:
def __init__(self):
self.nlp_processor = NLPQueryProcessor()
self.query_validator = QueryValidator()
self.suggestions_engine = SuggestionsEngine()
def process_natural_language_query(self, user_query):
"""Convert natural language to structured query"""
# Parse intent and entities
parsed = self.nlp_processor.parse(user_query)
# Extract query components
query_components = {
'intent': parsed.intent, # select, compare, trend, etc.
'entities': parsed.entities, # tables, fields, time ranges
'filters': parsed.filters,
'aggregations': parsed.aggregations,
'groupings': parsed.groupings
}
# Build structured query
structured_query = self.build_structured_query(query_components)
# Validate query
validation = self.query_validator.validate(structured_query)
if not validation.is_valid:
# Get suggestions
suggestions = self.suggestions_engine.get_suggestions(
query_components,
validation.errors
)
return {
'status': 'needs_clarification',
'suggestions': suggestions,
'partial_query': structured_query
}
return {
'status': 'ready',
'query': structured_query,
'explanation': self.explain_query(structured_query),
'preview_available': True
}
def handle_examples(self):
"""Provide example queries"""
return [
{
'category': 'Asset Queries',
'examples': [
"Show me all servers in the production environment",
"Which assets are due for replacement this quarter?",
"List all Windows servers with more than 32GB RAM"
]
},
{
'category': 'Service Queries',
'examples': [
"What services had incidents last week?",
"Show service availability for the past month",
"Which services depend on the customer database?"
]
},
{
'category': 'Trend Analysis',
'examples': [
"How has CPU utilization trended over the last 90 days?",
"Show me the incident trend by service",
"Compare this month's changes to last month"
]
}
]
Report Design Interface
Drag-and-Drop Report Builder
class DragDropReportBuilder {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.sections = [];
this.widgets = [];
this.layout = new GridLayout(12); // 12-column grid
this.initializeDragDrop();
}
initializeDragDrop() {
// Widget palette
this.palette = new WidgetPalette([
{ type: 'chart', icon: 'bar_chart', label: 'Chart' },
{ type: 'table', icon: 'table', label: 'Table' },
{ type: 'kpi', icon: 'dashboard', label: 'KPI Card' },
{ type: 'map', icon: 'map', label: 'Map' },
{ type: 'text', icon: 'text', label: 'Text/Markdown' },
{ type: 'image', icon: 'image', label: 'Image' }
]);
// Make widgets draggable
this.palette.widgets.forEach(widget => {
widget.draggable = true;
widget.ondragstart = (e) => {
e.dataTransfer.setData('widgetType', widget.type);
};
});
// Make report canvas droppable
this.canvas = new ReportCanvas(this.container);
this.canvas.ondrop = (e) => this.handleDrop(e);
this.canvas.ondragover = (e) => e.preventDefault();
}
handleDrop(e) {
e.preventDefault();
const widgetType = e.dataTransfer.getData('widgetType');
const position = this.layout.getDropPosition(e.x, e.y);
const widget = this.createWidget(widgetType, position);
this.addWidget(widget);
// Open configuration dialog
this.configureWidget(widget);
}
createWidget(type, position) {
const widgetConfig = {
id: generateId(),
type: type,
position: position,
size: this.getDefaultSize(type),
config: {},
data: null
};
const widgetElement = this.renderWidget(widgetConfig);
// Make widget resizable and movable
this.makeInteractive(widgetElement);
return widgetConfig;
}
configureWidget(widget) {
const dialog = new WidgetConfigDialog(widget);
dialog.onSave = (config) => {
widget.config = config;
// Update data binding
if (config.dataSource) {
this.bindData(widget, config.dataSource);
}
// Update visualization
this.updateWidgetVisualization(widget);
};
dialog.show();
}
}
Widget Configuration
class WidgetConfigurator:
def __init__(self):
self.widget_types = self.load_widget_types()
self.data_connectors = self.load_data_connectors()
def configure_chart_widget(self, widget_id):
"""Configure chart widget options"""
config = {
'widget_id': widget_id,
'type': 'chart',
'chart_options': {
'chart_type': 'line', # line, bar, pie, scatter, etc.
'title': '',
'subtitle': '',
'axes': {
'x': {
'field': None,
'label': '',
'type': 'auto', # auto, time, category, value
'format': None
},
'y': {
'field': None,
'label': '',
'type': 'auto',
'format': None,
'scale': 'linear' # linear, log
}
},
'series': [],
'legend': {
'show': True,
'position': 'bottom'
},
'colors': 'auto', # auto, custom palette
'interactions': {
'zoom': True,
'pan': True,
'tooltip': True,
'drill_down': False
}
},
'data_config': {
'source': None,
'query': None,
'refresh_interval': None,
'aggregation': None,
'filters': []
}
}
return config
def configure_table_widget(self, widget_id):
"""Configure table widget options"""
return {
'widget_id': widget_id,
'type': 'table',
'table_options': {
'title': '',
'columns': [],
'sorting': {
'enabled': True,
'default_column': None,
'default_order': 'asc'
},
'filtering': {
'enabled': True,
'search_box': True,
'column_filters': True
},
'pagination': {
'enabled': True,
'page_size': 25,
'page_sizes': [10, 25, 50, 100]
},
'row_actions': [],
'export': {
'enabled': True,
'formats': ['csv', 'excel', 'pdf']
},
'styling': {
'striped': True,
'bordered': True,
'hover': True,
'condensed': False
}
}
}
Data Processing
Advanced Aggregations
class DataAggregator {
constructor() {
this.aggregationFunctions = {
sum: (values) => values.reduce((a, b) => a + b, 0),
avg: (values) => this.sum(values) / values.length,
min: (values) => Math.min(...values),
max: (values) => Math.max(...values),
count: (values) => values.length,
distinct: (values) => new Set(values).size,
median: (values) => this.calculateMedian(values),
stddev: (values) => this.calculateStdDev(values),
percentile: (values, p) => this.calculatePercentile(values, p)
};
}
performAggregation(data, config) {
const grouped = this.groupData(data, config.groupBy);
const aggregated = new Map();
for (const [key, group] of grouped) {
const aggregates = {};
for (const agg of config.aggregations) {
const values = group.map(row => row[agg.field]);
const cleanValues = values.filter(v => v != null);
aggregates[agg.alias || agg.field] =
this.aggregationFunctions[agg.function](cleanValues);
}
aggregated.set(key, {
groupKey: key,
...aggregates,
_count: group.length,
_items: config.includeItems ? group : undefined
});
}
return this.formatResults(aggregated, config);
}
groupData(data, groupByFields) {
const groups = new Map();
for (const row of data) {
const key = groupByFields
.map(field => row[field])
.join('||');
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(row);
}
return groups;
}
calculateTimeBasedAggregations(data, timeField, interval) {
// Group by time intervals
const timeGroups = this.groupByTimeInterval(data, timeField, interval);
// Calculate rolling averages, trends, etc.
const enhanced = timeGroups.map((group, index) => {
const rollingAvg = this.calculateRollingAverage(
timeGroups,
index,
7 // 7-period rolling average
);
const trend = this.calculateTrend(
timeGroups.slice(Math.max(0, index - 30), index + 1)
);
return {
...group,
rolling_avg: rollingAvg,
trend: trend
};
});
return enhanced;
}
}
Data Transformations
class DataTransformer:
def __init__(self):
self.transformations = {
'pivot': self.pivot_data,
'unpivot': self.unpivot_data,
'calculate_field': self.calculate_field,
'join': self.join_datasets,
'filter': self.filter_data,
'sort': self.sort_data,
'deduplicate': self.deduplicate_data
}
def apply_transformation_pipeline(self, data, pipeline):
"""Apply series of transformations to data"""
result = data
for step in pipeline:
transformation = self.transformations[step['type']]
result = transformation(result, step['config'])
# Log transformation
self.log_transformation(step, len(result))
return result
def pivot_data(self, data, config):
"""Pivot data from rows to columns"""
pivot_config = {
'index': config['row_fields'],
'columns': config['column_field'],
'values': config['value_field'],
'aggfunc': config.get('aggregation', 'sum')
}
# Create pivot table
df = pd.DataFrame(data)
pivoted = df.pivot_table(**pivot_config)
# Flatten multi-index if needed
if config.get('flatten', True):
pivoted = pivoted.reset_index()
pivoted.columns = [str(col) for col in pivoted.columns]
return pivoted.to_dict('records')
def calculate_field(self, data, config):
"""Add calculated fields to data"""
expression = config['expression']
field_name = config['field_name']
# Safe expression evaluation
safe_dict = {
'sum': sum,
'len': len,
'max': max,
'min': min,
'avg': lambda x: sum(x) / len(x) if len(x) > 0 else 0
}
for row in data:
# Replace field references with values
expr = expression
for field in row:
expr = expr.replace(f'[{field}]', str(row[field]))
# Evaluate expression
try:
row[field_name] = eval(expr, {"__builtins__": {}}, safe_dict)
except Exception as e:
row[field_name] = None
self.log_error(f"Calculation error: {e}")
return data
Visualization Library
Chart Types and Options
class ChartLibrary {
constructor() {
this.chartTypes = {
line: {
name: 'Line Chart',
icon: 'show_chart',
bestFor: 'Trends over time',
requiredData: {
x: 'continuous',
y: 'numeric'
},
options: {
smooth: true,
area: false,
stacked: false,
markers: true
}
},
bar: {
name: 'Bar Chart',
icon: 'bar_chart',
bestFor: 'Comparing categories',
requiredData: {
x: 'categorical',
y: 'numeric'
},
options: {
orientation: 'vertical',
grouped: false,
stacked: false
}
},
scatter: {
name: 'Scatter Plot',
icon: 'scatter_plot',
bestFor: 'Correlations',
requiredData: {
x: 'numeric',
y: 'numeric'
},
options: {
size: 'fixed',
color: 'single',
trendline: false
}
},
heatmap: {
name: 'Heatmap',
icon: 'grid_on',
bestFor: 'Matrix data',
requiredData: {
rows: 'categorical',
columns: 'categorical',
values: 'numeric'
},
options: {
colorScale: 'sequential',
showValues: true
}
}
};
}
recommendChartType(data, intent) {
const dataProfile = this.analyzeDataProfile(data);
const recommendations = [];
for (const [type, config] of Object.entries(this.chartTypes)) {
const score = this.calculateSuitabilityScore(
dataProfile,
config.requiredData,
intent
);
if (score > 0.5) {
recommendations.push({
type: type,
score: score,
reason: this.explainRecommendation(type, dataProfile, intent)
});
}
}
return recommendations.sort((a, b) => b.score - a.score);
}
createChart(type, data, options) {
const chartConfig = {
type: type,
data: this.prepareChartData(type, data),
options: this.mergeOptions(this.chartTypes[type].options, options),
responsive: true,
maintainAspectRatio: false
};
// Add interactivity
chartConfig.options.interaction = {
mode: 'index',
intersect: false
};
// Add plugins
chartConfig.plugins = [
this.createTooltipPlugin(),
this.createZoomPlugin(),
this.createExportPlugin()
];
return new Chart(chartConfig);
}
}
Advanced Visualizations
class AdvancedVisualizations:
def create_sankey_diagram(self, flow_data):
"""Create Sankey diagram for flow visualization"""
import plotly.graph_objects as go
# Prepare nodes and links
nodes = self.extract_nodes(flow_data)
links = self.prepare_links(flow_data, nodes)
fig = go.Figure(data=[go.Sankey(
node=dict(
pad=15,
thickness=20,
line=dict(color="black", width=0.5),
label=[node['label'] for node in nodes],
color=[node['color'] for node in nodes]
),
link=dict(
source=[link['source'] for link in links],
target=[link['target'] for link in links],
value=[link['value'] for link in links],
color=[link['color'] for link in links]
)
)])
fig.update_layout(
title="Service Dependency Flow",
font_size=10,
height=600
)
return fig
def create_sunburst_chart(self, hierarchical_data):
"""Create sunburst chart for hierarchical data"""
import plotly.express as px
# Flatten hierarchical data
flat_data = self.flatten_hierarchy(hierarchical_data)
fig = px.sunburst(
flat_data,
path=['level1', 'level2', 'level3'],
values='value',
color='value',
color_continuous_scale='RdYlBu',
title='Hierarchical Data Visualization'
)
return fig
def create_network_graph(self, nodes, edges):
"""Create interactive network graph"""
import networkx as nx
from pyvis.network import Network
# Create graph
G = nx.Graph()
# Add nodes
for node in nodes:
G.add_node(
node['id'],
label=node['label'],
title=node.get('tooltip', ''),
color=node.get('color', '#97C2FC'),
size=node.get('size', 25)
)
# Add edges
for edge in edges:
G.add_edge(
edge['source'],
edge['target'],
weight=edge.get('weight', 1),
title=edge.get('label', '')
)
# Create interactive visualization
net = Network(
height='600px',
width='100%',
bgcolor='#ffffff',
font_color='#000000'
)
net.from_nx(G)
net.set_options("""
{
"physics": {
"stabilization": {
"enabled": true,
"iterations": 1000
}
},
"interaction": {
"dragNodes": true,
"dragView": true,
"zoomView": true
}
}
""")
return net
Report Formatting
Export Options
class ReportExporter {
constructor(report) {
this.report = report;
this.formatters = {
pdf: new PDFFormatter(),
excel: new ExcelFormatter(),
word: new WordFormatter(),
powerpoint: new PowerPointFormatter(),
html: new HTMLFormatter()
};
}
async exportReport(format, options = {}) {
const formatter = this.formatters[format];
if (!formatter) {
throw new Error(`Unsupported format: ${format}`);
}
// Prepare report data
const preparedData = await this.prepareForExport(format);
// Apply formatting
const formatted = await formatter.format(preparedData, {
...this.getDefaultOptions(format),
...options
});
// Add metadata
const withMetadata = this.addMetadata(formatted, format);
return withMetadata;
}
async exportToPDF(options) {
const pdfOptions = {
orientation: options.orientation || 'portrait',
pageSize: options.pageSize || 'A4',
margins: options.margins || { top: 20, bottom: 20, left: 20, right: 20 },
includeCharts: true,
includePageNumbers: true,
includeTableOfContents: this.report.sections.length > 3
};
// Convert charts to images
const chartsAsImages = await this.convertChartsToImages();
// Build PDF document
const doc = new PDFDocument(pdfOptions);
// Add cover page
if (options.includeCoverPage) {
this.addCoverPage(doc);
}
// Add table of contents
if (pdfOptions.includeTableOfContents) {
this.addTableOfContents(doc);
}
// Add report sections
for (const section of this.report.sections) {
await this.addSectionToPDF(doc, section, chartsAsImages);
}
return doc.generate();
}
}
Branding and Styling
class ReportStyleManager:
def __init__(self):
self.themes = self.load_themes()
self.brand_assets = self.load_brand_assets()
def apply_branding(self, report, brand_config):
"""Apply corporate branding to report"""
branded_report = deepcopy(report)
# Apply logo
if brand_config.get('logo'):
branded_report['header']['logo'] = {
'url': brand_config['logo']['url'],
'position': brand_config['logo'].get('position', 'top-left'),
'size': brand_config['logo'].get('size', 'medium')
}
# Apply color scheme
if brand_config.get('colors'):
branded_report['theme']['colors'] = {
'primary': brand_config['colors'].get('primary', '#1976D2'),
'secondary': brand_config['colors'].get('secondary', '#424242'),
'accent': brand_config['colors'].get('accent', '#82B1FF'),
'background': brand_config['colors'].get('background', '#FFFFFF'),
'text': brand_config['colors'].get('text', '#212121')
}
# Apply fonts
if brand_config.get('fonts'):
branded_report['theme']['typography'] = {
'heading': brand_config['fonts'].get('heading', 'Arial'),
'body': brand_config['fonts'].get('body', 'Arial'),
'monospace': brand_config['fonts'].get('monospace', 'Courier New')
}
# Apply custom CSS
if brand_config.get('custom_css'):
branded_report['custom_styles'] = brand_config['custom_css']
return branded_report
def create_report_template(self, name, base_style='default'):
"""Create reusable report template with styling"""
template = {
'name': name,
'base_style': base_style,
'layout': {
'page_size': 'A4',
'orientation': 'portrait',
'margins': {'top': 25, 'right': 25, 'bottom': 25, 'left': 25},
'header_height': 60,
'footer_height': 40
},
'header': {
'show': True,
'content': {
'left': '[LOGO]',
'center': '[REPORT_TITLE]',
'right': '[DATE]'
}
},
'footer': {
'show': True,
'content': {
'left': '[COMPANY_NAME]',
'center': '[PAGE_NUMBER]',
'right': '[CONFIDENTIALITY]'
}
},
'styles': {
'title': {
'font_size': 24,
'font_weight': 'bold',
'color': '#primary',
'margin_bottom': 20
},
'heading1': {
'font_size': 18,
'font_weight': 'bold',
'color': '#primary',
'margin_top': 20,
'margin_bottom': 10
},
'body': {
'font_size': 11,
'line_height': 1.5,
'color': '#text'
}
}
}
return template
Performance Optimization
Query Optimization
class QueryOptimizer:
def optimize_custom_query(self, query, data_profile):
"""Optimize query for performance"""
optimizations = []
# Analyze query
analysis = self.analyze_query(query)
# Index recommendations
if analysis.missing_indexes:
optimizations.append({
'type': 'index',
'recommendation': 'Create indexes on frequently filtered columns',
'columns': analysis.missing_indexes,
'impact': 'high'
})
# Query rewrite suggestions
if analysis.suboptimal_joins:
optimizations.append({
'type': 'query_rewrite',
'recommendation': 'Reorder joins for better performance',
'original': analysis.join_order,
'optimized': self.optimize_join_order(analysis.joins),
'impact': 'medium'
})
# Aggregation pushdown
if analysis.can_pushdown_aggregation:
optimizations.append({
'type': 'aggregation_pushdown',
'recommendation': 'Push aggregations to data source',
'impact': 'high'
})
# Caching opportunities
cache_config = self.identify_cache_opportunities(query, data_profile)
if cache_config:
optimizations.append({
'type': 'caching',
'recommendation': 'Cache frequently accessed data',
'cache_config': cache_config,
'impact': 'high'
})
return {
'original_query': query,
'optimized_query': self.apply_optimizations(query, optimizations),
'optimizations': optimizations,
'estimated_improvement': self.estimate_improvement(optimizations)
}
Best Practices
1. Query Design
- ✅ Start simple and build complexity
- ✅ Use appropriate aggregations
- ✅ Filter early in the query
- ✅ Limit result sets appropriately
2. Visualization Selection
- ✅ Match chart type to data type
- ✅ Consider audience needs
- ✅ Avoid chart junk
- ✅ Use consistent color schemes
3. Performance
- ✅ Optimize queries before visualization
- ✅ Use data sampling for previews
- ✅ Implement progressive loading
- ✅ Cache common queries
4. Usability
- ✅ Provide clear titles and labels
- ✅ Include context and explanations
- ✅ Enable interactivity where helpful
- ✅ Support multiple export formats
Examples
Sales Performance Dashboard
// Example custom report configuration
const salesPerformanceReport = {
title: "Sales Performance Analysis",
description: "Monthly sales performance metrics and trends",
dataSources: [
{
id: "sales_data",
type: "sql",
query: `
SELECT
DATE_TRUNC('month', order_date) as month,
region,
product_category,
SUM(revenue) as total_revenue,
COUNT(DISTINCT customer_id) as unique_customers,
AVG(order_value) as avg_order_value
FROM sales_orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1, 2, 3
`
}
],
layout: {
type: "responsive",
sections: [
{
id: "summary",
title: "Executive Summary",
grid: { x: 0, y: 0, w: 12, h: 2 },
widgets: [
{
type: "kpi",
title: "Total Revenue",
data: {
source: "sales_data",
metric: "SUM(total_revenue)",
comparison: "previous_period",
format: "currency"
}
}
]
},
{
id: "trends",
title: "Revenue Trends",
grid: { x: 0, y: 2, w: 8, h: 4 },
widgets: [
{
type: "line_chart",
config: {
x: "month",
y: "total_revenue",
series: "region",
showTrend: true
}
}
]
}
]
}
};
Next Steps
- 📖 Dashboard Builder - Creating interactive dashboards
- 📖 AI Analytics - AI-powered analytics
- 📖 Report Scheduling - Automated report delivery