Skip to content

Commit

Permalink
feat(exports): Add xlsx format exports (#20568)
Browse files Browse the repository at this point in the history
* Make export converter yield items
* Add xlsx support and optimize some things further
  • Loading branch information
webjunkie authored Feb 28, 2024
1 parent 7cc9076 commit 08255db
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ const ExportsContent = (): JSX.Element => {
>
<div className="flex items-center justify-between flex-auto p-2">
<div>
<span className="text-link font-medium block">
{asset.filename}
<LemonTag size="small" className="ml-2">
{asset.export_format}
</LemonTag>
</span>
<span className="text-link font-medium block">{asset.filename}</span>
{asset.expires_after && (
<span className="text-xs text-muted mt-1">
Expires {dayjs(asset.expires_after).fromNow()}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ export function InsightMeta({
export_format: ExporterFormat.CSV,
export_context: exporterResourceParams,
},
{
export_format: ExporterFormat.XLSX,
export_context: exporterResourceParams,
},
]}
/>
</>
Expand Down
19 changes: 16 additions & 3 deletions frontend/src/queries/nodes/DataTable/DataTableExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import { dataTableLogic, DataTableRow } from './dataTableLogic'

export const EXPORT_MAX_LIMIT = 10000

export async function startDownload(query: DataTableNode, onlySelectedColumns: boolean): Promise<void> {
export async function startDownload(
query: DataTableNode,
onlySelectedColumns: boolean,
format: ExporterFormat = ExporterFormat.CSV
): Promise<void> {
const exportContext = isPersonsNode(query.source)
? { path: getPersonsEndpoint(query.source) }
: { source: query.source }
Expand All @@ -49,7 +53,7 @@ export async function startDownload(query: DataTableNode, onlySelectedColumns: b
}
}
await triggerExport({
export_format: ExporterFormat.CSV,
export_format: format,
export_context: exportContext,
})
}
Expand Down Expand Up @@ -221,7 +225,16 @@ export function DataTableExport({ query }: DataTableExportProps): JSX.Element |
actor={isPersonsNode(query.source) ? 'persons' : 'events'}
limit={EXPORT_MAX_LIMIT}
>
<LemonButton fullWidth>Export all columns</LemonButton>
<LemonButton fullWidth>Export all columns (CSV)</LemonButton>
</ExportWithConfirmation>,
<ExportWithConfirmation
key={0}
placement="topRight"
onConfirm={() => void startDownload(query, false, ExporterFormat.XLSX)}
actor={isPersonsNode(query.source) ? 'persons' : 'events'}
limit={EXPORT_MAX_LIMIT}
>
<LemonButton fullWidth>Export all columns (XLS)</LemonButton>
</ExportWithConfirmation>,
]
: []
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/queries/nodes/InsightViz/InsightVizDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,18 @@ export function InsightVizDisplay({
{exportContext && (
<div className="flex items-center justify-between my-4 mx-0">
<h2 className="font-semibold text-lg m-0">Detailed results</h2>
<Tooltip title="Export this table in CSV format" placement="left">
<Tooltip title="Export this table" placement="left">
<ExportButton
type="secondary"
items={[
{
export_format: ExporterFormat.CSV,
export_context: exportContext,
},
{
export_format: ExporterFormat.XLSX,
export_context: exportContext,
},
]}
/>
</Tooltip>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/scenes/insights/InsightPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ export function InsightPageHeader({ insightLogicProps }: { insightLogicProps: In
export_format: ExporterFormat.CSV,
export_context: exporterResourceParams,
},
{
export_format: ExporterFormat.XLSX,
export_context: exporterResourceParams,
},
]}
/>
) : null}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3238,6 +3238,7 @@ export enum ExporterFormat {
CSV = 'text/csv',
PDF = 'application/pdf',
JSON = 'application/json',
XLSX = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}

/** Exporting directly from the browser to a file */
Expand Down
2 changes: 1 addition & 1 deletion latest_migrations.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name
ee: 0015_add_verified_properties
otp_static: 0002_throttling
otp_totp: 0002_auto_20190420_0723
posthog: 0391_alter_batchexportbackfill_status_and_more
posthog: 0392_alter_exportedasset_export_format
sessions: 0001_initial
social_django: 0010_uid_db_index
two_factor: 0007_auto_20201201_1019
28 changes: 28 additions & 0 deletions posthog/migrations/0392_alter_exportedasset_export_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.1.13 on 2024-02-27 07:55

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("posthog", "0391_alter_batchexportbackfill_status_and_more"),
]

operations = [
migrations.AlterField(
model_name="exportedasset",
name="export_format",
field=models.CharField(
choices=[
("image/png", "image/png"),
("application/pdf", "application/pdf"),
("text/csv", "text/csv"),
(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
),
],
max_length=100,
),
),
]
8 changes: 6 additions & 2 deletions posthog/models/exported_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,20 @@ class ExportFormat(models.TextChoices):
PNG = "image/png", "image/png"
PDF = "application/pdf", "application/pdf"
CSV = "text/csv", "text/csv"
XLSX = (
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)

SUPPORTED_FORMATS = [ExportFormat.PNG, ExportFormat.CSV]
SUPPORTED_FORMATS = [ExportFormat.PNG, ExportFormat.CSV, ExportFormat.XLSX]

# Relations
team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE)
dashboard = models.ForeignKey("posthog.Dashboard", on_delete=models.CASCADE, null=True)
insight = models.ForeignKey("posthog.Insight", on_delete=models.CASCADE, null=True)

# Content related fields
export_format: models.CharField = models.CharField(max_length=16, choices=ExportFormat.choices)
export_format: models.CharField = models.CharField(max_length=100, choices=ExportFormat.choices)
content: models.BinaryField = models.BinaryField(null=True)
created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True, blank=True)
# DateTime after the created_at after which this asset should be deleted
Expand Down
5 changes: 2 additions & 3 deletions posthog/tasks/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ def export_asset(exported_asset_id: int, limit: Optional[int] = None) -> None:
pk=exported_asset_id
)

is_csv_export = exported_asset.export_format == ExportedAsset.ExportFormat.CSV
if is_csv_export:
csv_exporter.export_csv(exported_asset, limit=limit)
if exported_asset.export_format in (ExportedAsset.ExportFormat.CSV, ExportedAsset.ExportFormat.XLSX):
csv_exporter.export_tabular(exported_asset, limit=limit)
EXPORT_QUEUED_COUNTER.labels(type="csv").inc()
else:
image_exporter.export_image(exported_asset)
Expand Down
Loading

0 comments on commit 08255db

Please sign in to comment.