Skip to content

Commit 730e0e6

Browse files
feat(preprod): Show image scale info on treemap tooltip if available (#104879)
Closes https://linear.app/getsentry/issue/EME-665/include-scale-parameter-in-the-output-for-images-in-treemap <img width="762" height="493" alt="Screenshot 2025-12-12 at 11 45 28 AM" src="https://github.com/user-attachments/assets/6f6e748d-0757-455d-a073-f6b2b801fbc1" /> Pre-req PRs: - getsentry/sentry-cli#3029 - getsentry/launchpad#524
1 parent 13d8ef2 commit 730e0e6

File tree

7 files changed

+77
-54
lines changed

7 files changed

+77
-54
lines changed

src/sentry/preprod/size_analysis/insight_models.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,15 @@ class OptimizableImageFile(BaseModel):
155155

156156
# Minification savings (optimizing current format)
157157
minify_savings: int
158-
minified_size: int | None
158+
minified_size: int | None = None
159159

160160
# HEIC conversion savings (converting to HEIC format)
161161
conversion_savings: int
162-
heic_size: int | None
162+
heic_size: int | None = None
163163

164164
# Asset catalog specific fields
165-
idiom: str | None
166-
colorspace: str | None
165+
idiom: str | None = None
166+
colorspace: str | None = None
167167

168168
@property
169169
def potential_savings(self) -> int:

src/sentry/preprod/size_analysis/models.py

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,39 @@
3131

3232

3333
class AndroidInsightResults(BaseModel):
34-
duplicate_files: DuplicateFilesInsightResult | None
35-
webp_optimization: WebPOptimizationInsightResult | None
36-
large_images: LargeImageFileInsightResult | None
37-
large_videos: LargeVideoFileInsightResult | None
38-
large_audio: LargeAudioFileInsightResult | None
39-
hermes_debug_info: HermesDebugInfoInsightResult | None
40-
multiple_native_library_archs: MultipleNativeLibraryArchInsightResult | None
34+
duplicate_files: DuplicateFilesInsightResult | None = None
35+
webp_optimization: WebPOptimizationInsightResult | None = None
36+
large_images: LargeImageFileInsightResult | None = None
37+
large_videos: LargeVideoFileInsightResult | None = None
38+
large_audio: LargeAudioFileInsightResult | None = None
39+
hermes_debug_info: HermesDebugInfoInsightResult | None = None
40+
multiple_native_library_archs: MultipleNativeLibraryArchInsightResult | None = None
4141

4242

4343
class AppleInsightResults(BaseModel):
44-
duplicate_files: DuplicateFilesInsightResult | None
45-
large_images: LargeImageFileInsightResult | None
46-
large_audios: LargeAudioFileInsightResult | None
47-
large_videos: LargeVideoFileInsightResult | None
48-
strip_binary: StripBinaryInsightResult | None
49-
localized_strings_minify: LocalizedStringCommentsInsightResult | None
50-
small_files: SmallFilesInsightResult | None
51-
loose_images: LooseImagesInsightResult | None
52-
hermes_debug_info: HermesDebugInfoInsightResult | None
53-
image_optimization: ImageOptimizationInsightResult | None
54-
main_binary_exported_symbols: MainBinaryExportMetadataResult | None
55-
unnecessary_files: UnnecessaryFilesInsightResult | None
56-
audio_compression: AudioCompressionInsightResult | None
57-
video_compression: VideoCompressionInsightResult | None
58-
alternate_icons_optimization: ImageOptimizationInsightResult | None
44+
duplicate_files: DuplicateFilesInsightResult | None = None
45+
large_images: LargeImageFileInsightResult | None = None
46+
large_audios: LargeAudioFileInsightResult | None = None
47+
large_videos: LargeVideoFileInsightResult | None = None
48+
strip_binary: StripBinaryInsightResult | None = None
49+
localized_strings_minify: LocalizedStringCommentsInsightResult | None = None
50+
small_files: SmallFilesInsightResult | None = None
51+
loose_images: LooseImagesInsightResult | None = None
52+
hermes_debug_info: HermesDebugInfoInsightResult | None = None
53+
image_optimization: ImageOptimizationInsightResult | None = None
54+
main_binary_exported_symbols: MainBinaryExportMetadataResult | None = None
55+
unnecessary_files: UnnecessaryFilesInsightResult | None = None
56+
audio_compression: AudioCompressionInsightResult | None = None
57+
video_compression: VideoCompressionInsightResult | None = None
58+
alternate_icons_optimization: ImageOptimizationInsightResult | None = None
59+
60+
61+
class TreemapElementMisc(BaseModel):
62+
"""Miscellaneous metadata for treemap elements."""
63+
64+
model_config = ConfigDict(frozen=True)
65+
66+
scale: int | None = None
5967

6068

6169
class TreemapElement(BaseModel):
@@ -64,11 +72,12 @@ class TreemapElement(BaseModel):
6472

6573
name: str
6674
size: int
67-
path: str | None
75+
path: str | None = None
6876
is_dir: bool
69-
type: str | None
77+
type: str | None = None
7078
""" Some files (like zip files) are not directories but have children. """
7179
children: list[TreemapElement]
80+
misc: TreemapElementMisc | None = None
7281

7382

7483
class TreemapResults(BaseModel):
@@ -125,7 +134,7 @@ class SizeAnalysisResults(BaseModel):
125134
download_size: int
126135
install_size: int
127136
treemap: TreemapResults | None = None
128-
insights: AndroidInsightResults | AppleInsightResults | None
137+
insights: AndroidInsightResults | AppleInsightResults | None = None
129138
analysis_version: str | None = None
130139
file_analysis: FileAnalysis | None = None
131140
app_components: list[AppComponent] | None = None
@@ -145,17 +154,17 @@ class DiffType(str, Enum):
145154

146155
class DiffItem(BaseModel):
147156
size_diff: int
148-
head_size: int | None
149-
base_size: int | None
157+
head_size: int | None = None
158+
base_size: int | None = None
150159
path: str
151-
item_type: str | None
160+
item_type: str | None = None
152161
type: DiffType
153-
diff_items: list[DiffItem] | None
162+
diff_items: list[DiffItem] | None = None
154163

155164

156165
class SizeMetricDiffItem(BaseModel):
157166
metrics_artifact_type: PreprodArtifactSizeMetrics.MetricsArtifactType
158-
identifier: str | None
167+
identifier: str | None = None
159168
head_install_size: int
160169
head_download_size: int
161170
base_install_size: int
@@ -181,5 +190,5 @@ class ComparisonResults(BaseModel):
181190
insight_diff_items: list[InsightDiffItem]
182191
size_metric_diff_item: SizeMetricDiffItem
183192
skipped_diff_item_comparison: bool
184-
head_analysis_version: str | None
185-
base_analysis_version: str | None
193+
head_analysis_version: str | None = None
194+
base_analysis_version: str | None = None

static/app/views/preprod/buildComparison/main/sizeCompareItemDiffTable.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,11 @@ export function SizeCompareItemDiffTable({
233233
{capitalize(diffItem.item_type ?? '')}
234234
</SimpleTable.RowCell>
235235
<SimpleTable.RowCell>
236-
{diffItem.head_size
236+
{typeof diffItem.head_size === 'number'
237237
? formatBytesBase10(diffItem.head_size)
238-
: formatBytesBase10(diffItem.base_size!)}
238+
: typeof diffItem.base_size === 'number'
239+
? formatBytesBase10(diffItem.base_size)
240+
: '-'}
239241
</SimpleTable.RowCell>
240242
<DiffTableChangeAmountCell changeType={diffItem.type}>
241243
{diffItem.size_diff > 0 ? '+' : '-'}

static/app/views/preprod/buildDetails/main/insights/appSizeInsightsSidebarRow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,9 @@ function OptimizableImageFileRow({
315315
}
316316

317317
const hasMinifySavings =
318-
originalFile.minified_size !== null && originalFile.minify_savings > 0;
318+
typeof originalFile.minified_size === 'number' && originalFile.minify_savings > 0;
319319
const hasHeicSavings =
320-
originalFile.heic_size !== null && originalFile.conversion_savings > 0;
320+
typeof originalFile.heic_size === 'number' && originalFile.conversion_savings > 0;
321321

322322
const maxSavings = Math.max(
323323
originalFile.minify_savings || 0,

static/app/views/preprod/components/visualizations/appSizeTreemap.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export function AppSizeTreemap(props: AppSizeTreemapProps) {
134134
value: element.size,
135135
path: element.path,
136136
category: element.type,
137+
misc: element.misc,
137138
itemStyle: {
138139
color: 'transparent',
139140
borderColor,
@@ -309,6 +310,9 @@ export function AppSizeTreemap(props: AppSizeTreemapProps) {
309310
const pathElement = params.data?.path
310311
? `<p style="font-size: 12px; margin-bottom: -4px;">${params.data.path}</p>`
311312
: null;
313+
const scaleElement = params.data?.misc?.scale
314+
? `<span style="font-size: 10px; background-color: ${theme.tokens.background.secondary}; color: ${theme.tokens.content.primary}; padding: 4px; border-radius: 3px; font-weight: normal;">@${params.data.misc.scale}x</span>`
315+
: '';
312316

313317
return `
314318
<div style="font-family: Rubik;">
@@ -317,7 +321,10 @@ export function AppSizeTreemap(props: AppSizeTreemapProps) {
317321
<span style="color: ${theme.tokens.content.primary}">${params.data?.category || 'Other'}</span>
318322
</div>
319323
<div style="display: flex; flex-direction: column; line-height: 1; gap: ${theme.space.sm}">
320-
<p style="font-size: 14px; font-weight: bold; margin-bottom: -2px;">${params.name}</p>
324+
<div style="display: flex; align-items: center; gap: 6px;">
325+
<span style="font-size: 14px; font-weight: bold;">${params.name}</span>
326+
${scaleElement}
327+
</div>
321328
${pathElement || ''}
322329
<p style="font-size: 12px; margin-bottom: -4px;">${formatBytesBase10(value)} (${percent}%)</p>
323330
</div>

static/app/views/preprod/types/appSizeTypes.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ export enum SizeAnalysisComparisonState {
2121

2222
export interface SizeAnalysisComparison {
2323
base_size_metric_id: number;
24-
comparison_id: number | null;
25-
error_code: string | null;
26-
error_message: string | null;
2724
head_size_metric_id: number;
2825
identifier: string;
2926
metrics_artifact_type: MetricsArtifactType;
3027
state: SizeAnalysisComparisonState;
28+
comparison_id?: number | null;
29+
error_code?: string | null;
30+
error_message?: string | null;
3131
}
3232

3333
export interface SizeComparisonApiResponse {
@@ -57,12 +57,17 @@ export interface TreemapResults {
5757
root: TreemapElement;
5858
}
5959

60+
interface TreemapElementMisc {
61+
scale?: number;
62+
}
63+
6064
export interface TreemapElement {
6165
children: TreemapElement[];
6266
is_dir: boolean;
6367
name: string;
6468
size: number;
6569
type: TreemapType;
70+
misc?: TreemapElementMisc;
6671
path?: string;
6772
}
6873

@@ -158,14 +163,14 @@ interface LooseImagesInsightResult extends GroupsInsightResult {}
158163
interface MainBinaryExportMetadataResult extends FilesInsightResult {}
159164

160165
export interface OptimizableImageFile {
161-
colorspace: string | null;
162166
conversion_savings: number;
163167
current_size: number;
164168
file_path: string;
165-
heic_size: number | null;
166-
idiom: string | null;
167-
minified_size: number | null;
168169
minify_savings: number;
170+
colorspace?: string | null;
171+
heic_size?: number | null;
172+
idiom?: string | null;
173+
minified_size?: number | null;
169174
}
170175

171176
interface ImageOptimizationInsightResult extends BaseInsightResult {
@@ -222,13 +227,13 @@ export interface InsightResults {
222227
export type DiffType = 'added' | 'removed' | 'increased' | 'decreased';
223228

224229
export interface DiffItem {
225-
base_size: number | null;
226-
head_size: number | null;
227-
item_type: TreemapType | null;
228230
path: string;
229231
size_diff: number;
230232
type: DiffType;
233+
base_size?: number | null;
231234
diff_items?: DiffItem[] | null;
235+
head_size?: number | null;
236+
item_type?: TreemapType | null;
232237
}
233238

234239
// Keep in sync with https://github.com/getsentry/sentry/blob/a85090d7b81832982b43a35c30db9970a0258e99/src/sentry/preprod/models.py#L230
@@ -243,8 +248,8 @@ interface SizeMetricDiffItem {
243248
base_install_size: number;
244249
head_download_size: number;
245250
head_install_size: number;
246-
identifier: string | null;
247251
metrics_artifact_type: MetricsArtifactType;
252+
identifier?: string | null;
248253
}
249254

250255
export type InsightStatus = 'new' | 'resolved' | 'unresolved';

static/app/views/preprod/types/listBuildsTypes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import type {BuildDetailsApiResponse} from './buildDetailsTypes';
33
interface PaginationInfo {
44
has_next: boolean;
55
has_prev: boolean;
6-
next: number | null;
76
page: number;
87
per_page: number;
9-
prev: number | null;
108
total_count: number | string;
9+
next?: number | null;
10+
prev?: number | null;
1111
}
1212

1313
export interface ListBuildsApiResponse {

0 commit comments

Comments
 (0)