Skip to content

Commit 6206821

Browse files
committed
fixes and add visualization challenges
1 parent 86f559d commit 6206821

9 files changed

Lines changed: 191 additions & 31 deletions

File tree

DEVELOPMENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ How to set up your local machine.
4444
```bash
4545
yarn start
4646
```
47-
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
47+
Open [http://localhost:5173](http://localhost:5173) to view it in the browser.
4848
The page will reload if you make edits. You will also see any lint errors in the console.
4949

5050
## Build for Production

py-src/data_formulator/app.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import random
66
import sys
77
import os
8+
import mimetypes
9+
mimetypes.add_type('application/javascript', '.js')
10+
mimetypes.add_type('application/javascript', '.mjs')
811

912
import flask
1013
from flask import Flask, request, send_from_directory, redirect, url_for
@@ -53,13 +56,45 @@
5356
@app.route('/vega-datasets')
5457
def get_example_dataset_list():
5558
dataset_names = vega_data.list_datasets()
56-
example_datasets = ['co2-concentration', 'movies', 'seattle-weather',
57-
'disasters', 'unemployment-across-industries']
59+
example_datasets = [
60+
{"name": "gapminder", "challenges": [
61+
{"text": "Create a line chart to show the life expectancy trend of each country over time.", "difficulty": "easy"},
62+
{"text": "What's the 10 countries with highest life expectancy in 2005?", "difficulty": "medium"},
63+
{"text": "Find top 10 countries that have the biggest difference of life expectancy in 1955 and 2005.", "difficulty": "hard"},
64+
{"text": "Rank countries by their average population per decade. Then only show countries with population over 50 million in 2005.", "difficulty": "hard"}
65+
]},
66+
{"name": "income", "challenges": [
67+
{"text": "Create a line chart to show the income trend of each state over time.", "difficulty": "easy"},
68+
{"text": "Only show washington and california's percentage of population in each income group each year.", "difficulty": "medium"},
69+
{"text": "Find the top 5 states with highest percentage of high income group in 2016.", "difficulty": "hard"}
70+
]},
71+
{"name": "disasters", "challenges": [
72+
{"text": "Create a scatter plot to show the number of death from each disaster type each year.", "difficulty": "easy"},
73+
{"text": "Filter the data and show the number of death caused by flood or drought each year.", "difficulty": "easy"},
74+
{"text": "Create a heatmap to show the total number of death caused by each disaster type each decade.", "difficulty": "hard"},
75+
{"text": "Exclude 'all natural disasters' from the previous chart.", "difficulty": "medium"}
76+
]},
77+
{"name": "movies", "challenges": [
78+
{"text": "Create a scatter plot to show the relationship between budget and worldwide gross.", "difficulty": "easy"},
79+
{"text": "Find the top 10 movies with highest profit after 2000 and visualize them in a bar chart.", "difficulty": "easy"},
80+
{"text": "Visualize the median profit ratio of movies in each genre", "difficulty": "medium"},
81+
{"text": "Create a scatter plot to show the relationship between profit and IMDB rating.", "difficulty": "medium"},
82+
{"text": "Turn the above plot into a heatmap by bucketing IMDB rating and profit, color tiles by the number of movies in each bucket.", "difficulty": "hard"}
83+
]},
84+
{"name": "unemployment-across-industries", "challenges": [
85+
{"text": "Create a scatter plot to show the relationship between unemployment rate and year.", "difficulty": "easy"},
86+
{"text": "Create a line chart to show the average unemployment per year for each industry", "difficulty": "medium"},
87+
{"text": "Find the 5 most stable industries (least change in unemployment rate between 2000 and 2010) and visualize their trend over time using line charts.", "difficulty": "medium"},
88+
{"text": "Create a bar chart to show the unemployment rate change between 2000 and 2010, and highlight the top 5 most stable industries with least change.", "difficulty": "hard"}
89+
]}
90+
]
5891
dataset_info = []
5992
print(dataset_names)
60-
for name in example_datasets:
93+
for dataset in example_datasets:
94+
name = dataset["name"]
95+
challenges = dataset["challenges"]
6196
try:
62-
info_obj = {'name': name, 'snapshot': vega_data(name).to_json(orient='records')}
97+
info_obj = {'name': name, 'challenges': challenges, 'snapshot': vega_data(name).to_json(orient='records')}
6398
dataset_info.append(info_obj)
6499
except:
65100
pass

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "data_formulator"
7-
version = "0.1.3.3"
7+
version = "0.1.3.4"
88

99
requires-python = ">=3.9"
1010
authors = [

src/app/dfSlice.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { getDataTable } from '../views/VisualizationView';
1111
import { findBaseFields } from '../views/ViewUtils';
1212
import { adaptChart, getTriggers, getUrls } from './utils';
1313
import { Type } from '../data/types';
14+
import { TableChallenges } from '../views/TableSelectionView';
1415

1516
enableMapSet();
1617

@@ -34,6 +35,8 @@ export interface DataFormulatorState {
3435

3536
tables : DictTable[];
3637
charts: Chart[];
38+
39+
activeChallenges: {tableId: string, challenges: { text: string; difficulty: 'easy' | 'medium' | 'hard'; }[]}[];
3740

3841
conceptShelfItems: FieldItem[];
3942

@@ -66,6 +69,8 @@ const initialState: DataFormulatorState = {
6669

6770
tables: [],
6871
charts: [],
72+
73+
activeChallenges: [],
6974

7075
conceptShelfItems: [],
7176

@@ -222,6 +227,7 @@ export const dataFormulatorSlice = createSlice({
222227

223228
state.tables = [];
224229
state.charts = [];
230+
state.activeChallenges = [];
225231

226232
state.conceptShelfItems = [];
227233

@@ -248,6 +254,8 @@ export const dataFormulatorSlice = createSlice({
248254
//state.table = undefined;
249255
state.tables = savedState.tables || [];
250256
state.charts = savedState.charts || [];
257+
258+
state.activeChallenges = savedState.activeChallenges || [];
251259

252260
state.conceptShelfItems = savedState.conceptShelfItems || [];
253261

@@ -306,6 +314,9 @@ export const dataFormulatorSlice = createSlice({
306314
// separate this, so that we only delete on tier of table a time
307315
state.charts = state.charts.filter(c => !(c.intermediate && c.intermediate.resultTableId == tableId));
308316
},
317+
addChallenges: (state, action: PayloadAction<{tableId: string, challenges: { text: string; difficulty: 'easy' | 'medium' | 'hard'; }[]}>) => {
318+
state.activeChallenges = [...state.activeChallenges, action.payload];
319+
},
309320
createNewChart: (state, action: PayloadAction<{chartType?: string, tableId?: string}>) => {
310321
let chartType = action.payload.chartType;
311322
let tableId = action.payload.tableId || state.tables[0].id;

src/components/ChartTemplates.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const tablePlots: ChartTemplate[] = [
5555
"chart": "Table",
5656
"icon": chartIconTable,
5757
"template": { },
58-
"channels": ["field 1", "field 2", "field 3", "field 4", "field 5", 'field 6'],
58+
"channels": [], //"field 1", "field 2", "field 3", "field 4", "field 5", 'field 6'
5959
"paths": { }
6060
},
6161
]

src/views/EncodingShelfCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,10 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
433433
newChart.intermediate = undefined;
434434
}
435435

436-
newChart = resolveChartFields(newChart, currentConcepts, refinedGoal, candidateTable);
436+
// there is no need to resolve fields for table chart, just display all fields
437+
if (chart.chartType != "Table") {
438+
newChart = resolveChartFields(newChart, currentConcepts, refinedGoal, candidateTable);
439+
}
437440

438441
dispatch(dfActions.addChart(newChart));
439442
dispatch(dfActions.setFocusedChart(newChart.id));

src/views/MessageSnackbar.tsx

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import IconButton from '@mui/material/IconButton';
88
import CloseIcon from '@mui/icons-material/Close';
99
import { DataFormulatorState, dfActions } from '../app/dfSlice';
1010
import { useDispatch, useSelector } from 'react-redux';
11-
import { Alert, Box, Tooltip, Typography } from '@mui/material';
11+
import { Alert, alpha, Box, Paper, Tooltip, Typography } from '@mui/material';
1212
import InfoIcon from '@mui/icons-material/Info';
13-
13+
import AssignmentIcon from '@mui/icons-material/Assignment';
1414

1515
export interface Message {
1616
type: "success" | "info" | "error",
@@ -22,11 +22,14 @@ export interface Message {
2222

2323
export function MessageSnackbar() {
2424

25+
const challenges = useSelector((state: DataFormulatorState) => state.activeChallenges);
2526
const messages = useSelector((state: DataFormulatorState) => state.messages);
2627
const displayedMessageIdx = useSelector((state: DataFormulatorState) => state.displayedMessageIdx);
2728
const dispatch = useDispatch();
29+
const tables = useSelector((state: DataFormulatorState) => state.tables);
2830

2931
const [open, setOpen] = React.useState(false);
32+
const [openChallenge, setOpenChallenge] = React.useState(true);
3033
const [message, setMessage] = React.useState<Message | undefined>();
3134

3235
React.useEffect(()=>{
@@ -60,13 +63,92 @@ export function MessageSnackbar() {
6063
let timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
6164
let timestamp = message == undefined ? "" : new Date((message as Message).timestamp).toLocaleString('en-US', { timeZone, hour: "2-digit", minute: "2-digit", second: "2-digit" });
6265

66+
console.log(challenges);
67+
let challenge = challenges.find(c => tables.find(t => t.id == c.tableId));
68+
6369
return (
6470
<Box>
71+
<Tooltip placement="right" title="view challenges">
72+
<IconButton
73+
disabled={challenges.length === 0}
74+
sx={{
75+
position: "absolute",
76+
bottom: 56,
77+
right: 0,
78+
animation: challenges.length > 0 ? 'glow 1.5s ease-in-out infinite alternate' : 'none',
79+
'@keyframes glow': {
80+
from: {
81+
boxShadow: '0 0 5px #fff, 0 0 10px #fff, 0 0 15px #1976d2'
82+
},
83+
to: {
84+
boxShadow: '0 0 10px #fff, 0 0 20px #fff, 0 0 30px #1976d2'
85+
}
86+
}
87+
}}
88+
onClick={() => setOpenChallenge(true)}
89+
>
90+
<AssignmentIcon />
91+
</IconButton>
92+
</Tooltip>
6593
<Tooltip placement="right" title="view last message"><IconButton disabled={messages.length == 0} sx={{position: "absolute", bottom: 16, right: 0}}
6694
onClick={()=>{
6795
setOpen(true);
6896
setMessage(messages[messages.length - 1]);
6997
}}><InfoIcon /></IconButton></Tooltip>
98+
{challenge != undefined ? <Snackbar
99+
open={openChallenge}
100+
anchorOrigin={{vertical: 'bottom', horizontal: 'right'}}
101+
sx={{maxWidth: '400px'}}
102+
>
103+
<Paper sx={{
104+
width: '100%',
105+
bgcolor: 'rgb(237,244,251)',
106+
color: 'text.primary',
107+
p: 2,
108+
boxShadow: 1,
109+
borderRadius: 1,
110+
display: 'flex',
111+
flexDirection: 'column'
112+
}}>
113+
<Box sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1}}>
114+
<Typography variant="subtitle1" sx={{fontWeight: 'bold', fontSize: 14}}>
115+
Visualization Challenges for <Box component="span" sx={{fontWeight: 'bold', color: 'primary.main'}}>{challenge.tableId}</Box>
116+
</Typography>
117+
<IconButton
118+
size="small"
119+
aria-label="close"
120+
onClick={() => setOpenChallenge(false)}
121+
>
122+
<CloseIcon fontSize="small" />
123+
</IconButton>
124+
</Box>
125+
<Box sx={{mb: 2}}>
126+
{challenge.challenges.map((ch, j) => (
127+
<Typography
128+
key={j}
129+
variant="body2"
130+
sx={{
131+
fontSize: 12,
132+
marginBottom: 1,
133+
color: ch.difficulty === 'easy'
134+
? '#2e7d32'
135+
: ch.difficulty === 'medium'
136+
? '#f57c00'
137+
: '#d32f2f'
138+
}}
139+
>
140+
<Box
141+
component="span"
142+
sx={{fontWeight: 'bold'}}
143+
>
144+
[{ch.difficulty}]
145+
</Box>
146+
{' '}{ch.text}
147+
</Typography>
148+
))}
149+
</Box>
150+
</Paper>
151+
</Snackbar> : ""}
70152
{message != undefined ? <Snackbar
71153
open={open && message != undefined}
72154
autoHideDuration={message?.type == "error" ? 15000 : 5000}

src/views/SelectableDataGrid.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ export const SelectableDataGrid: React.FC<SelectableDataGridProps> = ({ rows, ta
274274
<DeleteIcon/>
275275
</IconButton>
276276
</Tooltip> */}
277-
278277

279278
<IconButton size="small" color="primary"
280279
onClick={() => {

0 commit comments

Comments
 (0)