# Formatter Design
## Requirements
Take EDP queried data as input, output the following widget responses
- score card
- line chart
there is also two modes to support:
- normal
- comparison(compare metrics between two different time intervals)
## Request
Example 1
```
impressions | performance_datetime | performance_datetime_index | campaign_channel
-------------+----------------------+----------------------------+------------------
839591 | 1648771200 | 0 | Android Push
40149 | 1648771200 | 0 | InApp
125457 | 1648771200 | 0 | iOS Push
1255733 | 1648857600 | 1 | Android Push
52629 | 1648857600 | 1 | InApp
286768 | 1648857600 | 1 | iOS Push
939040 | 1648944000 | 2 | Android Push
52562 | 1648944000 | 2 | InApp
1 | 1648944000 | 2 | InWeb
194820 | 1648944000 | 2 | iOS Push
1230936 | 1649030400 | 3 | Android Push
54023 | 1649030400 | 3 | InApp
232125 | 1649030400 | 3 | iOS Push
1271609 | 1649116800 | 4 | Android Push
43161 | 1649116800 | 4 | InApp
2 | 1649116800 | 4 | InWeb
211339 | 1649116800 | 4 | iOS Push
1076898 | 1649203200 | 5 | Android Push
43513 | 1649203200 | 5 | InApp
178588 | 1649203200 | 5 | iOS Push
1114294 | 1649289600 | 6 | Android Push
38571 | 1649289600 | 6 | InApp
1 | 1649289600 | 6 | InWeb
202949 | 1649289600 | 6 | iOS Push
1147923 | 1649376000 | 7 | Android Push
29250 | 1649376000 | 7 | InApp
793111 | 1649462400 | 8 | Android Push
21037 | 1649462400 | 8 | InApp
884273 | 1649548800 | 9 | Android Push
25554 | 1649548800 | 9 | InApp
```
Example 2:
```
unique_conversions_overall | performance_datetime | performance_datetime_index | channel
----------------------------+----------------------+----------------------------+--------------
355 | 1648771200 | 0 | Android Push
3390 | 1648771200 | 0 | InApp
40 | 1648771200 | 0 | iOS Push
438 | 1648857600 | 1 | Android Push
6019 | 1648857600 | 1 | InApp
29 | 1648857600 | 1 | iOS Push
405 | 1648944000 | 2 | Android Push
4096 | 1648944000 | 2 | InApp
31 | 1648944000 | 2 | iOS Push
535 | 1649030400 | 3 | Android Push
3987 | 1649030400 | 3 | InApp
47 | 1649030400 | 3 | iOS Push
882 | 1649116800 | 4 | Android Push
6386 | 1649116800 | 4 | InApp
33 | 1649116800 | 4 | iOS Push
566 | 1649203200 | 5 | Android Push
4502 | 1649203200 | 5 | InApp
43 | 1649203200 | 5 | iOS Push
558 | 1649289600 | 6 | Android Push
2972 | 1649289600 | 6 | InApp
46 | 1649289600 | 6 | iOS Push
479 | 1649376000 | 7 | Android Push
2939 | 1649376000 | 7 | InApp
361 | 1649462400 | 8 | Android Push
2410 | 1649462400 | 8 | InApp
622 | 1649548800 | 9 | Android Push
3314 | 1649548800 | 9 | InApp
371 | 1649635200 | 10 | Android Push
2128 | 1649635200 | 10 | InApp
```
Questions:
- Column name 包含在 request 內嗎? 或是更廣義的問,request 是以什麼樣的形式傳進 formatter? CSV or JSON 之類的?
- Unifier, EDP driver, composer and formatter 是以什麼樣的形式協作:
- Code level(i.e. different Python modules)
- Service level
- Other ways???
Answers:
- Yes, each row would be a dict from column name to value
- Code level for now.(Update: 2022/05/03, EDP driver -> formatter via HTTP response)
## Response
### Normal
#### Score card
```json=
dataset: {
dimensions: ['Channel'],
source: [
['Android Push', 24600],
['iOS Push', 13800],
['SMS', 34100],
['Web Push', 29000],
['Email', 76200],
['Line', 1500000],
],
}
```
#### Time Series
```json=
{
dataset: {
dimensions: ['date', 'regular', 'trigger', 'in-web', 'in-app', 'journey'],
source: [
[unix_timestamp_in_sec, 12, 55, 66, 2],
[unix_timestamp_in_sec, 6, 16, 23, 1],
[unix_timestamp_in_sec, 8, 30, 74, 7],
[unix_timestamp_in_sec, 90, 2, 52, 3],
[unix_timestamp_in_sec, 100, 40, 59, 11],
]
},
series: [
{type: 'line'},
{type: 'line'},
{type: 'line'},
{type: 'line'}
],
}
```
### Comparison
#### Score card
```json=
dataset: {
dimensions: ['Channel'],
source: [
['Android Push', 24600],
['iOS Push',13800]
['SMS', 34100],
['Web Push', 29000],
['Email', 76200],
['Line', 1500000],
],
comparison: {
'Android Push': {
uplift: 0.2,
},
...
}
}
```
#### Time Series
```json=
{
dataset: {
dimensions: ['date', 'conversion', , 'Previous Period'],
source: [
[1646092800, 12, 3],
[1646179200, 6, 2],
[1646265600, 8, 94],
[1646352000, 90, 38],
[1646438400, 121, 58],
[1646524800, 100, 48],
[1646611200, 30, NaN]
],
comparison: {
// the data point of 1646092800 is mapped to 1645488000
1646092800: {
previous_time: 1645488000,
uplift: 0.2,
},
...
}
},
series: [
{type: 'line'},
{type: 'line', is_comparison_mode: true},
],
}
```
## Pseudo Code
### Normal & Comparison
```python
DataRow = Tuple[Union[str, float]]
Dims = Tuple[str]
def interpolation(data: List[DataRow],
beg_time: int,
eng_time: int,
interval: int):
pass
def convert_query_result_to_widget_format(data: List[DataRow],
dims: Dims,
list_prev_dims: Optional[List[Dims]]
datetime_option: Optional[Dict]
):
"""
data -- EDP queried result, e.g. [(839591, 1648771200, 'Android Push'),
(40149, 1648771200, 'InApp')]
dims -- Column(Dimension) IDs, e.g. ('impression', 'datetime', 'campaign_type')
list_prev_dims -- Specified dims for previous periods, would be used by comparison mode.
datetime_option -- Specified time range and interval in second(usually dim 0 if exist?), e.g. {
'datetime_range': [1648771200, 1648794300],
'interval': 43200
}
"""
# 1. interpolation
if datetime_option:
data = interpolation(data,
datetime_option['datetime_range'][0],
datetime_option['datetime_range'][1],
datetime_option['interval'],
)
# 2. real formatting work
pass
```