├── .editorconfig ├── .git-blame-ignore-revs ├── .github ├── chart-builder.png ├── frappe-logo.svg ├── helpers │ └── install_dependencies.sh ├── hero-image-v2.png ├── hero-image.png ├── join-editor.png ├── logo.png ├── new-logo.svg ├── preview.gif ├── query-builder.png ├── query-view.png ├── result-view.png ├── try-on-fc.svg ├── visualize-view.png └── workflows │ ├── build.yml │ ├── make-release-pr.yml │ ├── playwright.yml │ ├── release.yml │ └── server-tests.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .releaserc ├── MANIFEST.in ├── README.md ├── docker ├── docker-compose.yml └── init.sh ├── flake8 ├── frontend ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── README.md ├── components.d.ts ├── index.html ├── index_v2.html ├── package.json ├── playwright.config.js ├── postcss.config.js ├── public │ ├── favicon.png │ └── insights-logo.png ├── src │ ├── App.vue │ ├── api │ │ ├── index.ts │ │ └── whitelistedMethods.ts │ ├── assets │ │ ├── ERPNextIcon.png │ │ ├── FiraCode │ │ │ ├── FiraCode-Bold.woff │ │ │ ├── FiraCode-Bold.woff2 │ │ │ ├── FiraCode-Light.woff │ │ │ ├── FiraCode-Light.woff2 │ │ │ ├── FiraCode-Medium.woff │ │ │ ├── FiraCode-Medium.woff2 │ │ │ ├── FiraCode-Regular.woff │ │ │ ├── FiraCode-Regular.woff2 │ │ │ ├── FiraCode-SemiBold.woff │ │ │ ├── FiraCode-SemiBold.woff2 │ │ │ ├── FiraCode-VF.woff │ │ │ ├── FiraCode-VF.woff2 │ │ │ └── fira_code.css │ │ ├── Inter │ │ │ ├── Inter-Black.woff │ │ │ ├── Inter-Black.woff2 │ │ │ ├── Inter-BlackItalic.woff │ │ │ ├── Inter-BlackItalic.woff2 │ │ │ ├── Inter-Bold.woff │ │ │ ├── Inter-Bold.woff2 │ │ │ ├── Inter-BoldItalic.woff │ │ │ ├── Inter-BoldItalic.woff2 │ │ │ ├── Inter-ExtraBold.woff │ │ │ ├── Inter-ExtraBold.woff2 │ │ │ ├── Inter-ExtraBoldItalic.woff │ │ │ ├── Inter-ExtraBoldItalic.woff2 │ │ │ ├── Inter-ExtraLight.woff │ │ │ ├── Inter-ExtraLight.woff2 │ │ │ ├── Inter-ExtraLightItalic.woff │ │ │ ├── Inter-ExtraLightItalic.woff2 │ │ │ ├── Inter-Italic.woff │ │ │ ├── Inter-Italic.woff2 │ │ │ ├── Inter-Light.woff │ │ │ ├── Inter-Light.woff2 │ │ │ ├── Inter-LightItalic.woff │ │ │ ├── Inter-LightItalic.woff2 │ │ │ ├── Inter-Medium.woff │ │ │ ├── Inter-Medium.woff2 │ │ │ ├── Inter-MediumItalic.woff │ │ │ ├── Inter-MediumItalic.woff2 │ │ │ ├── Inter-Regular.woff │ │ │ ├── Inter-Regular.woff2 │ │ │ ├── Inter-SemiBold.woff │ │ │ ├── Inter-SemiBold.woff2 │ │ │ ├── Inter-SemiBoldItalic.woff │ │ │ ├── Inter-SemiBoldItalic.woff2 │ │ │ ├── Inter-Thin.woff │ │ │ ├── Inter-Thin.woff2 │ │ │ ├── Inter-ThinItalic.woff │ │ │ ├── Inter-ThinItalic.woff2 │ │ │ ├── Inter-italic.var.woff2 │ │ │ ├── Inter-roman.var.woff2 │ │ │ ├── Inter.var.woff2 │ │ │ └── inter.css │ │ ├── MariaDBIcon.png │ │ ├── PostgreSQLIcon.png │ │ ├── SampleDataIcon.png │ │ ├── SheetIcon.png │ │ ├── add-data-source-new.mp4 │ │ ├── add-data-source.mp4 │ │ ├── build-first-query-new.mp4 │ │ ├── build-first-query.mp4 │ │ ├── create-first-dashboard-new.mp4 │ │ ├── create-first-dashboard.mp4 │ │ ├── frappe-framework-logo.svg │ │ ├── insights-icon.svg │ │ ├── insights-logo-new.svg │ │ └── insights-logo.svg │ ├── components │ │ ├── AppShell.vue │ │ ├── BasePage.vue │ │ ├── Breadcrumbs.vue │ │ ├── Charts │ │ │ ├── BaseChart.vue │ │ │ └── ChartTitle.vue │ │ ├── CommandPalette.vue │ │ ├── ContentEditable.vue │ │ ├── Controls │ │ │ ├── Attachment.vue │ │ │ ├── Autocomplete.vue │ │ │ ├── Checkbox.vue │ │ │ ├── Code.vue │ │ │ ├── ColorInput.vue │ │ │ ├── ColorPalette.vue │ │ │ ├── ColorPicker.vue │ │ │ ├── DatePicker.vue │ │ │ ├── DatePickerFlat.vue │ │ │ ├── DateRangePicker.vue │ │ │ ├── DateRangePickerFlat.vue │ │ │ ├── InputWithTabs.vue │ │ │ ├── LinkIcon.vue │ │ │ ├── ListPicker.vue │ │ │ ├── TimespanPicker.vue │ │ │ └── TimespanPickerFlat.vue │ │ ├── DraggableList.vue │ │ ├── DraggableListItemMenu.vue │ │ ├── ExpressionHelp.vue │ │ ├── Grid.vue │ │ ├── HelpDialog.vue │ │ ├── Icons │ │ │ ├── ComboChartIcon.vue │ │ │ ├── DragHandleIcon.vue │ │ │ ├── FrappeLogo.vue │ │ │ ├── HomeIcon.vue │ │ │ ├── IndicatorIcon.vue │ │ │ ├── JoinFullIcon.vue │ │ │ ├── JoinInnerIcon.vue │ │ │ ├── JoinLeftIcon.vue │ │ │ └── JoinRightIcon.vue │ │ ├── ListFilter │ │ │ ├── FilterIcon.vue │ │ │ ├── ListFilter.vue │ │ │ ├── NestedPopover.vue │ │ │ └── SearchComplete.vue │ │ ├── LoginBox.vue │ │ ├── NewDialogWithTypes.vue │ │ ├── PageBreadcrumbs.vue │ │ ├── PageTitle.vue │ │ ├── Popover.vue │ │ ├── PublicShareDialog.vue │ │ ├── ResizeableInput.vue │ │ ├── Setting.vue │ │ ├── ShareDialog.vue │ │ ├── Sidebar.vue │ │ ├── SuspenseFallback.jsx │ │ ├── Table │ │ │ ├── TableColumnFilter.vue │ │ │ ├── TableEmpty.vue │ │ │ ├── TableGroupedCell.vue │ │ │ ├── TableLinkCell.vue │ │ │ ├── TableNumberCell.vue │ │ │ ├── TanstackTable.vue │ │ │ └── utils.js │ │ ├── Tabs.vue │ │ ├── Toast.vue │ │ ├── Topbar.vue │ │ ├── UsePopover.vue │ │ └── UseTooltip.vue │ ├── dashboard │ │ ├── Dashboard.vue │ │ ├── DashboardEmptyState.vue │ │ ├── DashboardItem.vue │ │ ├── DashboardItemActions.vue │ │ ├── DashboardList.vue │ │ ├── DashboardListCard.vue │ │ ├── DashboardListGroup.vue │ │ ├── DashboardMenuButton.vue │ │ ├── DashboardNavbarButtons.vue │ │ ├── DashboardQueryDialog.vue │ │ ├── DashboardQueryEditor.vue │ │ ├── DashboardQueryOption.vue │ │ ├── DashboardShareButton.vue │ │ ├── DashboardWidgetsOptions.vue │ │ ├── PublicDashboard.vue │ │ ├── PublicDashboardItem.vue │ │ ├── SimpleFilter.vue │ │ ├── UseDropZone.vue │ │ ├── VueGridLayout.vue │ │ ├── useDashboard.js │ │ ├── useDashboards.js │ │ └── usePublicDashboard.js │ ├── datasource │ │ ├── ConnectMariaDBDialog.vue │ │ ├── ConnectPostgreDBDialog.vue │ │ ├── DataSource.vue │ │ ├── DataSourceList.vue │ │ ├── DataSourceRelationships.vue │ │ ├── DataSourceTable.vue │ │ ├── DataSourceTableColumnHeader.vue │ │ ├── FileSourceForm.vue │ │ ├── MariaDBForm.vue │ │ ├── PostgreSQLForm.vue │ │ ├── SampleDatasetList.vue │ │ ├── TableEdge.vue │ │ ├── TableNode.vue │ │ ├── TableRelationshipEditor.vue │ │ ├── UploadCSVFileDialog.vue │ │ ├── useDataSource.ts │ │ └── useDataSourceTable.ts │ ├── global.d.ts │ ├── globals.js │ ├── home │ │ ├── Home.vue │ │ ├── HomeOnboarding.vue │ │ ├── HomePinnedItems.vue │ │ ├── HomeQuickActions.vue │ │ ├── HomeRecentRecords.vue │ │ └── ProgressRing.vue │ ├── index.css │ ├── layouts │ │ └── BaseLayout.vue │ ├── main.js │ ├── notebook │ │ ├── Notebook.vue │ │ ├── NotebookList.vue │ │ ├── NotebookPage.vue │ │ ├── NotebookPageDropdown.vue │ │ ├── blocks │ │ │ ├── BlockAction.vue │ │ │ ├── BlockActions.vue │ │ │ ├── chart │ │ │ │ ├── ChartBlock.vue │ │ │ │ ├── ChartOptions.vue │ │ │ │ └── ChartOptionsDropdown.vue │ │ │ └── query │ │ │ │ ├── QueryBlock.vue │ │ │ │ └── QueryBlockHeader.vue │ │ ├── tiptap │ │ │ ├── TipTap.vue │ │ │ ├── extensions │ │ │ │ ├── Chart.js │ │ │ │ ├── Chart.vue │ │ │ │ ├── Query.vue │ │ │ │ ├── QueryEditor.js │ │ │ │ └── utils.js │ │ │ └── slash-command │ │ │ │ ├── CommandsList.vue │ │ │ │ ├── commands.js │ │ │ │ └── suggestion.js │ │ ├── useNotebook.js │ │ ├── useNotebookPage.js │ │ └── useNotebooks.js │ ├── pages │ │ ├── AddTeamDialog.vue │ │ ├── AddUserDialog.vue │ │ ├── Avatars.vue │ │ ├── Form.vue │ │ ├── Login.vue │ │ ├── ManageTeamDialog.vue │ │ ├── ManageTeamMembers.vue │ │ ├── ManageTeamResourceAccess.vue │ │ ├── ManageTeamSidebar.vue │ │ ├── NoPermission.vue │ │ ├── NotFound.vue │ │ ├── Settings.vue │ │ ├── Teams.vue │ │ ├── TrialExpired.vue │ │ └── Users.vue │ ├── query │ │ ├── ChartActionButtons.vue │ │ ├── ChartOptions.vue │ │ ├── ChartSection.vue │ │ ├── ChartSectionEmptySvg.vue │ │ ├── ChartTypeSelector.vue │ │ ├── ExpressionHelpDialog.vue │ │ ├── NativeQueryBuilder.vue │ │ ├── NativeQueryEditor.vue │ │ ├── PublicChart.vue │ │ ├── Query.vue │ │ ├── QueryDataSourceSelector.vue │ │ ├── QueryHeader.vue │ │ ├── QueryHeaderTitle.vue │ │ ├── QueryList.vue │ │ ├── QueryMenu.vue │ │ ├── ResultSection.vue │ │ ├── SchemaExplorerDialog.vue │ │ ├── ScriptQueryEditor.vue │ │ ├── VariablesDialog.vue │ │ ├── deprecated │ │ │ ├── ClassicQueryBuilder.vue │ │ │ ├── Column │ │ │ │ ├── ColumnEditor.vue │ │ │ │ ├── ColumnExpressionPicker.vue │ │ │ │ ├── ColumnList.vue │ │ │ │ ├── ColumnPanel.vue │ │ │ │ ├── ColumnPicker.vue │ │ │ │ └── SimpleColumnPicker.vue │ │ │ ├── Filter │ │ │ │ ├── BinaryExpression.vue │ │ │ │ ├── CallExpression.vue │ │ │ │ ├── Expression.vue │ │ │ │ ├── ExpressionTerm.vue │ │ │ │ ├── FilterExpressionPicker.vue │ │ │ │ ├── FilterPanel.vue │ │ │ │ ├── FilterPicker.vue │ │ │ │ ├── LogicalExpression.vue │ │ │ │ └── SimpleFilterPicker.vue │ │ │ ├── LimitsAndOrder.vue │ │ │ └── Table │ │ │ │ ├── TableJoiner.vue │ │ │ │ └── TablePanel.vue │ │ ├── resources │ │ │ ├── useQuery.js │ │ │ ├── useQueryChart.js │ │ │ └── useQueryResults.js │ │ ├── useChart.js │ │ ├── usePublicChart.js │ │ ├── useQueryResource.js │ │ └── visual │ │ │ ├── ColumnExpressionEditor.vue │ │ │ ├── ColumnListItem.vue │ │ │ ├── ColumnSection.vue │ │ │ ├── CumulativeSumTransformFields.vue │ │ │ ├── ExpressionBuilder.vue │ │ │ ├── FilterEditor.vue │ │ │ ├── FilterListItem.vue │ │ │ ├── FilterSection.vue │ │ │ ├── FilterValueSelector.vue │ │ │ ├── LimitSection.vue │ │ │ ├── PivotTransformFields.vue │ │ │ ├── ResultColumnActions.vue │ │ │ ├── ResultFooter.vue │ │ │ ├── SectionHeader.vue │ │ │ ├── SimpleColumnEditor.vue │ │ │ ├── SourceSection.vue │ │ │ ├── TableJoinEditor.vue │ │ │ ├── TableSection.vue │ │ │ ├── TransformEditor.vue │ │ │ ├── TransformListItem.vue │ │ │ ├── TransformSection.vue │ │ │ ├── VisualQueryBuilder.vue │ │ │ ├── constants.js │ │ │ ├── messages.js │ │ │ ├── useAssistedQuery.js │ │ │ └── utils.js │ ├── router.ts │ ├── setup │ │ ├── Setup.vue │ │ ├── SetupQuestions.vue │ │ ├── SourceConnectionStep.vue │ │ └── SourceTypeStep.vue │ ├── socket.js │ ├── stores │ │ ├── cacheStore.ts │ │ ├── dataSourceStore.ts │ │ ├── queryStore.ts │ │ ├── sessionStore.ts │ │ ├── settingsStore.ts │ │ └── setupStore.ts │ ├── subscription │ │ └── index.js │ ├── utils │ │ ├── colors.ts │ │ ├── commandPalette.js │ │ ├── dayjs.js │ │ ├── expressions │ │ │ ├── filter.js │ │ │ ├── index.js │ │ │ └── tokenize.js │ │ ├── format.js │ │ ├── index.js │ │ ├── prompt.js │ │ ├── query │ │ │ ├── columns.js │ │ │ ├── filters.js │ │ │ ├── index.js │ │ │ ├── results.js │ │ │ └── tables.js │ │ ├── resizer.js │ │ ├── systemSettings.js │ │ ├── telemetry.js │ │ ├── toasts.js │ │ ├── transitions.js │ │ ├── useTeams.js │ │ └── useUsers.js │ ├── vite-end.d.ts │ └── widgets │ │ ├── AxisChart │ │ ├── AxisChartOptions.vue │ │ └── getAxisChartOptions.js │ │ ├── Bar │ │ ├── Bar.vue │ │ ├── BarOptions.vue │ │ └── getBarChartOptions.js │ │ ├── Filter │ │ ├── Filter.vue │ │ └── FilterOptions.vue │ │ ├── Funnel │ │ ├── Funnel.vue │ │ ├── FunnelOptions.vue │ │ └── getFunnelChartOptions.js │ │ ├── InvalidWidget.vue │ │ ├── Line │ │ ├── Line.vue │ │ ├── LineOptions.vue │ │ └── getLineChartOptions.js │ │ ├── MixedAxis │ │ ├── MixedAxis.vue │ │ ├── MixedAxisOptions.vue │ │ └── getMixedAxisChartOptions.js │ │ ├── Number │ │ ├── Number.vue │ │ └── NumberOptions.vue │ │ ├── Pie │ │ ├── Pie.vue │ │ ├── PieOptions.vue │ │ └── getPieChartOptions.js │ │ ├── PivotTable │ │ ├── PivotTable.vue │ │ ├── PivotTableOptions.vue │ │ └── utils.js │ │ ├── Progress │ │ ├── Progress.vue │ │ └── ProgressOptions.vue │ │ ├── Row │ │ ├── Row.vue │ │ ├── RowOptions.vue │ │ └── getRowChartOptions.js │ │ ├── Scatter │ │ ├── Scatter.vue │ │ ├── ScatterOptions.vue │ │ └── getScatterChartOptions.js │ │ ├── SeriesOption.vue │ │ ├── Table │ │ ├── Table.vue │ │ ├── TableColumnOptions.vue │ │ └── TableOptions.vue │ │ ├── Text │ │ ├── Text.vue │ │ └── TextOptions.vue │ │ ├── Trend │ │ ├── Trend.vue │ │ └── TrendOptions.vue │ │ ├── useChartData.js │ │ ├── widgetDimensions.json │ │ └── widgets.ts ├── src2 │ ├── App.vue │ ├── assets │ │ ├── duckdb-logo.webp │ │ ├── insights-logo-new-full.svg │ │ └── insights-logo-new.svg │ ├── auth │ │ ├── Login.vue │ │ ├── LoginBox.vue │ │ └── NotFound.vue │ ├── charts │ │ ├── ChartBuilder.vue │ │ ├── SharedChart.vue │ │ ├── chart.ts │ │ ├── colors.ts │ │ ├── components │ │ │ ├── BarChartConfigForm.vue │ │ │ ├── BaseChart.vue │ │ │ ├── ChartBuilderTable.vue │ │ │ ├── ChartConfigForm.vue │ │ │ ├── ChartFilterConfig.vue │ │ │ ├── ChartIcon.vue │ │ │ ├── ChartQuerySelector.vue │ │ │ ├── ChartRenderer.vue │ │ │ ├── ChartShareDialog.vue │ │ │ ├── ChartSortConfig.vue │ │ │ ├── ChartTitle.vue │ │ │ ├── ChartTypeSelector.vue │ │ │ ├── CollapsibleSection.vue │ │ │ ├── DimensionPicker.vue │ │ │ ├── DonutChartConfigForm.vue │ │ │ ├── DrillDown.vue │ │ │ ├── FunnelChartConfigForm.vue │ │ │ ├── LineChartConfigForm.vue │ │ │ ├── MeasurePicker.vue │ │ │ ├── NewMeasureSelectorDialog.vue │ │ │ ├── NumberChart.vue │ │ │ ├── NumberChartConfigForm.vue │ │ │ ├── Sparkline.vue │ │ │ ├── SplitByConfig.vue │ │ │ ├── TableChart.vue │ │ │ ├── TableChartConfigForm.vue │ │ │ ├── XAxisConfig.vue │ │ │ └── YAxisConfig.vue │ │ └── helpers.ts │ ├── components │ │ ├── AppSidebar.vue │ │ ├── Autocomplete.vue │ │ ├── Checkbox.vue │ │ ├── Code.vue │ │ ├── ConfirmDialog.vue │ │ ├── ContentEditable.vue │ │ ├── DataTable.vue │ │ ├── DataTableColumn.vue │ │ ├── DraggableList.vue │ │ ├── Form.vue │ │ ├── FormControl.vue │ │ ├── Icons │ │ │ ├── CSVIcon.vue │ │ │ ├── CollapseSidebar.vue │ │ │ ├── DuckDBIcon.vue │ │ │ ├── FrappeCloudIcon.vue │ │ │ ├── FrappeLogo.vue │ │ │ ├── IndicatorIcon.vue │ │ │ ├── JoinFullIcon.vue │ │ │ ├── JoinInnerIcon.vue │ │ │ ├── JoinLeftIcon.vue │ │ │ ├── JoinRightIcon.vue │ │ │ ├── MariaDBIcon.vue │ │ │ ├── PostgreSQLIcon.vue │ │ │ └── SQLiteIcon.vue │ │ ├── InlineFormControlLabel.vue │ │ ├── LoadingOverlay.vue │ │ ├── Popover.vue │ │ ├── SelectTypeDialog.vue │ │ ├── SidebarLink.vue │ │ ├── Switch.vue │ │ ├── TabbedSidebarLayout.vue │ │ ├── Toast.vue │ │ ├── UserDropdown.vue │ │ └── UserSelector.vue │ ├── dashboard │ │ ├── Dashboard.vue │ │ ├── DashboardBuilder.vue │ │ ├── DashboardChart.vue │ │ ├── DashboardChartSelectorDialog.vue │ │ ├── DashboardFilter.vue │ │ ├── DashboardFilterEditor.vue │ │ ├── DashboardItem.vue │ │ ├── DashboardItemActions.vue │ │ ├── DashboardList.vue │ │ ├── DashboardShareDialog.vue │ │ ├── DashboardText.vue │ │ ├── Filter.vue │ │ ├── SharedDashboard.vue │ │ ├── VueGridLayout.vue │ │ ├── dashboard.ts │ │ └── dashboards.ts │ ├── data_source │ │ ├── ConnectDuckDBDialog.vue │ │ ├── ConnectMariaDBDialog.vue │ │ ├── ConnectPostgreSQLDialog.vue │ │ ├── DataSourceList.vue │ │ ├── DataSourceTable.vue │ │ ├── DataSourceTableList.vue │ │ ├── UploadCSVFileDialog.vue │ │ ├── data_source.ts │ │ ├── data_source.types.ts │ │ └── tables.ts │ ├── data_store │ │ ├── DataStoreList.vue │ │ ├── ImportTableDialog.vue │ │ └── data_store.ts │ ├── globals.ts │ ├── helpers │ │ ├── confirm_dialog.ts │ │ ├── constants.ts │ │ ├── dayjs.ts │ │ ├── index.ts │ │ ├── resource.ts │ │ ├── store_locally.ts │ │ └── toasts.ts │ ├── home │ │ ├── Home.vue │ │ ├── HomeQuickActions.vue │ │ └── HomeWorkbookList.vue │ ├── index.css │ ├── main.ts │ ├── query │ │ ├── Query.vue │ │ ├── alert.ts │ │ ├── components │ │ │ ├── AddOperationPopover.vue │ │ │ ├── AlertSetupDialog.vue │ │ │ ├── ColumnFilter.vue │ │ │ ├── ColumnFilterBody.vue │ │ │ ├── ColumnFilterTypeDate.vue │ │ │ ├── ColumnFilterTypeNumber.vue │ │ │ ├── ColumnFilterTypeText.vue │ │ │ ├── ColumnFilterValueSelector.vue │ │ │ ├── ColumnRemove.vue │ │ │ ├── ColumnRename.vue │ │ │ ├── ColumnSort.vue │ │ │ ├── ColumnTypeChange.vue │ │ │ ├── ColumnsSelector.vue │ │ │ ├── ColumnsSelectorDialog.vue │ │ │ ├── CustomScriptDialog.vue │ │ │ ├── DataTypeIcon.vue │ │ │ ├── DatePicker.vue │ │ │ ├── DatePickerControl.vue │ │ │ ├── ExpressionEditor.vue │ │ │ ├── FilterRule.vue │ │ │ ├── FiltersSelector.vue │ │ │ ├── FiltersSelectorDialog.vue │ │ │ ├── InlineExpression.vue │ │ │ ├── JoinSelectorDialog.vue │ │ │ ├── NativeQueryEditor.vue │ │ │ ├── NewColumnSelectorDialog.vue │ │ │ ├── NumberFilterPicker.vue │ │ │ ├── QueryAlertsDialog.vue │ │ │ ├── QueryBuilder.vue │ │ │ ├── QueryBuilderSourceSelector.vue │ │ │ ├── QueryBuilderTable.vue │ │ │ ├── QueryBuilderToolbar.vue │ │ │ ├── QueryDataTable.vue │ │ │ ├── QueryInfo.vue │ │ │ ├── QueryOperations.vue │ │ │ ├── RelativeDatePicker.vue │ │ │ ├── RelativeDatePickerControl.vue │ │ │ ├── ScriptQueryEditor.vue │ │ │ ├── SummarySelectorDialog.vue │ │ │ ├── UnionSelectorDialog.vue │ │ │ ├── ViewSQLDialog.vue │ │ │ ├── filter_utils.ts │ │ │ ├── join_utils.ts │ │ │ └── source_selector │ │ │ │ ├── DataSourceSelector.vue │ │ │ │ ├── DataSourceTableList.vue │ │ │ │ ├── SourceSelectorDialog.vue │ │ │ │ └── WorkbookQueryList.vue │ │ ├── helpers.ts │ │ └── query.ts │ ├── router.ts │ ├── session.ts │ ├── settings │ │ ├── DataStoreSettings.vue │ │ ├── GeneralSettings.vue │ │ ├── PermissionsSettings.vue │ │ ├── ProfileSettings.vue │ │ ├── SettingItem.vue │ │ ├── Settings.vue │ │ ├── UsersSettings.vue │ │ └── settings.ts │ ├── socket.ts │ ├── styles │ │ └── codemirror.css │ ├── teams │ │ ├── CreateTeamDialog.vue │ │ ├── ManageTeamDialog.vue │ │ ├── TeamList.vue │ │ ├── TeamResourceSelector.vue │ │ └── teams.ts │ ├── telemetry.ts │ ├── types │ │ ├── chart.types.ts │ │ ├── query.types.ts │ │ └── workbook.types.ts │ ├── users │ │ ├── UserList.vue │ │ └── users.ts │ └── workbook │ │ ├── AvatarGroup.vue │ │ ├── Workbook.vue │ │ ├── WorkbookChart.vue │ │ ├── WorkbookDashboard.vue │ │ ├── WorkbookList.vue │ │ ├── WorkbookNavbar.vue │ │ ├── WorkbookNavbarActions.vue │ │ ├── WorkbookQuery.vue │ │ ├── WorkbookQueryEmptyState.vue │ │ ├── WorkbookShareDialog.vue │ │ ├── WorkbookSidebar.vue │ │ ├── WorkbookSidebarListSection.vue │ │ ├── WorkbookTabSwitcher.vue │ │ ├── workbook.ts │ │ └── workbooks.ts ├── tailwind.config.js ├── tests │ ├── dashboard_page.spec.js │ └── query_builder.spec.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.js ├── insights ├── __init__.py ├── api │ ├── __init__.py │ ├── alerts.py │ ├── dashboards.py │ ├── data_sources.py │ ├── data_store.py │ ├── home.py │ ├── notebooks.py │ ├── permissions.py │ ├── public.py │ ├── queries.py │ ├── setup.py │ ├── shared.py │ ├── subscription.py │ ├── telemetry.py │ ├── user.py │ └── workbooks.py ├── cache_utils.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── coverage.py ├── decorators.py ├── fixtures │ ├── insights_data_source.json │ ├── insights_data_source_v3.json │ ├── insights_notebook.json │ └── role.json ├── hooks.py ├── insights │ ├── __init__.py │ ├── doctype │ │ ├── __init__.py │ │ ├── insights_alert │ │ │ ├── __init__.py │ │ │ ├── insights_alert.js │ │ │ ├── insights_alert.json │ │ │ ├── insights_alert.py │ │ │ └── test_insights_alert.py │ │ ├── insights_chart │ │ │ ├── __init__.py │ │ │ ├── insights_chart.js │ │ │ ├── insights_chart.json │ │ │ ├── insights_chart.py │ │ │ ├── patches │ │ │ │ ├── __init__.py │ │ │ │ └── convert_bar_to_row_chart.py │ │ │ └── test_insights_chart.py │ │ ├── insights_chart_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_chart_v3.js │ │ │ ├── insights_chart_v3.json │ │ │ ├── insights_chart_v3.py │ │ │ └── test_insights_chart_v3.py │ │ ├── insights_dashboard │ │ │ ├── __init__.py │ │ │ ├── insights_dashboard.js │ │ │ ├── insights_dashboard.json │ │ │ ├── insights_dashboard.py │ │ │ ├── test_insights_dashboard.py │ │ │ └── utils.py │ │ ├── insights_dashboard_chart_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_dashboard_chart_v3.json │ │ │ └── insights_dashboard_chart_v3.py │ │ ├── insights_dashboard_item │ │ │ ├── __init__.py │ │ │ ├── insights_dashboard_item.json │ │ │ └── insights_dashboard_item.py │ │ ├── insights_dashboard_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_dashboard_v3.js │ │ │ ├── insights_dashboard_v3.json │ │ │ ├── insights_dashboard_v3.py │ │ │ └── test_insights_dashboard_v3.py │ │ ├── insights_data_source │ │ │ ├── __init__.py │ │ │ ├── insights_data_source.js │ │ │ ├── insights_data_source.json │ │ │ ├── insights_data_source.py │ │ │ ├── sources │ │ │ │ ├── base_database.py │ │ │ │ ├── frappe_db.py │ │ │ │ ├── mariadb.py │ │ │ │ ├── postgresql.py │ │ │ │ ├── query_store.py │ │ │ │ ├── sqlite.py │ │ │ │ └── utils.py │ │ │ └── test_insights_data_source.py │ │ ├── insights_data_source_v3 │ │ │ ├── __init__.py │ │ │ ├── connectors │ │ │ │ ├── bigquery.py │ │ │ │ ├── duckdb.py │ │ │ │ ├── frappe_db.py │ │ │ │ ├── mariadb.py │ │ │ │ ├── mssql.py │ │ │ │ ├── postgresql.py │ │ │ │ └── sqlite.py │ │ │ ├── data_warehouse.py │ │ │ ├── ibis │ │ │ │ ├── __init__.py │ │ │ │ ├── functions.py │ │ │ │ └── utils.py │ │ │ ├── ibis_utils.py │ │ │ ├── insights_data_source_v3.js │ │ │ ├── insights_data_source_v3.json │ │ │ ├── insights_data_source_v3.py │ │ │ ├── patches │ │ │ │ ├── __init__.py │ │ │ │ └── copy_data_sources.py │ │ │ └── test_insights_data_source_v3.py │ │ ├── insights_notebook │ │ │ ├── __init__.py │ │ │ ├── insights_notebook.js │ │ │ ├── insights_notebook.json │ │ │ ├── insights_notebook.py │ │ │ └── test_insights_notebook.py │ │ ├── insights_notebook_page │ │ │ ├── __init__.py │ │ │ ├── insights_notebook_page.js │ │ │ ├── insights_notebook_page.json │ │ │ ├── insights_notebook_page.py │ │ │ ├── patches │ │ │ │ ├── __init__.py │ │ │ │ └── replace_query_builder_with_editor.py │ │ │ └── test_insights_notebook_page.py │ │ ├── insights_query │ │ │ ├── __init__.py │ │ │ ├── insights_assisted_query.py │ │ │ ├── insights_legacy_query.py │ │ │ ├── insights_legacy_query_utils.py │ │ │ ├── insights_query.js │ │ │ ├── insights_query.json │ │ │ ├── insights_query.py │ │ │ ├── insights_query_client.py │ │ │ ├── insights_raw_query.py │ │ │ ├── insights_script_query.py │ │ │ ├── patches │ │ │ │ ├── __init__.py │ │ │ │ ├── flatten_columns_in_query_json.py │ │ │ │ ├── make_query_variable_value_password_field.py │ │ │ │ ├── migrate_old_query_to_new_query_structure.py │ │ │ │ ├── rename_untitled_query_to_query_name.py │ │ │ │ └── set_chart_name.py │ │ │ ├── test_insights_query.py │ │ │ └── utils.py │ │ ├── insights_query_chart │ │ │ ├── __init__.py │ │ │ ├── insights_query_chart.js │ │ │ ├── insights_query_chart.json │ │ │ ├── insights_query_chart.py │ │ │ └── test_insights_query_chart.py │ │ ├── insights_query_column │ │ │ ├── __init__.py │ │ │ ├── insights_query_column.json │ │ │ └── insights_query_column.py │ │ ├── insights_query_execution_log │ │ │ ├── __init__.py │ │ │ ├── insights_query_execution_log.js │ │ │ ├── insights_query_execution_log.json │ │ │ ├── insights_query_execution_log.py │ │ │ └── test_insights_query_execution_log.py │ │ ├── insights_query_result │ │ │ ├── __init__.py │ │ │ ├── insights_query_result.js │ │ │ ├── insights_query_result.json │ │ │ ├── insights_query_result.py │ │ │ └── test_insights_query_result.py │ │ ├── insights_query_table │ │ │ ├── __init__.py │ │ │ ├── insights_query_table.json │ │ │ └── insights_query_table.py │ │ ├── insights_query_transform │ │ │ ├── __init__.py │ │ │ ├── insights_query_transform.js │ │ │ ├── insights_query_transform.json │ │ │ ├── insights_query_transform.py │ │ │ └── test_insights_query_transform.py │ │ ├── insights_query_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_query_v3.js │ │ │ ├── insights_query_v3.json │ │ │ ├── insights_query_v3.py │ │ │ └── test_insights_query_v3.py │ │ ├── insights_query_variable │ │ │ ├── __init__.py │ │ │ ├── insights_query_variable.json │ │ │ └── insights_query_variable.py │ │ ├── insights_resource_permission │ │ │ ├── __init__.py │ │ │ ├── insights_resource_permission.json │ │ │ └── insights_resource_permission.py │ │ ├── insights_settings │ │ │ ├── __init__.py │ │ │ ├── insights_settings.js │ │ │ ├── insights_settings.json │ │ │ ├── insights_settings.py │ │ │ └── test_insights_settings.py │ │ ├── insights_table │ │ │ ├── __init__.py │ │ │ ├── insights_table.js │ │ │ ├── insights_table.json │ │ │ ├── insights_table.py │ │ │ ├── patches │ │ │ │ ├── __init__.py │ │ │ │ ├── delete_duplicate_records.py │ │ │ │ ├── delete_unused_query_based_tables.py │ │ │ │ └── sync_table_links.py │ │ │ ├── test_insights_table.py │ │ │ └── test_records.json │ │ ├── insights_table_column │ │ │ ├── __init__.py │ │ │ ├── insights_table_column.js │ │ │ ├── insights_table_column.json │ │ │ ├── insights_table_column.py │ │ │ └── test_insights_table_column.py │ │ ├── insights_table_import │ │ │ ├── __init__.py │ │ │ ├── insights_table_import.js │ │ │ ├── insights_table_import.json │ │ │ ├── insights_table_import.py │ │ │ └── test_insights_table_import.py │ │ ├── insights_table_import_log │ │ │ ├── __init__.py │ │ │ ├── insights_table_import_log.js │ │ │ ├── insights_table_import_log.json │ │ │ ├── insights_table_import_log.py │ │ │ └── test_insights_table_import_log.py │ │ ├── insights_table_link │ │ │ ├── __init__.py │ │ │ ├── insights_table_link.js │ │ │ ├── insights_table_link.json │ │ │ ├── insights_table_link.py │ │ │ └── test_insights_table_link.py │ │ ├── insights_table_link_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_table_link_v3.js │ │ │ ├── insights_table_link_v3.json │ │ │ ├── insights_table_link_v3.py │ │ │ └── test_insights_table_link_v3.py │ │ ├── insights_table_v3 │ │ │ ├── __init__.py │ │ │ ├── insights_table_v3.js │ │ │ ├── insights_table_v3.json │ │ │ ├── insights_table_v3.py │ │ │ ├── patches │ │ │ │ └── force_sync_tables.py │ │ │ └── test_insights_table_v3.py │ │ ├── insights_team │ │ │ ├── __init__.py │ │ │ ├── insights_team.js │ │ │ ├── insights_team.json │ │ │ ├── insights_team.py │ │ │ ├── insights_team_client.py │ │ │ └── test_insights_team.py │ │ ├── insights_team_member │ │ │ ├── __init__.py │ │ │ ├── insights_team_member.json │ │ │ └── insights_team_member.py │ │ ├── insights_user_invitation │ │ │ ├── __init__.py │ │ │ ├── insights_user_invitation.js │ │ │ ├── insights_user_invitation.json │ │ │ ├── insights_user_invitation.py │ │ │ └── test_insights_user_invitation.py │ │ └── insights_workbook │ │ │ ├── __init__.py │ │ │ ├── insights_workbook.js │ │ │ ├── insights_workbook.json │ │ │ ├── insights_workbook.py │ │ │ └── test_insights_workbook.py │ ├── page │ │ ├── __init__.py │ │ └── insights │ │ │ ├── __init__.py │ │ │ ├── insights.js │ │ │ └── insights.json │ └── query_builders │ │ ├── __init__.py │ │ ├── legacy_query_builder.py │ │ ├── postgresql │ │ └── builder.py │ │ ├── sql_builder.py │ │ ├── sql_functions.py │ │ ├── sqlite │ │ └── sqlite_query_builder.py │ │ ├── test_sql_builder.py │ │ └── utils.py ├── migrate.py ├── modules.txt ├── patches.txt ├── patches │ ├── __init__.py │ ├── add_column_row_to_result.py │ ├── add_last_execution_field.py │ ├── add_position_key_to_filter.py │ ├── add_roles.py │ ├── convert_duration_to_float.py │ ├── create_query_tables.py │ ├── enable_data_store.py │ ├── fix_select_options_after_rename.py │ ├── make_filter_links.py │ ├── make_query_tables.py │ ├── migrate_dashboard_charts.py │ ├── modify_dashboard_layout.py │ ├── modify_join_condition.py │ ├── normalize_workbook.py │ ├── refactor_dashboard_filter.py │ ├── refactor_dashboard_item.py │ ├── refresh_tables.py │ ├── rename_column_type.py │ ├── rename_count_column_name.py │ ├── rename_data_to_config.py │ ├── rename_doctypes.py │ ├── rename_like_to_contains.py │ ├── rename_target_column_field.py │ ├── rename_visualization.py │ ├── replace_demo_data_source.py │ ├── replace_pivot_column_with_label.py │ ├── reset_query_filters.py │ ├── show_support_login_message.py │ └── store_queries.py ├── permissions.py ├── public │ ├── .gitkeep │ └── js │ │ └── setup_wizard.js ├── setup │ ├── __init__.py │ ├── demo.py │ ├── insights_demo_data.duckdb │ ├── sample_workbook.json │ ├── setup_wizard.py │ └── test_demo_setup.py ├── templates │ ├── __init__.py │ ├── alert.html │ ├── emails │ │ └── insights_invitation.html │ └── pages │ │ └── __init__.py ├── tests │ ├── __init__.py │ ├── test_permissions.py │ └── utils.py ├── utils.py └── www │ ├── __init__.py │ ├── insights.py │ └── insights_v2.py ├── license.txt ├── package.json ├── pyproject.toml └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Root editor config file 2 | root = true 3 | 4 | # Common settings 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | charset = utf-8 10 | 11 | # python - standard formatting 12 | [{*.py}] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [{*.js,*.vue}] 17 | indent_style = tab 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 9d4e122dc4b0f91c8861e61bc90015b02d793c07 2 | ac8d649974555cdf14c2aac52bc30c2b62b8aac7 3 | 3e7fd6f3b821cde5c792d2fb12fdee14fdcd1b88 4 | e8fd9e6db550f515523a6f1fc2fcf0fa9cfd8293 5 | 3faf40d64b6fa16e0efba028a19c0582e84bbe2c 6 | 1e998cd69199c090d74f4e121e52524db992c738 7 | 23228452df9591a42a2630c805cd9bfaf7224303 8 | 17e5fa7fa31df5470c8f7e8e1d49c71032452b9a 9 | fe1b4c26687d5a11b56fff60c37e9e68ebe663cb 10 | 61c4ae6cafd23e560e4bd83e07606e4f6da252fb -------------------------------------------------------------------------------- /.github/chart-builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/chart-builder.png -------------------------------------------------------------------------------- /.github/helpers/install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Setting Up System Dependencies..." 5 | 6 | sudo apt update 7 | sudo apt install libcups2-dev redis-server mariadb-client-10.6 8 | 9 | install_wkhtmltopdf() { 10 | wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb 11 | sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb 12 | } 13 | install_wkhtmltopdf & 14 | -------------------------------------------------------------------------------- /.github/hero-image-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/hero-image-v2.png -------------------------------------------------------------------------------- /.github/hero-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/hero-image.png -------------------------------------------------------------------------------- /.github/join-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/join-editor.png -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/logo.png -------------------------------------------------------------------------------- /.github/new-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/preview.gif -------------------------------------------------------------------------------- /.github/query-builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/query-builder.png -------------------------------------------------------------------------------- /.github/query-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/query-view.png -------------------------------------------------------------------------------- /.github/result-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/result-view.png -------------------------------------------------------------------------------- /.github/visualize-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/.github/visualize-view.png -------------------------------------------------------------------------------- /.github/workflows/make-release-pr.yml: -------------------------------------------------------------------------------- 1 | # forked (:p) from frappe/frappe 2 | 3 | name: Create fortnightly release 4 | on: 5 | schedule: 6 | # 13:00 UTC -> 7pm IST on every alternate Tuesday 7 | - cron: '0 13 * * 2/2' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | 17 | steps: 18 | - uses: octokit/request-action@v2.x 19 | with: 20 | route: POST /repos/{owner}/{repo}/pulls 21 | owner: frappe 22 | repo: insights 23 | title: |- 24 | "chore: merge 'develop' into 'main'" 25 | body: "Automated fortnightly release" 26 | base: main 27 | head: develop 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Generate Semantic Release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Entire Repository 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | persist-credentials: false 17 | - name: Setup Node.js 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 18 21 | - name: Setup dependencies 22 | run: | 23 | npm install @semantic-release/git @semantic-release/exec --no-save 24 | - name: Create Release 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: npx semantic-release -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frappe-ui"] 2 | path = frappe-ui 3 | url = https://github.com/nextchamp-saqib/frappe-ui.git 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: 'node_modules|.git' 2 | default_stages: [pre-commit] 3 | fail_fast: false 4 | 5 | 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v5.0.0 9 | hooks: 10 | - id: trailing-whitespace 11 | exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" 12 | - id: check-merge-conflict 13 | - id: check-ast 14 | - id: check-json 15 | - id: check-toml 16 | - id: check-yaml 17 | - id: debug-statements 18 | 19 | - repo: https://github.com/astral-sh/ruff-pre-commit 20 | rev: v0.8.1 21 | hooks: 22 | - id: ruff 23 | name: "Run ruff import sorter" 24 | args: ["--select=I", "--fix"] 25 | 26 | - id: ruff 27 | name: "Run ruff linter" 28 | 29 | - id: ruff-format 30 | name: "Run ruff formatter" 31 | 32 | - repo: https://github.com/pre-commit/mirrors-prettier 33 | rev: v3.1.0 34 | hooks: 35 | - id: prettier 36 | types_or: [javascript, vue, scss] 37 | 38 | ci: 39 | autoupdate_schedule: weekly 40 | skip: [] 41 | submodules: false 42 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main" 4 | ], 5 | "plugins": [ 6 | "@semantic-release/commit-analyzer", 7 | { 8 | "preset": "angular", 9 | "releaseRules": [ 10 | { 11 | "breaking": true, 12 | "release": false 13 | } 14 | ] 15 | }, 16 | [ 17 | "@semantic-release/exec", 18 | { 19 | "prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" insights/__init__.py' 20 | } 21 | ], 22 | [ 23 | "@semantic-release/git", 24 | { 25 | "assets": [ 26 | "insights/__init__.py" 27 | ], 28 | "message": "chore(release): bumped to v${nextRelease.version}" 29 | } 30 | ], 31 | "@semantic-release/github" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include insights *.css 8 | recursive-include insights *.csv 9 | recursive-include insights *.html 10 | recursive-include insights *.ico 11 | recursive-include insights *.js 12 | recursive-include insights *.json 13 | recursive-include insights *.md 14 | recursive-include insights *.png 15 | recursive-include insights *.py 16 | recursive-include insights *.svg 17 | recursive-include insights *.txt 18 | recursive-exclude insights *.pyc -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | mariadb: 4 | image: mariadb:10.8 5 | command: 6 | - --character-set-server=utf8mb4 7 | - --collation-server=utf8mb4_unicode_ci 8 | - --skip-character-set-client-handshake 9 | - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 10 | environment: 11 | MYSQL_ROOT_PASSWORD: 123 12 | volumes: 13 | - mariadb-data:/var/lib/mysql 14 | 15 | redis: 16 | image: redis:alpine 17 | 18 | frappe: 19 | image: frappe/bench:latest 20 | command: bash /workspace/init.sh 21 | environment: 22 | - SHELL=/bin/bash 23 | working_dir: /home/frappe 24 | volumes: 25 | - .:/workspace 26 | ports: 27 | - 8000:8000 28 | - 9000:9000 29 | 30 | volumes: 31 | mariadb-data: -------------------------------------------------------------------------------- /flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | B007, 4 | B950, 5 | E101, 6 | E111, 7 | E114, 8 | E116, 9 | E117, 10 | E121, 11 | E122, 12 | E123, 13 | E124, 14 | E125, 15 | E126, 16 | E127, 17 | E128, 18 | E131, 19 | E201, 20 | E202, 21 | E203, 22 | E211, 23 | E221, 24 | E222, 25 | E223, 26 | E224, 27 | E225, 28 | E226, 29 | E228, 30 | E231, 31 | E241, 32 | E242, 33 | E251, 34 | E261, 35 | E262, 36 | E265, 37 | E266, 38 | E271, 39 | E272, 40 | E273, 41 | E274, 42 | E301, 43 | E302, 44 | E303, 45 | E305, 46 | E306, 47 | E401, 48 | E402, 49 | E501, 50 | E502, 51 | E701, 52 | E702, 53 | E703, 54 | E741, 55 | F401, 56 | F403, 57 | W191, 58 | W291, 59 | W292, 60 | W293, 61 | W391, 62 | W503, 63 | W504, 64 | E731, 65 | C420 66 | 67 | max-line-length = 88 68 | -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es2021": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:vue/vue3-recommended", 10 | "plugin:prettier/recommended" 11 | ], 12 | "parser": "vue-eslint-parser", 13 | "parserOptions": { 14 | "parser": "@typescript-eslint/parser" 15 | }, 16 | "plugins": [ 17 | "vue", 18 | "prettier" 19 | ], 20 | "rules": { 21 | "vue/no-reserved-component-names": "off", 22 | "vue/multi-word-component-names": "off", 23 | "no-unused-vars": "off", 24 | } 25 | } -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | /test-results/ 7 | /playwright-report/ 8 | /playwright/.cache/ 9 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "useTabs": true, 5 | "printWidth": 100, 6 | "vueIndentScriptAndStyle": false 7 | } -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Frappe Insights 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/index_v2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Frappe Insights 8 | 9 | 10 |
11 |
12 |
13 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/public/insights-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/public/insights-logo.png -------------------------------------------------------------------------------- /frontend/src/api/whitelistedMethods.ts: -------------------------------------------------------------------------------- 1 | const whitelistedMethods = { 2 | 'Insights Settings': { 3 | update_settings: 'update_settings', 4 | }, 5 | 'Insights Data Source': { 6 | enqueue_sync_tables: 'enqueue_sync_tables', 7 | get_tables: 'get_tables', 8 | get_queries: 'get_queries', 9 | update_table_link: 'update_table_link', 10 | delete_table_link: 'delete_table_link', 11 | }, 12 | 'Insights Table': { 13 | syncTable: 'sync_table', 14 | updateVisibility: 'update_visibility', 15 | getPreview: 'get_preview', 16 | update_column_type: 'update_column_type', 17 | }, 18 | } 19 | export default function getWhitelistedMethods(doctype: string) { 20 | return whitelistedMethods[doctype as keyof typeof whitelistedMethods] || {} 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/assets/ERPNextIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/ERPNextIcon.png -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Bold.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Light.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Light.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Medium.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Medium.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Regular.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-SemiBold.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-SemiBold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-VF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-VF.woff -------------------------------------------------------------------------------- /frontend/src/assets/FiraCode/FiraCode-VF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/FiraCode/FiraCode-VF.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Black.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Black.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-BlackItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-BlackItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-BlackItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-BlackItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Bold.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Bold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-BoldItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-BoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraBold.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraBold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraLight.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraLight.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraLightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Italic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Italic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Light.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Light.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-LightItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-LightItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Medium.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Medium.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-MediumItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-MediumItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Regular.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Regular.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-SemiBold.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-SemiBold.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-SemiBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Thin.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-Thin.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ThinItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ThinItalic.woff -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-ThinItalic.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-italic.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-italic.var.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter-roman.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter-roman.var.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/Inter/Inter.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/Inter/Inter.var.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/MariaDBIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/MariaDBIcon.png -------------------------------------------------------------------------------- /frontend/src/assets/PostgreSQLIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/PostgreSQLIcon.png -------------------------------------------------------------------------------- /frontend/src/assets/SampleDataIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/SampleDataIcon.png -------------------------------------------------------------------------------- /frontend/src/assets/SheetIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/SheetIcon.png -------------------------------------------------------------------------------- /frontend/src/assets/add-data-source-new.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/add-data-source-new.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/add-data-source.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/add-data-source.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/build-first-query-new.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/build-first-query-new.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/build-first-query.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/build-first-query.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/create-first-dashboard-new.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/create-first-dashboard-new.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/create-first-dashboard.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/assets/create-first-dashboard.mp4 -------------------------------------------------------------------------------- /frontend/src/assets/frappe-framework-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/src/assets/insights-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/src/assets/insights-logo-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/src/components/BasePage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /frontend/src/components/Charts/ChartTitle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /frontend/src/components/Controls/LinkIcon.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /frontend/src/components/DraggableListItemMenu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/ExpressionHelp.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 22 | -------------------------------------------------------------------------------- /frontend/src/components/Icons/DragHandleIcon.vue: -------------------------------------------------------------------------------- 1 | 19 | 24 | -------------------------------------------------------------------------------- /frontend/src/components/Icons/IndicatorIcon.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /frontend/src/components/Icons/JoinFullIcon.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/ListFilter/FilterIcon.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/LoginBox.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/PageTitle.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | -------------------------------------------------------------------------------- /frontend/src/components/ResizeableInput.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /frontend/src/components/Setting.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /frontend/src/components/SuspenseFallback.jsx: -------------------------------------------------------------------------------- 1 | import { LoadingIndicator } from 'frappe-ui' 2 | export default { 3 | name: 'SuspenseFallback', 4 | components: { LoadingIndicator }, 5 | render() { 6 | return ( 7 |
8 | 9 |
10 | ) 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/components/Table/TableColumnFilter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /frontend/src/components/Table/TableEmpty.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /frontend/src/components/Table/TableGroupedCell.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/Table/TableLinkCell.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /frontend/src/components/Topbar.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /frontend/src/dashboard/DashboardEmptyState.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 22 | -------------------------------------------------------------------------------- /frontend/src/dashboard/DashboardQueryEditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /frontend/src/dashboard/DashboardShareButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | -------------------------------------------------------------------------------- /frontend/src/dashboard/DashboardWidgetsOptions.vue: -------------------------------------------------------------------------------- 1 | 12 | 33 | -------------------------------------------------------------------------------- /frontend/src/datasource/ConnectMariaDBDialog.vue: -------------------------------------------------------------------------------- 1 | 12 | 19 | -------------------------------------------------------------------------------- /frontend/src/datasource/ConnectPostgreDBDialog.vue: -------------------------------------------------------------------------------- 1 | 12 | 19 | -------------------------------------------------------------------------------- /frontend/src/datasource/UploadCSVFileDialog.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /frontend/src/home/Home.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | -------------------------------------------------------------------------------- /frontend/src/layouts/BaseLayout.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /frontend/src/notebook/blocks/BlockAction.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 32 | -------------------------------------------------------------------------------- /frontend/src/notebook/blocks/BlockActions.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 25 | -------------------------------------------------------------------------------- /frontend/src/notebook/blocks/chart/ChartOptionsDropdown.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/extensions/Chart.js: -------------------------------------------------------------------------------- 1 | import { createNodeExtension } from './utils' 2 | 3 | import Chart from './Chart.vue' 4 | export default createNodeExtension({ 5 | name: 'chart', 6 | tag: 'chart', 7 | component: Chart, 8 | attributes: { 9 | chart_name: undefined, 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/extensions/Chart.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/extensions/Query.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/extensions/QueryEditor.js: -------------------------------------------------------------------------------- 1 | import { createNodeExtension } from './utils' 2 | 3 | import Query from './Query.vue' 4 | export default createNodeExtension({ 5 | name: 'query-editor', 6 | tag: 'query-editor', 7 | component: Query, 8 | attributes: { 9 | query: undefined, 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/extensions/utils.js: -------------------------------------------------------------------------------- 1 | import { mergeAttributes, Node } from '@tiptap/core' 2 | import { VueNodeViewRenderer } from '@tiptap/vue-3' 3 | 4 | export function createNodeExtension(options) { 5 | const { name, component, tag, ...rest } = options 6 | return Node.create({ 7 | name, 8 | group: rest.group || 'block', 9 | atom: rest.atom || true, 10 | addAttributes() { 11 | return rest.attributes || {} 12 | }, 13 | parseHTML() { 14 | return [{ tag }] 15 | }, 16 | renderHTML({ HTMLAttributes }) { 17 | return [tag, mergeAttributes(HTMLAttributes)] 18 | }, 19 | addNodeView() { 20 | return VueNodeViewRenderer(component) 21 | }, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/notebook/tiptap/slash-command/commands.js: -------------------------------------------------------------------------------- 1 | import { Extension } from '@tiptap/core' 2 | import Suggestion from '@tiptap/suggestion' 3 | 4 | export default Extension.create({ 5 | name: 'slash-commands', 6 | 7 | addOptions() { 8 | return { 9 | suggestion: { 10 | char: '/', 11 | command: ({ editor, range, props }) => { 12 | props.command({ editor, range }) 13 | }, 14 | }, 15 | } 16 | }, 17 | 18 | addProseMirrorPlugins() { 19 | return [ 20 | Suggestion({ 21 | editor: this.editor, 22 | ...this.options.suggestion, 23 | }), 24 | ] 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /frontend/src/pages/Avatars.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /frontend/src/pages/NoPermission.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /frontend/src/pages/NotFound.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /frontend/src/pages/TrialExpired.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | -------------------------------------------------------------------------------- /frontend/src/query/PublicChart.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | -------------------------------------------------------------------------------- /frontend/src/query/QueryHeader.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 28 | -------------------------------------------------------------------------------- /frontend/src/query/QueryHeaderTitle.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | -------------------------------------------------------------------------------- /frontend/src/query/deprecated/Column/ColumnEditor.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | -------------------------------------------------------------------------------- /frontend/src/query/deprecated/Filter/BinaryExpression.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /frontend/src/query/deprecated/Filter/CallExpression.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | -------------------------------------------------------------------------------- /frontend/src/query/deprecated/Filter/ExpressionTerm.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | -------------------------------------------------------------------------------- /frontend/src/query/resources/useQueryResults.js: -------------------------------------------------------------------------------- 1 | import { safeJSONParse } from '@/utils' 2 | import { createDocumentResource } from 'frappe-ui' 3 | import { computed, reactive } from 'vue' 4 | import { getFormattedResult } from '@/utils/query/results' 5 | 6 | export default function useQueryResults(result_name) { 7 | const resource = getResultResource(result_name) 8 | resource.get.fetch() 9 | return reactive({ 10 | reload: () => resource.get.fetch(), 11 | loading: computed(() => resource.loading), 12 | data: computed(() => resource.doc?.results || []), 13 | columns: computed(() => resource.doc?.results?.[0] || []), 14 | formattedResults: computed(() => getFormattedResult(resource.doc?.results)), 15 | }) 16 | } 17 | 18 | export function getResultResource(resultName) { 19 | return createDocumentResource({ 20 | doctype: 'Insights Query Result', 21 | name: resultName, 22 | auto: false, 23 | transform: (doc) => { 24 | doc.results = safeJSONParse(doc.results, []) 25 | return doc 26 | }, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/query/visual/CumulativeSumTransformFields.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 33 | -------------------------------------------------------------------------------- /frontend/src/query/visual/LimitSection.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | -------------------------------------------------------------------------------- /frontend/src/query/visual/SectionHeader.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 25 | -------------------------------------------------------------------------------- /frontend/src/query/visual/SourceSection.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /frontend/src/query/visual/constants.js: -------------------------------------------------------------------------------- 1 | export const NEW_COLUMN = { 2 | table: '', 3 | column: '', 4 | label: '', 5 | type: '', 6 | alias: '', 7 | order: '', 8 | granularity: '', 9 | aggregation: '', 10 | format: {}, 11 | expression: {}, 12 | } 13 | 14 | export const NEW_FILTER = { 15 | column: { ...NEW_COLUMN }, 16 | operator: {}, 17 | value: {}, 18 | expression: {}, 19 | } 20 | 21 | export const NEW_JOIN = { 22 | join_type: { label: 'Inner Join', value: 'inner' }, 23 | left_table: {}, 24 | left_column: {}, 25 | right_table: {}, 26 | right_column: {}, 27 | } 28 | 29 | export const NEW_TRANSFORM = { 30 | type: '', 31 | options: {}, 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/query/visual/messages.js: -------------------------------------------------------------------------------- 1 | export const WARN_UNABLE_TO_INFER_JOIN = (table1, table2) => ({ 2 | variant: 'warning', 3 | title: 'Unable to find a relationship', 4 | message: `Please add a relationship between ${table1} and ${table2} manually`, 5 | }) 6 | export const ERROR_CANNOT_ADD_SELF_AS_TABLE = () => ({ 7 | variant: 'error', 8 | title: 'Cannot add self as table', 9 | message: `Please select a different table`, 10 | }) 11 | export const ERROR_UNABLE_TO_RESET_MAIN_TABLE = () => ({ 12 | variant: 'error', 13 | title: 'Unable to reset main table', 14 | message: 'Please remove all columns and joins before resetting the main table', 15 | }) 16 | -------------------------------------------------------------------------------- /frontend/src/socket.js: -------------------------------------------------------------------------------- 1 | import { io } from 'socket.io-client' 2 | import { socketio_port } from '../../../../sites/common_site_config.json' 3 | 4 | export function initSocket() { 5 | let host = window.location.hostname 6 | let siteName = import.meta.env.DEV ? host : window.site_name 7 | let port = window.location.port ? `:${socketio_port}` : '' 8 | let protocol = port ? 'http' : 'https' 9 | let url = `${protocol}://${host}${port}/${siteName}` 10 | 11 | let socket = io(url, { 12 | withCredentials: true, 13 | reconnectionAttempts: 5, 14 | }) 15 | return socket 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/stores/cacheStore.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from '@/datasource/useDataSource' 2 | import { DataSourceTable } from '@/datasource/useDataSourceTable' 3 | import { defineStore } from 'pinia' 4 | import { ref } from 'vue' 5 | 6 | type DataSourceCache = Record 7 | type TableCache = Record 8 | 9 | const useCacheStore = defineStore('insights:cache', () => { 10 | const dataSourceCache = ref({}) 11 | function getDataSource(name: string) { 12 | return dataSourceCache.value[name] 13 | } 14 | function setDataSource(name: string, dataSource: DataSource) { 15 | dataSourceCache.value[name] = dataSource 16 | } 17 | 18 | const tableCache = ref({}) 19 | function getTable(name: string) { 20 | return tableCache.value[name] 21 | } 22 | function setTable(name: string, table: DataSourceTable) { 23 | tableCache.value[name] = table 24 | } 25 | 26 | return { 27 | getDataSource, 28 | setDataSource, 29 | getTable, 30 | setTable, 31 | } 32 | }) 33 | 34 | export default useCacheStore 35 | -------------------------------------------------------------------------------- /frontend/src/stores/setupStore.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src/stores/setupStore.ts -------------------------------------------------------------------------------- /frontend/src/subscription/index.js: -------------------------------------------------------------------------------- 1 | import { createResource, call } from 'frappe-ui' 2 | import { reactive } from 'vue' 3 | 4 | const subscription = reactive({ 5 | trialExpired: null, 6 | fetchTrialStatus, 7 | }) 8 | 9 | async function fetchTrialStatus() { 10 | subscription.trialExpired = await call('insights.api.subscription.trial_expired') 11 | return subscription.trialExpired 12 | } 13 | 14 | export async function getLoginLink() { 15 | return await call('insights.api.subscription.get_login_link') 16 | } 17 | 18 | export async function getTrialStatus() { 19 | if (import.meta.env.DEV) return false 20 | return subscription.trialExpired !== null ? subscription.trialExpired : await fetchTrialStatus() 21 | } 22 | 23 | export default subscription 24 | -------------------------------------------------------------------------------- /frontend/src/utils/dayjs.js: -------------------------------------------------------------------------------- 1 | import customParseFormat from 'dayjs/esm/plugin/customParseFormat' 2 | import quarterOfYear from 'dayjs/esm/plugin/quarterOfYear' 3 | import { dayjs } from 'frappe-ui' 4 | 5 | dayjs.extend(quarterOfYear) 6 | dayjs.extend(customParseFormat) 7 | 8 | export default dayjs 9 | -------------------------------------------------------------------------------- /frontend/src/utils/prompt.js: -------------------------------------------------------------------------------- 1 | import { reactive } from 'vue' 2 | 3 | let prompt = reactive({ 4 | show: false, 5 | options: {}, 6 | }) 7 | 8 | export default function usePrompt() { 9 | return prompt 10 | } 11 | 12 | export function showPrompt(promptOptions) { 13 | prompt.show = true 14 | prompt.options = { 15 | title: promptOptions.title, 16 | message: promptOptions.message, 17 | icon: promptOptions.icon, 18 | actions: [ 19 | { 20 | ...promptOptions.primaryAction, 21 | label: promptOptions.primaryAction.label, 22 | handler: promptOptions.primaryAction.action, 23 | }, 24 | { 25 | ...promptOptions.secondaryAction, 26 | label: promptOptions.secondaryAction?.label || 'Cancel', 27 | handler: promptOptions.secondaryAction?.action, 28 | }, 29 | ], 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/utils/systemSettings.js: -------------------------------------------------------------------------------- 1 | import { createDocumentResource } from 'frappe-ui' 2 | 3 | const resource = createDocumentResource({ 4 | doctype: 'System Settings', 5 | name: 'System Settings', 6 | auto: false, 7 | }) 8 | 9 | export default resource 10 | -------------------------------------------------------------------------------- /frontend/src/utils/toasts.js: -------------------------------------------------------------------------------- 1 | import Toast from '@/components/Toast.vue' 2 | import { h, markRaw } from 'vue' 3 | import { toast } from 'vue-sonner' 4 | 5 | export function createToast(toastOptions) { 6 | const options = {} 7 | if (toastOptions.message && toastOptions.title) { 8 | options.message = toastOptions.message 9 | options.title = toastOptions.title 10 | } else if (toastOptions.message && !toastOptions.title) { 11 | options.message = toastOptions.message 12 | options.title = titleCase(toastOptions.variant) 13 | } else if (!toastOptions.message && toastOptions.title) { 14 | options.message = '' 15 | options.title = toastOptions.title 16 | } 17 | const component = h(Toast, { ...toastOptions, ...options }) 18 | toast.custom(markRaw(component)) 19 | } 20 | 21 | function titleCase(str) { 22 | return str 23 | .toLowerCase() 24 | .split(' ') 25 | .map(function (word) { 26 | return word.charAt(0).toUpperCase() + word.slice(1) 27 | }) 28 | .join(' ') 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/utils/transitions.js: -------------------------------------------------------------------------------- 1 | export const slideDownTransition = { 2 | enterActiveClass: 'transition duration-200 ease-out', 3 | leaveActiveClass: 'transition duration-150 ease-in', 4 | enterFromClass: 'translate-y-1 opacity-0', 5 | enterToClass: 'translate-y-0 opacity-100', 6 | leaveFromClass: 'translate-y-0 opacity-100', 7 | leaveToClass: 'translate-y-1 opacity-0', 8 | } 9 | 10 | export const slideRightTransition = { 11 | enterActiveClass: 'transition duration-200 ease-out', 12 | leaveActiveClass: 'transition duration-150 ease-in', 13 | enterFromClass: 'translate-x-1 opacity-0', 14 | enterToClass: 'translate-x-0 opacity-100', 15 | leaveFromClass: 'translate-x-0 opacity-100', 16 | leaveToClass: 'translate-x-1 opacity-0', 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/utils/useUsers.js: -------------------------------------------------------------------------------- 1 | import { reactive, computed } from 'vue' 2 | import { createResource } from 'frappe-ui' 3 | 4 | const userListResource = createResource('insights.api.user.get_users') 5 | const addUserResource = createResource({ 6 | url: 'insights.api.user.add_insights_user', 7 | }) 8 | 9 | export function useUsers() { 10 | const users = reactive({ 11 | list: computed(() => userListResource.data), 12 | loading: computed(() => userListResource.loading), 13 | error: computed(() => userListResource.error), 14 | refresh: () => userListResource.fetch(), 15 | add: (user) => addUserResource.submit({ user }).then(() => userListResource.fetch()), 16 | }) 17 | 18 | return users 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/vite-end.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/src/widgets/Bar/Bar.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /frontend/src/widgets/Bar/BarOptions.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 25 | -------------------------------------------------------------------------------- /frontend/src/widgets/Filter/Filter.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 31 | -------------------------------------------------------------------------------- /frontend/src/widgets/Funnel/Funnel.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /frontend/src/widgets/InvalidWidget.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /frontend/src/widgets/Line/Line.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /frontend/src/widgets/Line/LineOptions.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /frontend/src/widgets/MixedAxis/MixedAxis.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /frontend/src/widgets/MixedAxis/MixedAxisOptions.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /frontend/src/widgets/Pie/Pie.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /frontend/src/widgets/Row/RowOptions.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /frontend/src/widgets/Scatter/Scatter.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /frontend/src/widgets/Scatter/ScatterOptions.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /frontend/src/widgets/Text/Text.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 40 | -------------------------------------------------------------------------------- /frontend/src/widgets/Text/TextOptions.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 32 | -------------------------------------------------------------------------------- /frontend/src2/assets/duckdb-logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/frontend/src2/assets/duckdb-logo.webp -------------------------------------------------------------------------------- /frontend/src2/assets/insights-logo-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/src2/auth/LoginBox.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /frontend/src2/auth/NotFound.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /frontend/src2/charts/SharedChart.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /frontend/src2/charts/colors.ts: -------------------------------------------------------------------------------- 1 | 2 | export const COLOR_MAP = { 3 | blue: '#318AD8', 4 | pink: '#F683AE', 5 | green: '#48BB74', 6 | red: '#F56B6B', 7 | yellow: '#FACF7A', 8 | purple: '#44427B', 9 | teal: '#5FD8C4', 10 | orange: '#F8814F', 11 | cyan: '#15CCEF', 12 | grey: '#A6B1B9', 13 | '#449CF0': '#449CF0', 14 | '#ECAD4B': '#ECAD4B', 15 | '#761ACB': '#761ACB', 16 | '#CB2929': '#CB2929', 17 | '#ED6396': '#ED6396', 18 | '#29CD42': '#29CD42', 19 | '#4463F0': '#4463F0', 20 | '#EC864B': '#EC864B', 21 | '#4F9DD9': '#4F9DD9', 22 | '#39E4A5': '#39E4A5', 23 | '#B4CD29': '#B4CD29', 24 | } 25 | 26 | // https://10015.io/tools/color-shades-generator 27 | export const GRADIENT_COLORS = { 28 | blue: ['#2d87d6', '#4393da', '#589fdf', '#6dace3', '#83b8e7', '#98c4eb', '#c3dcf3', '#d8e9f7', '#edf5fc', '#ffffff'], 29 | } 30 | 31 | export const getColors = () => { 32 | return Object.values(COLOR_MAP) 33 | } 34 | 35 | export const getGradientColors = (color: keyof typeof GRADIENT_COLORS) => { 36 | return GRADIENT_COLORS[color] 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src2/charts/components/ChartIcon.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /frontend/src2/charts/components/ChartTitle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /frontend/src2/charts/components/ChartTypeSelector.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | -------------------------------------------------------------------------------- /frontend/src2/charts/components/CollapsibleSection.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 32 | -------------------------------------------------------------------------------- /frontend/src2/components/FormControl.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src2/components/Icons/CollapseSidebar.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /frontend/src2/components/Icons/DuckDBIcon.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /frontend/src2/components/Icons/IndicatorIcon.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /frontend/src2/components/Icons/JoinFullIcon.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /frontend/src2/components/InlineFormControlLabel.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | -------------------------------------------------------------------------------- /frontend/src2/components/LoadingOverlay.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /frontend/src2/dashboard/DashboardItemActions.vue: -------------------------------------------------------------------------------- 1 | 23 | 35 | -------------------------------------------------------------------------------- /frontend/src2/helpers/confirm_dialog.ts: -------------------------------------------------------------------------------- 1 | import ConfirmDialog from '../components/ConfirmDialog.vue' 2 | import { VNode, h, ref } from 'vue' 3 | 4 | export const dialogs = ref([]) 5 | 6 | export function confirmDialog({ 7 | title = 'Untitled', 8 | message = '', 9 | primaryActionLabel = 'Confirm', 10 | theme = 'gray', 11 | fields = [], 12 | onSuccess = () => {}, 13 | }) { 14 | const component = h(ConfirmDialog, { 15 | title, 16 | message, 17 | theme, 18 | fields, 19 | onSuccess, 20 | primaryActionLabel, 21 | }) 22 | dialogs.value.push(component) 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src2/helpers/dayjs.ts: -------------------------------------------------------------------------------- 1 | import customParseFormat from 'dayjs/esm/plugin/customParseFormat' 2 | import quarterOfYear from 'dayjs/esm/plugin/quarterOfYear' 3 | import { dayjs } from 'frappe-ui' 4 | 5 | dayjs.extend(quarterOfYear) 6 | dayjs.extend(customParseFormat) 7 | 8 | export default dayjs 9 | -------------------------------------------------------------------------------- /frontend/src2/helpers/store_locally.ts: -------------------------------------------------------------------------------- 1 | import { useStorage, watchDebounced } from '@vueuse/core' 2 | 3 | type Props = { 4 | key: keyof T 5 | namespace: string 6 | serializeFn: () => T 7 | defaultValue: T 8 | } 9 | export default function storeLocally(props: Props) { 10 | const serialized = props.serializeFn() 11 | const keyValue = serialized[props.key] 12 | const localData = useStorage(`${props.namespace}:${keyValue}`, props.defaultValue) 13 | watchDebounced(props.serializeFn, (data) => (localData.value = data), { 14 | deep: true, 15 | debounce: 1000, 16 | }) 17 | return localData 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src2/home/Home.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /frontend/src2/index.css: -------------------------------------------------------------------------------- 1 | @import 'frappe-ui/src/fonts/Inter/inter.css'; 2 | @import 'frappe-ui/src/style.css'; 3 | @import './styles/codemirror.css'; 4 | 5 | body { 6 | @apply text-base; 7 | } 8 | 9 | .tnum { 10 | font-feature-settings: 'tnum'; 11 | } 12 | 13 | @layer components { 14 | /* Works on Firefox */ 15 | * { 16 | scrollbar-width: thin; 17 | scrollbar-color: #c0c6cc #ebeef0; 18 | } 19 | 20 | html { 21 | scrollbar-width: auto; 22 | } 23 | 24 | /* Works on Chrome, Edge, and Safari */ 25 | *::-webkit-scrollbar-thumb { 26 | background: #e2e8f0; 27 | border-radius: 6px; 28 | } 29 | 30 | *::-webkit-scrollbar-track, 31 | *::-webkit-scrollbar-corner { 32 | background: #f8fafc; 33 | } 34 | 35 | *::-webkit-scrollbar { 36 | width: 0px; 37 | height: 6px; 38 | } 39 | 40 | body::-webkit-scrollbar { 41 | width: 0px; 42 | height: 12px; 43 | } 44 | } 45 | 46 | .fade-enter-active, 47 | .fade-leave-active { 48 | transition: opacity 0.1s ease; 49 | } 50 | 51 | .fade-enter-from, 52 | .fade-leave-to { 53 | opacity: 0; 54 | } 55 | -------------------------------------------------------------------------------- /frontend/src2/main.ts: -------------------------------------------------------------------------------- 1 | import { frappeRequest, setConfig } from 'frappe-ui' 2 | import { GridItem, GridLayout } from 'grid-layout-plus' 3 | import { createPinia } from 'pinia' 4 | import { createApp } from 'vue' 5 | import App from './App.vue' 6 | import { registerControllers, registerGlobalComponents } from './globals.ts' 7 | import './index.css' 8 | import router from './router.ts' 9 | 10 | setConfig('resourceFetcher', frappeRequest) 11 | 12 | const app = createApp(App) 13 | const pinia = createPinia() 14 | 15 | app.use(pinia) 16 | app.use(router) 17 | app.component('grid-layout', GridLayout) 18 | app.component('grid-item', GridItem) 19 | 20 | app.config.errorHandler = (err, vm, info) => { 21 | console.groupCollapsed('Unhandled Error in: ', info) 22 | console.error('Context:', vm) 23 | console.error('Error:', err) 24 | console.groupEnd() 25 | return false 26 | } 27 | 28 | registerGlobalComponents(app) 29 | registerControllers(app) 30 | 31 | app.mount('#app') 32 | -------------------------------------------------------------------------------- /frontend/src2/query/components/ColumnRemove.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | -------------------------------------------------------------------------------- /frontend/src2/query/components/ColumnSort.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 38 | -------------------------------------------------------------------------------- /frontend/src2/query/components/DataTypeIcon.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /frontend/src2/query/components/InlineExpression.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 38 | -------------------------------------------------------------------------------- /frontend/src2/query/components/RelativeDatePickerControl.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /frontend/src2/query/components/ViewSQLDialog.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 38 | -------------------------------------------------------------------------------- /frontend/src2/settings/SettingItem.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /frontend/src2/socket.ts: -------------------------------------------------------------------------------- 1 | import { io, Socket } from 'socket.io-client' 2 | import { socketio_port } from '../../../../sites/common_site_config.json' 3 | 4 | let socket: Socket 5 | 6 | export function getSocket() { 7 | if (socket) { 8 | return socket 9 | } 10 | 11 | let host = window.location.hostname 12 | let siteName = import.meta.env.DEV ? host : window.site_name 13 | let port = window.location.port ? `:${socketio_port}` : '' 14 | let protocol = port ? 'http' : 'https' 15 | let url = `${protocol}://${host}${port}/${siteName}` 16 | 17 | socket = io(url, { 18 | withCredentials: true, 19 | reconnectionAttempts: 5, 20 | }) 21 | return socket 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src2/teams/CreateTeamDialog.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 38 | -------------------------------------------------------------------------------- /frontend/src2/workbook/AvatarGroup.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /frontend/src2/workbook/WorkbookChart.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 34 | -------------------------------------------------------------------------------- /frontend/src2/workbook/WorkbookDashboard.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 31 | -------------------------------------------------------------------------------- /frontend/src2/workbook/WorkbookQuery.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import containerQueries from '@tailwindcss/container-queries' 2 | import frappeUIPreset from 'frappe-ui/src/tailwind/preset.js' 3 | 4 | export default { 5 | presets: [frappeUIPreset], 6 | content: [ 7 | './index.html', 8 | './src/**/*.{vue,js,ts,jsx,tsx}', 9 | './src2/**/*.{vue,js,ts,jsx,tsx}', 10 | './node_modules/frappe-ui/src/components/**/*.{vue,js,ts,jsx,tsx}', 11 | '../node_modules/frappe-ui/src/components/**/*.{vue,js,ts,jsx,tsx}', 12 | ], 13 | theme: { 14 | container: { 15 | center: true, 16 | padding: { 17 | DEFAULT: '1rem', 18 | sm: '2rem', 19 | lg: '2rem', 20 | xl: '4rem', 21 | '2xl': '4rem', 22 | }, 23 | }, 24 | extend: { 25 | maxWidth: { 26 | 'main-content': '768px', 27 | }, 28 | screens: { 29 | standalone: { 30 | raw: '(display-mode: standalone)', 31 | }, 32 | }, 33 | }, 34 | }, 35 | plugins: [containerQueries], 36 | } 37 | -------------------------------------------------------------------------------- /frontend/tests/dashboard_page.spec.js: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test' 2 | 3 | const login = async () => { 4 | await frappe.call('login', { 5 | usr: 'frappe@example.com', 6 | pwd: 'frappe', 7 | }) 8 | } 9 | 10 | test('dashboard_page', async ({ page }) => { 11 | await page.goto('http://frappe-insights:8000/') 12 | await page.evaluate(login) 13 | await page.goto('http://frappe-insights:8000/insights') 14 | expect(page.getByText('Dashboards')).toBeTruthy() 15 | }) 16 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable" 10 | ], 11 | "skipLibCheck": true, 12 | /* Bundler mode */ 13 | "moduleResolution": "bundler", 14 | "allowImportingTsExtensions": true, 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "preserve", 19 | "allowJs": true, 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": false, 23 | "noUnusedParameters": false, 24 | "noFallthroughCasesInSwitch": true, 25 | "paths": { 26 | "@/*": [ 27 | "./src/*" 28 | ] 29 | } 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.d.ts", 34 | "src/**/*.tsx", 35 | "src/**/*.vue", 36 | "src2/**/*.ts", 37 | "src2/**/*.d.ts", 38 | "src2/**/*.tsx", 39 | "src2/**/*.vue" 40 | ], 41 | "references": [ 42 | { 43 | "path": "./tsconfig.node.json" 44 | } 45 | ], 46 | } -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } -------------------------------------------------------------------------------- /insights/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | __version__ = "3.0.26" 6 | 7 | 8 | def create_toast( 9 | message: str | None = None, 10 | title: str | None = None, 11 | type: str = "info", 12 | duration: int = 5, 13 | ): 14 | import frappe 15 | 16 | if not title: 17 | title = type.capitalize() 18 | 19 | frappe.publish_realtime( 20 | event="insights_notification", 21 | user=frappe.session.user, 22 | message={ 23 | "message": message, 24 | "title": title, 25 | "type": type, 26 | "user": frappe.session.user, 27 | "duration": duration, 28 | }, 29 | ) 30 | 31 | 32 | # for backward compatibility 33 | def notify(*args, **kwargs): 34 | create_toast(*args, **kwargs) 35 | -------------------------------------------------------------------------------- /insights/api/alerts.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from insights.decorators import insights_whitelist, validate_type 4 | 5 | 6 | @insights_whitelist() 7 | @validate_type 8 | def get_alerts(query: str): 9 | return frappe.get_list( 10 | "Insights Alert", 11 | filters={"query": query}, 12 | fields=["*"], 13 | ) 14 | -------------------------------------------------------------------------------- /insights/api/notebooks.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | @frappe.whitelist() 5 | def get_notebooks(): 6 | # TODO: Add permission check 7 | return frappe.get_list( 8 | "Insights Notebook", 9 | fields=["name", "title", "creation", "modified"], 10 | order_by="creation desc", 11 | ) 12 | 13 | 14 | @frappe.whitelist() 15 | def create_notebook(title): 16 | notebook = frappe.new_doc("Insights Notebook") 17 | notebook.title = title 18 | notebook.save() 19 | return notebook.name 20 | 21 | 22 | @frappe.whitelist() 23 | def create_notebook_page(notebook): 24 | notebook_page = frappe.new_doc("Insights Notebook Page") 25 | notebook_page.notebook = notebook 26 | notebook_page.title = "Untitled" 27 | notebook_page.save() 28 | return notebook_page.name 29 | 30 | 31 | @frappe.whitelist() 32 | def get_notebook_pages(notebook): 33 | return frappe.get_list( 34 | "Insights Notebook Page", 35 | filters={"notebook": notebook}, 36 | fields=["name", "title", "creation", "modified"], 37 | order_by="creation desc", 38 | ) 39 | -------------------------------------------------------------------------------- /insights/cache_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import hashlib 5 | 6 | import frappe 7 | 8 | EXPIRY = 60 * 10 9 | 10 | 11 | def make_digest(*args): 12 | key = "" 13 | for arg in args: 14 | if isinstance(arg, dict): 15 | key += frappe.as_json(arg) 16 | key += frappe.cstr(arg) 17 | return hashlib.md5(key.encode("utf-8")).hexdigest() 18 | 19 | 20 | def get_or_set_cache(key, func, force=False, expiry=EXPIRY): 21 | key = f"insights|{key}" 22 | cached_value = frappe.cache().get_value(key) 23 | if cached_value is not None and not force: 24 | return cached_value 25 | 26 | value = func() 27 | frappe.cache().set_value(key, value, expires_in_sec=expiry) 28 | return value 29 | 30 | 31 | @frappe.whitelist() 32 | def reset_insights_cache(): 33 | frappe.only_for("System Manager") 34 | frappe.cache().delete_keys("insights*") 35 | -------------------------------------------------------------------------------- /insights/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/config/__init__.py -------------------------------------------------------------------------------- /insights/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "Frappe Insights", 8 | "color": "grey", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("Frappe Insights"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /insights/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/[org_name]/insights" 6 | # headline = "App that does everything" 7 | # sub_heading = "Yes, you got that right the first time, everything" 8 | 9 | 10 | def get_context(context): 11 | context.brand_html = "Frappe Insights" 12 | -------------------------------------------------------------------------------- /insights/fixtures/insights_data_source.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "doctype": "Insights Data Source", 4 | "is_site_db": 1, 5 | "name": "Site DB", 6 | "title": "Site DB", 7 | "status": "Active", 8 | "database_type": "MariaDB", 9 | "modified": "2022-01-01 00:01:00.000000", 10 | "creation": "2022-01-01 00:01:00.000000" 11 | }, 12 | { 13 | "doctype": "Insights Data Source", 14 | "status": "Active", 15 | "name": "Query Store", 16 | "title": "Query Store", 17 | "database_type": "SQLite", 18 | "allow_imports": 1, 19 | "modified": "2022-01-01 00:01:00.000000", 20 | "creation": "2022-01-01 00:01:00.000000" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /insights/fixtures/insights_data_source_v3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "doctype": "Insights Data Source v3", 4 | "is_site_db": 1, 5 | "is_frappe_db": 1, 6 | "name": "Site DB", 7 | "title": "Site DB", 8 | "status": "Active", 9 | "database_type": "MariaDB", 10 | "owner": "Administrator", 11 | "modified_by": "Administrator", 12 | "modified": "2022-01-01 00:01:00.000000", 13 | "creation": "2022-01-01 00:01:00.000000" 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /insights/fixtures/insights_notebook.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Uncategorized", 4 | "owner": "Administrator", 5 | "creation": "2023-04-16 21:35:21.596615", 6 | "modified": "2023-04-16 21:35:21.596615", 7 | "modified_by": "Administrator", 8 | "docstatus": 0, 9 | "idx": 0, 10 | "title": "Uncategorized", 11 | "doctype": "Insights Notebook" 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /insights/fixtures/role.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bulk_actions": 0, 4 | "dashboard": 0, 5 | "desk_access": 1, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Role", 9 | "form_sidebar": 0, 10 | "is_custom": 0, 11 | "list_sidebar": 0, 12 | "modified": "2022-12-07 21:36:44.284615", 13 | "name": "Insights Admin", 14 | "notifications": 0, 15 | "restrict_to_domain": null, 16 | "role_name": "Insights Admin", 17 | "search_bar": 0, 18 | "timeline": 0, 19 | "two_factor_auth": 0, 20 | "view_switcher": 0 21 | }, 22 | { 23 | "bulk_actions": 0, 24 | "dashboard": 0, 25 | "desk_access": 0, 26 | "disabled": 0, 27 | "docstatus": 0, 28 | "doctype": "Role", 29 | "form_sidebar": 0, 30 | "is_custom": 0, 31 | "list_sidebar": 0, 32 | "modified": "2022-12-07 21:36:44.121244", 33 | "name": "Insights User", 34 | "notifications": 0, 35 | "restrict_to_domain": null, 36 | "role_name": "Insights User", 37 | "search_bar": 0, 38 | "timeline": 0, 39 | "two_factor_auth": 0, 40 | "view_switcher": 0 41 | } 42 | ] -------------------------------------------------------------------------------- /insights/insights/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_alert/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_alert/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_alert/insights_alert.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Alert", { 5 | refresh: function (frm) { 6 | frm.add_custom_button(__("Send Alert"), function () { 7 | frappe.dom.freeze(__("Sending Alert...")); 8 | frm.call("send_alert") 9 | .then(() => { 10 | frappe.dom.unfreeze(); 11 | frappe.show_alert({ 12 | message: __("Alert sent"), 13 | indicator: "green", 14 | }); 15 | }) 16 | .catch(() => { 17 | frappe.dom.unfreeze(); 18 | }); 19 | }); 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_alert/test_insights_alert.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsAlert(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_chart/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart/insights_chart.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Chart", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_chart/patches/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart/test_insights_chart.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsChart(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_chart_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart_v3/insights_chart_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Chart v3", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_chart_v3/test_insights_chart_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsChartv3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_dashboard/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard/insights_dashboard.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Insights Dashboard', { 5 | // refresh: function(frm) { 6 | // } 7 | }) 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard/test_insights_dashboard.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsDashboard(FrappeTestCase): 9 | def test_something(self): 10 | pass 11 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_chart_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_dashboard_chart_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_chart_v3/insights_dashboard_chart_v3.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2025-03-19 14:51:15.821678", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "chart" 10 | ], 11 | "fields": [ 12 | { 13 | "fieldname": "chart", 14 | "fieldtype": "Link", 15 | "label": "Chart", 16 | "options": "Insights Chart v3" 17 | } 18 | ], 19 | "index_web_pages_for_search": 1, 20 | "istable": 1, 21 | "links": [], 22 | "modified": "2025-03-19 14:51:30.724154", 23 | "modified_by": "Administrator", 24 | "module": "Insights", 25 | "name": "Insights Dashboard Chart v3", 26 | "owner": "Administrator", 27 | "permissions": [], 28 | "sort_field": "creation", 29 | "sort_order": "DESC", 30 | "states": [] 31 | } -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_chart_v3/insights_dashboard_chart_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsDashboardChartv3(Document): 9 | # begin: auto-generated types 10 | # This code is auto-generated. Do not modify anything in this block. 11 | 12 | from typing import TYPE_CHECKING 13 | 14 | if TYPE_CHECKING: 15 | from frappe.types import DF 16 | 17 | chart: DF.Link | None 18 | parent: DF.Data 19 | parentfield: DF.Data 20 | parenttype: DF.Data 21 | # end: auto-generated types 22 | 23 | pass 24 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_item/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_dashboard_item/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_item/insights_dashboard_item.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsDashboardItem(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_dashboard_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_v3/insights_dashboard_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Dashboard v3", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_dashboard_v3/test_insights_dashboard_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsDashboardv3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_data_source/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source/insights_data_source.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Data Source", { 5 | refresh: function (frm) { 6 | if (frm.name == "Query Store") { 7 | frm.set_read_only(); 8 | } 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_data_source_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/connectors/bigquery.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | import ibis 6 | 7 | 8 | def get_bigquery_connection(data_source): 9 | project_id = data_source.bigquery_project_id 10 | dataset_id = data_source.bigquery_dataset_id 11 | credentials = data_source.bigquery_service_account_key 12 | 13 | try: 14 | from google.oauth2 import service_account 15 | except ImportError: 16 | raise ImportError("Please install google-auth to use BigQuery as a data source") 17 | 18 | credentials = service_account.Credentials.from_service_account_info( 19 | frappe.parse_json(credentials) 20 | ) 21 | 22 | return ibis.bigquery.connect( 23 | project_id=project_id, 24 | dataset_id=dataset_id, 25 | credentials=credentials, 26 | ) 27 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/connectors/mssql.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | import ibis 6 | 7 | 8 | def get_mssql_connection(data_source): 9 | if not frappe.conf.get("mssql_odbc_driver"): 10 | frappe.throw( 11 | "MSSQL ODBC driver path not configured. Please set it in common_site_config.json" 12 | " under 'mssql_odbc_driver' key." 13 | " Eg. 'mssql_odbc_driver': '/usr/local/lib/libtdsodbc.so' or 'FreeTDS'" 14 | ) 15 | 16 | password = data_source.get_password(raise_exception=False) 17 | data_source.port = int(data_source.port or 1433) 18 | 19 | return ibis.mssql.connect( 20 | host=data_source.host, 21 | port=data_source.port, 22 | user=data_source.username, 23 | password=password, 24 | database=data_source.database_name, 25 | driver=frappe.conf.get("mssql_odbc_driver"), 26 | ) 27 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/connectors/postgresql.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | from urllib.parse import quote_plus 5 | 6 | import ibis 7 | 8 | 9 | def get_postgres_connection(data_source): 10 | if data_source.connection_string: 11 | conn_string = quote_plus(data_source.connection_string) 12 | return ibis.connect(conn_string) 13 | else: 14 | password = data_source.get_password(raise_exception=False) 15 | data_source.port = int(data_source.port or 5432) 16 | return ibis.postgres.connect( 17 | host=data_source.host, 18 | port=data_source.port, 19 | user=data_source.username, 20 | password=password, 21 | database=data_source.database_name, 22 | schema=data_source.schema, 23 | sslmode="require" if data_source.use_ssl else None, 24 | ) 25 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/connectors/sqlite.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import os 5 | 6 | import ibis 7 | from frappe.utils import get_files_path 8 | 9 | 10 | def get_sqlite_connection(data_source): 11 | database_path = get_files_path(is_private=1) 12 | database_path = os.path.join(database_path, f"{data_source.database_name}.sqlite") 13 | database_path = os.path.abspath(database_path) 14 | database_path = database_path.lstrip("/") 15 | return ibis.connect(database_path) 16 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/ibis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_data_source_v3/ibis/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/insights_data_source_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Data Source v3", { 5 | refresh: function (frm) {}, 6 | }); 7 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_data_source_v3/patches/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_data_source_v3/test_insights_data_source_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsDataSourcev3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_notebook/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook/insights_notebook.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Notebook", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook/insights_notebook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsNotebook(Document): 9 | def on_trash(self): 10 | if self.name == "Uncategorized": 11 | frappe.throw("Cannot delete the default notebook") 12 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook/test_insights_notebook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsNotebook(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_notebook_page/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/insights_notebook_page.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Notebook Page", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/insights_notebook_page.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsNotebookPage(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_notebook_page/patches/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/patches/replace_query_builder_with_editor.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from pypika.terms import CustomFunction 3 | 4 | 5 | def execute(): 6 | if not frappe.db.count("Insights Notebook Page", {"content": ["like", '%"query-builder"%']}): 7 | return 8 | 9 | pages = frappe.get_all( 10 | "Insights Notebook Page", 11 | filters={"content": ["like", '%"query-builder"%']}, 12 | pluck="name", 13 | ) 14 | for page in pages: 15 | content = frappe.get_value("Insights Notebook Page", page, "content") 16 | content = content.replace('"query-builder"', '"query-editor"') 17 | frappe.db.set_value( 18 | "Insights Notebook Page", page, "content", content, update_modified=False 19 | ) 20 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_notebook_page/test_insights_notebook_page.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsNotebookPage(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/insights_query.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Query", { 5 | refresh(frm) { 6 | if (frm.doc.status == "Pending Execution") { 7 | frm.add_custom_button(__("Run"), () => { 8 | frm.call({ 9 | method: "run", 10 | doc: frm.doc, 11 | callback: (r) => { 12 | frm.reload_doc(); 13 | }, 14 | }); 15 | }); 16 | } 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query/patches/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/patches/make_query_variable_value_password_field.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """make_query_variable_value_password_field""" 6 | 7 | if not frappe.db.exists("DocType", "Insights Query Variable"): 8 | return 9 | 10 | query_variables = frappe.get_all( 11 | "Insights Query Variable", fields=["name", "parent", "variable_value"] 12 | ) 13 | frappe.reload_doc("insights", "doctype", "insights_query") 14 | frappe.reload_doc("insights", "doctype", "insights_query_variable") 15 | for query_variable in query_variables: 16 | doc = frappe.get_doc("Insights Query", query_variable["parent"]) 17 | for var in doc.variables: 18 | if var.name == query_variable["name"]: 19 | var.variable_value = query_variable["variable_value"] 20 | doc.save() 21 | break 22 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/patches/rename_untitled_query_to_query_name.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """rename untitled query to query name""" 6 | 7 | query_names = frappe.get_all( 8 | "Insights Query", 9 | fields=["name", "title"], 10 | filters={"title": "Untitled Query"}, 11 | ) 12 | 13 | for query in query_names: 14 | frappe.db.set_value( 15 | "Insights Query", 16 | query.name, 17 | "title", 18 | query.name.replace("-", " ").replace("QRY", "Query"), 19 | ) 20 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/patches/set_chart_name.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | # set chart name for existing insights query 6 | frappe.db.sql( 7 | """ 8 | UPDATE `tabInsights Query` q 9 | SET chart = (select name from `tabInsights Chart` where query = q.name limit 1) 10 | WHERE chart IS NULL 11 | """ 12 | ) 13 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query/test_insights_query.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | 5 | import frappe 6 | from frappe.tests.utils import FrappeTestCase 7 | 8 | test_dependencies = ("Insights Data Source", "Insights Table") 9 | test_records = frappe.get_test_records("Insights Query") 10 | 11 | 12 | class TestInsightsQuery(FrappeTestCase): 13 | pass 14 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_chart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_chart/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_chart/insights_query_chart.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Query Chart", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_chart/insights_query_chart.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | from json import dumps 5 | 6 | import frappe 7 | from frappe import _dict 8 | from frappe.model.document import Document 9 | 10 | 11 | class InsightsQueryChart(Document): 12 | @frappe.whitelist() 13 | def update_doc(self, doc): 14 | doc = _dict(doc) 15 | self.title = doc.title 16 | self.type = doc.type 17 | self.config = dumps(doc.config, indent=2) 18 | self.save() 19 | 20 | @frappe.whitelist() 21 | def add_to_dashboard(self, dashboard): 22 | dashboard_doc = frappe.get_doc("Insights Dashboard", dashboard) 23 | dashboard_doc.add_item( 24 | { 25 | "item_type": "Chart", 26 | "chart": self.name, 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_chart/test_insights_query_chart.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsQueryChart(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_column/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_column/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_column/insights_query_column.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryColumn(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_execution_log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_execution_log/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_execution_log/insights_query_execution_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Query Execution Log", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_execution_log/insights_query_execution_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryExecutionLog(Document): 9 | # begin: auto-generated types 10 | # This code is auto-generated. Do not modify anything in this block. 11 | 12 | from typing import TYPE_CHECKING 13 | 14 | if TYPE_CHECKING: 15 | from frappe.types import DF 16 | 17 | data_source: DF.Data | None 18 | query: DF.Data | None 19 | sql: DF.Code | None 20 | time_taken: DF.Float 21 | # end: auto-generated types 22 | 23 | pass 24 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_execution_log/test_insights_query_execution_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsQueryExecutionLog(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_result/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_result/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_result/insights_query_result.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Query Result", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_result/insights_query_result.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryResult(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_result/test_insights_query_result.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsQueryResult(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_table/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_table/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_table/insights_query_table.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryTable(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_transform/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_transform/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_transform/insights_query_transform.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Query Transform", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_transform/insights_query_transform.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2022-11-18 16:33:40.015647", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "type", 10 | "options" 11 | ], 12 | "fields": [ 13 | { 14 | "fieldname": "type", 15 | "fieldtype": "Select", 16 | "in_list_view": 1, 17 | "label": "Type", 18 | "options": "Pivot\nUnpivot\nTranspose\nCumulativeSum" 19 | }, 20 | { 21 | "default": "{}", 22 | "fieldname": "options", 23 | "fieldtype": "JSON", 24 | "in_list_view": 1, 25 | "label": "Options", 26 | "read_only": 1 27 | } 28 | ], 29 | "index_web_pages_for_search": 1, 30 | "istable": 1, 31 | "links": [], 32 | "modified": "2023-12-02 14:08:17.355102", 33 | "modified_by": "Administrator", 34 | "module": "Insights", 35 | "name": "Insights Query Transform", 36 | "owner": "Administrator", 37 | "permissions": [], 38 | "sort_field": "modified", 39 | "sort_order": "DESC", 40 | "states": [] 41 | } -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_transform/insights_query_transform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryTransform(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_transform/test_insights_query_transform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsQueryTransform(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_v3/insights_query_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Query v3", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_v3/test_insights_query_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsQueryv3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_variable/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_query_variable/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_variable/insights_query_variable.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2023-08-12 23:06:17.425710", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "variable_name", 10 | "variable_value" 11 | ], 12 | "fields": [ 13 | { 14 | "fieldname": "variable_name", 15 | "fieldtype": "Data", 16 | "in_list_view": 1, 17 | "label": "Variable Name", 18 | "reqd": 1 19 | }, 20 | { 21 | "fieldname": "variable_value", 22 | "fieldtype": "Password", 23 | "in_list_view": 1, 24 | "label": "Variable Value", 25 | "reqd": 1 26 | } 27 | ], 28 | "index_web_pages_for_search": 1, 29 | "istable": 1, 30 | "links": [], 31 | "modified": "2023-09-18 13:27:44.064362", 32 | "modified_by": "Administrator", 33 | "module": "Insights", 34 | "name": "Insights Query Variable", 35 | "owner": "Administrator", 36 | "permissions": [], 37 | "sort_field": "modified", 38 | "sort_order": "DESC", 39 | "states": [] 40 | } -------------------------------------------------------------------------------- /insights/insights/doctype/insights_query_variable/insights_query_variable.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsQueryVariable(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_resource_permission/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_resource_permission/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_resource_permission/insights_resource_permission.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsResourcePermission(Document): 9 | # begin: auto-generated types 10 | # This code is auto-generated. Do not modify anything in this block. 11 | 12 | from typing import TYPE_CHECKING 13 | 14 | if TYPE_CHECKING: 15 | from frappe.types import DF 16 | 17 | name: DF.Int | None 18 | parent: DF.Data 19 | parentfield: DF.Data 20 | parenttype: DF.Data 21 | resource_name: DF.DynamicLink 22 | resource_type: DF.Link 23 | table_restrictions: DF.Data | None 24 | # end: auto-generated types 25 | 26 | pass 27 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_settings/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_settings/insights_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Insights Settings', { 5 | // refresh: function(frm) { 6 | // } 7 | }) 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_settings/test_insights_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsSettings(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/insights_table.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Insights Table', { 5 | // refresh: function(frm) { 6 | // } 7 | }) 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table/patches/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/patches/delete_unused_query_based_tables.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """delete_unused_query_based_tables""" 6 | 7 | # get all insights tables 8 | insights_tables = frappe.get_all( 9 | "Insights Table", filters={"is_query_based": 1}, fields=["name", "table"] 10 | ) 11 | 12 | # get all generate sqls 13 | generated_sqls = frappe.get_all("Insights Query", pluck="sql") 14 | 15 | # delete insights table if `table` not present in any sql 16 | for table in insights_tables: 17 | for sql in generated_sqls: 18 | if sql and table.table in sql: 19 | break 20 | else: 21 | frappe.delete_doc("Insights Table", table.name) 22 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/test_insights_table.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | test_dependencies = ["Insights Data Source"] 8 | 9 | 10 | class TestInsightsTable(FrappeTestCase): 11 | pass 12 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table/test_records.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data_source": "Site DB", 4 | "table": "tabToDo", 5 | "label": "ToDo", 6 | "hidden": 0, 7 | "doctype": "Insights Table", 8 | "table_links": [], 9 | "columns": [] 10 | }, 11 | { 12 | "data_source": "Site DB", 13 | "table": "tabUser", 14 | "label": "User", 15 | "hidden": 0, 16 | "doctype": "Insights Table", 17 | "table_links": [], 18 | "columns": [] 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_column/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_column/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_column/insights_table_column.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Insights Table Column', { 5 | // refresh: function(frm) { 6 | // } 7 | }) 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_column/test_insights_table_column.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTableColumn(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_import/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import/insights_table_import.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Table Import", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import/test_insights_table_import.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTableImport(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import_log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_import_log/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import_log/insights_table_import_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Table Import Log", { 5 | refresh(frm) { 6 | if (frm.doc.status == "In Progress") { 7 | frm.add_custom_button(__("Mark as Failed"), function () { 8 | frm.call("mark_as_failed").then(() => { 9 | frm.reload_doc(); 10 | }); 11 | }); 12 | } 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_import_log/test_insights_table_import_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTableImportLog(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_link/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link/insights_table_link.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Insights Table Link', { 5 | // refresh: function(frm) { 6 | // } 7 | }) 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link/insights_table_link.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsTableLink(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link/test_insights_table_link.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTableLink(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_link_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link_v3/insights_table_link_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Table Link v3", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_link_v3/test_insights_table_link_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTableLinkv3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_v3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_table_v3/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_v3/insights_table_v3.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Table v3", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_table_v3/test_insights_table_v3.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsTablev3(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_team/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_team/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_team/insights_team.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Insights Team", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_team_member/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_team_member/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_team_member/insights_team_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "autoincrement", 4 | "creation": "2022-12-22 20:17:59.274364", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "user" 10 | ], 11 | "fields": [ 12 | { 13 | "fieldname": "user", 14 | "fieldtype": "Link", 15 | "in_list_view": 1, 16 | "label": "User", 17 | "options": "User", 18 | "reqd": 1 19 | } 20 | ], 21 | "index_web_pages_for_search": 1, 22 | "istable": 1, 23 | "links": [], 24 | "modified": "2022-12-22 20:17:59.274364", 25 | "modified_by": "Administrator", 26 | "module": "Insights", 27 | "name": "Insights Team Member", 28 | "naming_rule": "Autoincrement", 29 | "owner": "Administrator", 30 | "permissions": [], 31 | "sort_field": "modified", 32 | "sort_order": "DESC", 33 | "states": [] 34 | } -------------------------------------------------------------------------------- /insights/insights/doctype/insights_team_member/insights_team_member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class InsightsTeamMember(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_user_invitation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_user_invitation/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_user_invitation/insights_user_invitation.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights User Invitation", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_user_invitation/test_insights_user_invitation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsUserInvitation(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_workbook/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/doctype/insights_workbook/__init__.py -------------------------------------------------------------------------------- /insights/insights/doctype/insights_workbook/insights_workbook.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("Insights Workbook", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /insights/insights/doctype/insights_workbook/test_insights_workbook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestInsightsWorkbook(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /insights/insights/page/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/page/__init__.py -------------------------------------------------------------------------------- /insights/insights/page/insights/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/page/insights/__init__.py -------------------------------------------------------------------------------- /insights/insights/page/insights/insights.js: -------------------------------------------------------------------------------- 1 | frappe.pages['insights'].on_page_load = function (wrapper) { 2 | window.location.href = '/insights' 3 | } 4 | -------------------------------------------------------------------------------- /insights/insights/page/insights/insights.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": null, 3 | "creation": "2022-08-23 18:00:40.056399", 4 | "docstatus": 0, 5 | "doctype": "Page", 6 | "idx": 0, 7 | "modified": "2022-08-23 18:00:46.933155", 8 | "modified_by": "Administrator", 9 | "module": "Insights", 10 | "name": "insights", 11 | "owner": "Administrator", 12 | "page_name": "insights", 13 | "roles": [], 14 | "script": null, 15 | "standard": "Yes", 16 | "style": null, 17 | "system_page": 1, 18 | "title": "Insights" 19 | } -------------------------------------------------------------------------------- /insights/insights/query_builders/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/insights/query_builders/__init__.py -------------------------------------------------------------------------------- /insights/migrate.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | 5 | import frappe 6 | 7 | 8 | def after_migrate(): 9 | try: 10 | create_admin_team() 11 | except Exception: 12 | frappe.log_error(title="Error creating Admin Team") 13 | 14 | 15 | def create_admin_team(): 16 | if not frappe.db.exists("Insights Team", "Admin"): 17 | frappe.get_doc( 18 | { 19 | "doctype": "Insights Team", 20 | "team_name": "Admin", 21 | "team_members": [{"user": "Administrator"}], 22 | } 23 | ).insert(ignore_permissions=True) 24 | -------------------------------------------------------------------------------- /insights/modules.txt: -------------------------------------------------------------------------------- 1 | Insights -------------------------------------------------------------------------------- /insights/patches/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/patches/__init__.py -------------------------------------------------------------------------------- /insights/patches/add_column_row_to_result.py: -------------------------------------------------------------------------------- 1 | from json import dumps 2 | 3 | import click 4 | import frappe 5 | 6 | 7 | def execute(): 8 | if not frappe.db.a_row_exists("Insights Query"): 9 | return 10 | 11 | queries = frappe.get_all("Insights Query", pluck="name") 12 | for query in queries: 13 | try: 14 | doc = frappe.get_doc("Insights Query", query) 15 | results = frappe.parse_json(doc.results) 16 | if not results or "::" in str(results[0][0]): 17 | continue 18 | columns = [f"{c.label or c.column}::{c.type}" for c in doc.get_columns()] 19 | results.insert(0, columns) 20 | frappe.db.set_value( 21 | "Insights Query", query, "result", dumps(results), update_modified=False 22 | ) 23 | frappe.db.commit() 24 | except Exception: 25 | frappe.db.rollback() 26 | frappe.log_error(title="Error while adding column row for - " + query) 27 | click.secho("Error while adding column row for - " + query, fg="red") 28 | -------------------------------------------------------------------------------- /insights/patches/add_last_execution_field.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Query"): 6 | return 7 | 8 | Query = frappe.qb.DocType("Insights Query") 9 | frappe.qb.update(Query).set(Query.last_execution, Query.modified).run() 10 | -------------------------------------------------------------------------------- /insights/patches/add_position_key_to_filter.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import frappe 4 | from frappe.utils import cstr 5 | 6 | 7 | def execute(): 8 | if not frappe.db.a_row_exists("Insights Query"): 9 | return 10 | 11 | queries = frappe.get_all("Insights Query", fields=["name", "filters"]) 12 | 13 | for query in queries: 14 | _filters = json.loads(query.get("filters")) 15 | set_default_position(_filters) 16 | _filters = json.dumps(_filters, indent=2, default=cstr) 17 | frappe.db.set_value( 18 | "Query", query.get("name"), "filters", _filters, update_modified=False 19 | ) 20 | 21 | 22 | def set_default_position(filters): 23 | if "conditions" not in filters: 24 | return 25 | 26 | filters["position"] = filters.get("position") or 1 27 | for condition in filters.get("conditions"): 28 | set_default_position(condition) 29 | -------------------------------------------------------------------------------- /insights/patches/add_roles.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | from insights.setup import create_roles 5 | 6 | 7 | def execute(): 8 | create_roles() 9 | -------------------------------------------------------------------------------- /insights/patches/convert_duration_to_float.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Query"): 6 | return 7 | 8 | Query = frappe.qb.DocType("Insights Query") 9 | frappe.qb.update(Query).set(Query.execution_time, 0).run() 10 | -------------------------------------------------------------------------------- /insights/patches/create_query_tables.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Query"): 6 | return 7 | 8 | if not frappe.db.exists("Insights Data Source", "Query Store"): 9 | frappe.get_doc( 10 | { 11 | "status": "Active", 12 | "name": "Query Store", 13 | "title": "Query Store", 14 | "database_type": "MariaDB", 15 | "doctype": "Insights Data Source", 16 | "modified": "2022-01-01 00:01:00.000000", 17 | "creation": "2022-01-01 00:01:00.000000", 18 | } 19 | ).insert() 20 | 21 | for query_name in frappe.get_all("Insights Query", pluck="name"): 22 | query = frappe.get_doc("Insights Query", query_name) 23 | query.update_query_table() 24 | -------------------------------------------------------------------------------- /insights/patches/enable_data_store.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if frappe.db.exists("Insights Table v3", {"stored": 1}): 6 | frappe.db.set_single_value("Insights Settings", "enable_data_store", 1) 7 | -------------------------------------------------------------------------------- /insights/patches/fix_select_options_after_rename.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | import frappe 4 | 5 | 6 | def execute(): 7 | # when the doctype Query was renamed, the options like "Permission Query" in Server Script was renamed to "Permission Insights Query" 8 | # this patch fixes the options in the docfields 9 | frappe.db.sql( 10 | "update `tabDocField` set `options` = replace(`options`, 'Insights ', '') where fieldtype = 'Select' and options like '%Insights %'" 11 | ) 12 | 13 | frappe.db.sql( 14 | "update `tabCustom Field` set `options` = replace(`options`, 'Insights ', '') where fieldtype = 'Select' and options like '%Insights %'" 15 | ) 16 | 17 | frappe.db.sql( 18 | "update `tabProperty Setter` set `value` = replace(`value`, 'Insights ', '') where property = 'options' and value like '%Insights %'" 19 | ) 20 | -------------------------------------------------------------------------------- /insights/patches/make_query_tables.py: -------------------------------------------------------------------------------- 1 | import click 2 | import frappe 3 | 4 | 5 | def execute(): 6 | if not frappe.db.a_row_exists("Insights Query"): 7 | return 8 | 9 | for query in frappe.get_all("Insights Query", pluck="name"): 10 | try: 11 | frappe.get_doc("Insights Query", query).update_insights_table() 12 | except Exception as e: 13 | click.secho(f"Failed to create table for {query}: {e}", fg="orange") 14 | -------------------------------------------------------------------------------- /insights/patches/migrate_dashboard_charts.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Dashboard Item"): 6 | return 7 | 8 | frappe.db.sql( 9 | """ 10 | UPDATE 11 | `tabInsights Dashboard Item` 12 | SET 13 | `item_type` = 'Chart', 14 | `chart` = `query_chart` 15 | WHERE 16 | `item_type` IS NULL 17 | OR `item_type` = '' 18 | """ 19 | ) 20 | -------------------------------------------------------------------------------- /insights/patches/rename_column_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Query Column"): 6 | return 7 | 8 | QueryColumn = frappe.qb.DocType("Insights Query Column") 9 | replace_map = { 10 | "Varchar": "String", 11 | "Int": "Integer", 12 | "Float": "Decimal", 13 | "Timestamp": "Datetime", 14 | "Longtext": "Text", 15 | } 16 | for old, new in replace_map.items(): 17 | ( 18 | frappe.qb.update(QueryColumn) 19 | .set(QueryColumn.type, new) 20 | .where(QueryColumn.type == old) 21 | .run() 22 | ) 23 | -------------------------------------------------------------------------------- /insights/patches/rename_count_column_name.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from pypika import Criterion 3 | 4 | 5 | def execute(): 6 | if not frappe.db.a_row_exists("Insights Query"): 7 | return 8 | 9 | frappe.db.sql( 10 | """ 11 | UPDATE 12 | `tabInsights Query Column` 13 | SET 14 | `column` = '*' 15 | WHERE 16 | `column` = '__count' OR `column` = 'count' 17 | """ 18 | ) 19 | -------------------------------------------------------------------------------- /insights/patches/rename_data_to_config.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | Chart = frappe.qb.DocType("Insights Query Chart") 6 | frappe.qb.update(Chart).set(Chart.config, Chart.data).run() 7 | -------------------------------------------------------------------------------- /insights/patches/rename_doctypes.py: -------------------------------------------------------------------------------- 1 | import click 2 | import frappe 3 | 4 | 5 | def execute(): 6 | rename_map = { 7 | "Query": "Insights Query", 8 | "Table": "Insights Table", 9 | "Table Link": "Insights Table Link", 10 | "Query Visualization": "Insights Query Chart", 11 | "Data Source": "Insights Data Source", 12 | "Query Table": "Insights Query Table", 13 | "Query Column": "Insights Query Column", 14 | } 15 | for oldname, newname in rename_map.items(): 16 | if frappe.db.exists("DocType", {"module": "Insights", "name": oldname}): 17 | try: 18 | frappe.rename_doc("DocType", oldname, newname, force=True) 19 | except Exception as e: 20 | frappe.log_error(title=f"Insights: Error renaming DocType {oldname}") 21 | click.echo( 22 | f"Error renaming {oldname} to {newname}: {str(e)}", color="orange" 23 | ) 24 | -------------------------------------------------------------------------------- /insights/patches/rename_like_to_contains.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.a_row_exists("Insights Query"): 6 | return 7 | 8 | queries = frappe.get_all("Insights Query", fields=["name", "filters"]) 9 | 10 | for query in queries: 11 | new_filter = query.filters.replace("like", "contains") 12 | frappe.db.set_value( 13 | "Query", query.name, "filters", new_filter, update_modified=False 14 | ) 15 | -------------------------------------------------------------------------------- /insights/patches/rename_target_column_field.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | import click 4 | import frappe 5 | 6 | 7 | def execute(): 8 | for chart in frappe.get_all( 9 | "Insights Query Chart", 10 | filters={"config": ("like", "%targetColumn%")}, 11 | fields=["name", "config"], 12 | ): 13 | try: 14 | chart_doc = frappe.get_doc("Insights Query Chart", chart.name) 15 | config = frappe.parse_json(chart_doc.config) 16 | config.target = config.targetColumn 17 | config.targetType = "Column" 18 | del config.targetColumn 19 | chart_doc.config = frappe.as_json(config) 20 | chart_doc.save() 21 | except Exception: 22 | click.secho(f"Failed to update {chart.name}", fg="red") 23 | frappe.log_error(title="Insights: Failed to update chart") 24 | -------------------------------------------------------------------------------- /insights/patches/rename_visualization.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.model.utils.rename_field import rename_field 3 | 4 | 5 | def execute(): 6 | try: 7 | rename_field("Insights Dashboard Item", "visualization", "query_chart") 8 | InsightsDashboardItem = frappe.qb.DocType("Insights Dashboard Item") 9 | ( 10 | frappe.qb.update(InsightsDashboardItem) 11 | .set(InsightsDashboardItem.parentfield, "items") 12 | .where(InsightsDashboardItem.parentfield == "visualizations") 13 | .run() 14 | ) 15 | 16 | except Exception as e: 17 | if e.args[0] != 1054: 18 | raise 19 | -------------------------------------------------------------------------------- /insights/patches/reset_query_filters.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import frappe 4 | 5 | 6 | def execute(): 7 | if not frappe.db.a_row_exists("Insights Query"): 8 | return 9 | 10 | Query = frappe.qb.DocType("Insights Query") 11 | default_filters = json.dumps( 12 | { 13 | "type": "LogicalExpression", 14 | "operator": "&&", 15 | "level": 1, 16 | "position": 1, 17 | "conditions": [], 18 | }, 19 | indent=2, 20 | ) 21 | frappe.qb.update(Query).set(Query.filters, default_filters).run() 22 | -------------------------------------------------------------------------------- /insights/patches/show_support_login_message.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from insights.api.subscription import get_subscription_key 4 | 5 | 6 | def execute(): 7 | if get_subscription_key(): 8 | notify_users() 9 | 10 | 11 | def notify_users(): 12 | # notify users about the support portal access 13 | note = frappe.new_doc("Note") 14 | note.title = "Insights Support Portal" 15 | note.public = 1 16 | note.notify_on_login = 1 17 | note.content = """ 18 |

Insights Support Portal


You have an active subscription (or trial) for Frappe Insights. You can now access the Insights Support Portal to get help from the Frappe team.


To access the portal go to Settings -> Send Login Link

support_portal_1.png


19 | """ 20 | note.insert(ignore_mandatory=True) 21 | -------------------------------------------------------------------------------- /insights/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/public/.gitkeep -------------------------------------------------------------------------------- /insights/public/js/setup_wizard.js: -------------------------------------------------------------------------------- 1 | // redirect to desk page 'insights' after setup wizard is complete 2 | // 'insights' desk page redirects to '/insights' 3 | frappe.setup.welcome_page = "/app/insights"; 4 | -------------------------------------------------------------------------------- /insights/setup/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/setup/__init__.py -------------------------------------------------------------------------------- /insights/setup/insights_demo_data.duckdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/setup/insights_demo_data.duckdb -------------------------------------------------------------------------------- /insights/setup/test_demo_setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | 5 | import unittest 6 | 7 | from .demo import DemoDataFactory 8 | 9 | 10 | class TestDemoSetup(unittest.TestCase): 11 | def test_import_demo_data(self): 12 | factory = DemoDataFactory().run() 13 | self.assertTrue(factory.demo_data_exists()) 14 | -------------------------------------------------------------------------------- /insights/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/templates/__init__.py -------------------------------------------------------------------------------- /insights/templates/emails/insights_invitation.html: -------------------------------------------------------------------------------- 1 |

You have been invited to use Insights

2 |

3 | Accept Invitation 4 |

5 | -------------------------------------------------------------------------------- /insights/templates/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/templates/pages/__init__.py -------------------------------------------------------------------------------- /insights/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/tests/__init__.py -------------------------------------------------------------------------------- /insights/www/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/insights/648112309292ae6a9bcddeed001623e10993f50f/insights/www/__init__.py -------------------------------------------------------------------------------- /insights/www/insights_v2.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # GNU GPLv3 License. See license.txt 3 | 4 | 5 | import frappe 6 | 7 | from insights.api.telemetry import track_active_site 8 | 9 | no_cache = 1 10 | 11 | 12 | def get_context(context): 13 | is_v2_user = frappe.db.count("Insights Query", cache=True) > 0 14 | if not is_v2_user: 15 | frappe.local.flags.redirect_location = "/insights" 16 | raise frappe.Redirect 17 | 18 | csrf_token = frappe.sessions.get_csrf_token() 19 | frappe.db.commit() 20 | context.csrf_token = csrf_token 21 | context.site_name = frappe.local.site 22 | track_active_site() 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "workspaces": ["frontend", "frappe-ui"], 5 | "scripts": { 6 | "postinstall": "cd frontend && yarn install --check-files", 7 | "dev": "cd frontend && yarn dev", 8 | "build": "cd frontend && yarn build", 9 | "test": "cd frontend && yarn test" 10 | } 11 | } 12 | --------------------------------------------------------------------------------