Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Commit

Permalink
Consumption of timezone offset and adjustment of datelabels (#230)
Browse files Browse the repository at this point in the history
* Seems to work, txs in boundary situations get located at either side of the date label. Tests are needed

* Added test for timestamp label for different timezone
  • Loading branch information
jpalvarezl authored Jan 19, 2021
1 parent 16aeeea commit f8d2028
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 13 deletions.
128 changes: 123 additions & 5 deletions src/services/tests/transactions_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,9 @@ fn backend_txs_to_summary_txs_with_values() {
#[test]
fn service_txs_to_tx_list_items_empty() {
let service_tx: Vec<TransactionSummary> = vec![];
let berlin_timezone_offset = 3600;

let actual = service_txs_to_tx_list_items(service_tx, -1).unwrap();
let actual = service_txs_to_tx_list_items(service_tx, -1, berlin_timezone_offset).unwrap();

assert_eq!(actual.is_empty(), true);
}
Expand All @@ -241,6 +242,7 @@ fn service_txs_to_tx_list_items_last_timestamp_undefined() {
mock_info_provider.expect_token_info().times(0);
let service_txs = get_service_txs(&mut mock_info_provider);
let service_txs_copy = get_service_txs(&mut mock_info_provider);
let berlin_timezone_offset = 3600;

let mut service_txs_inter = service_txs.into_iter();

Expand Down Expand Up @@ -277,13 +279,15 @@ fn service_txs_to_tx_list_items_last_timestamp_undefined() {
},
];

let actual = service_txs_to_tx_list_items(service_txs_copy, -1).unwrap();
let actual =
service_txs_to_tx_list_items(service_txs_copy, -1, berlin_timezone_offset).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn service_txs_to_tx_list_items_last_timestamp_defined_but_different() {
let last_timestamp = 1606867200000;
let berlin_timezone_offset = 3600;

let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
Expand Down Expand Up @@ -326,13 +330,16 @@ fn service_txs_to_tx_list_items_last_timestamp_defined_but_different() {
},
];

let actual = service_txs_to_tx_list_items(service_txs_copy, last_timestamp).unwrap();
let actual =
service_txs_to_tx_list_items(service_txs_copy, last_timestamp, berlin_timezone_offset)
.unwrap();
assert_eq!(expected, actual);
}

#[test]
fn service_txs_to_tx_list_items_last_timestamp_defined_and_same() {
let last_timestamp = 1606780800000;
let berlin_timezone_offset = 3600;

let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
Expand Down Expand Up @@ -372,13 +379,120 @@ fn service_txs_to_tx_list_items_last_timestamp_defined_and_same() {
},
];

let actual = service_txs_to_tx_list_items(service_txs_copy, last_timestamp).unwrap();
let actual =
service_txs_to_tx_list_items(service_txs_copy, last_timestamp, berlin_timezone_offset)
.unwrap();
assert_eq!(expected, actual);
}

#[test]
fn service_txs_to_tx_list_items_date_label_berlin_timezone() {
let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
mock_info_provider.expect_token_info().times(0);

let service_txs = get_service_txs(&mut mock_info_provider);
let service_txs_copy = get_service_txs(&mut mock_info_provider);
let berlin_timezone_offset = 3600; // + 1 hours

let mut service_txs_inter = service_txs.into_iter();

let expected = vec![
TransactionListItem::DateLabel {
timestamp: 1606780800000,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::DateLabel {
timestamp: 1606694400000,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
];

let actual =
service_txs_to_tx_list_items(service_txs_copy, -1, berlin_timezone_offset).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn service_txs_to_tx_list_items_date_label_someplace_else_timezone() {
let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
mock_info_provider.expect_token_info().times(0);

let service_txs = get_service_txs(&mut mock_info_provider);
let service_txs_copy = get_service_txs(&mut mock_info_provider);
let berlin_timezone_offset = 39600; // + 11 hours Melbourne/Australia

let mut service_txs_inter = service_txs.into_iter();

let expected = vec![
TransactionListItem::DateLabel {
timestamp: 1606867200000, // 2020/12/02
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::DateLabel {
timestamp: 1606780800000, // 2020/12/01
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
TransactionListItem::DateLabel {
timestamp: 1606694400000, // 2020/11/30
},
TransactionListItem::Transaction {
transaction: service_txs_inter.next().unwrap(),
conflict_type: ConflictType::None,
},
];

let actual =
service_txs_to_tx_list_items(service_txs_copy, -1, berlin_timezone_offset).unwrap();

assert_eq!(expected, actual);
}

#[test]
#[should_panic]
fn peek_timestamp_and_remove_item_empty() {
let berlin_timezone_offset = 3600;
let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
mock_info_provider.expect_token_info().times(0);
Expand All @@ -389,13 +503,15 @@ fn peek_timestamp_and_remove_item_empty() {
&mut backend_txs_iter,
&mut mock_info_provider,
"0x1230B3d59858296A31053C1b8562Ecf89A2f888b",
berlin_timezone_offset,
)
.unwrap();
}

#[test]
fn peek_timestamp_and_remove_item_with_items() {
let expected_timestamp = 1606780800000;
let berlin_timezone_offset = 3600;

let mut mock_info_provider = MockInfoProvider::new();
mock_info_provider.expect_safe_info().times(0);
Expand All @@ -410,6 +526,7 @@ fn peek_timestamp_and_remove_item_with_items() {
&mut backend_txs_iter,
&mut mock_info_provider,
"0x1230B3d59858296A31053C1b8562Ecf89A2f888b",
berlin_timezone_offset,
)
.unwrap();

Expand All @@ -419,8 +536,9 @@ fn peek_timestamp_and_remove_item_with_items() {
#[test]
fn get_day_timestamp_millis_for_02_12_2020_00_00_01() {
let input = 1606867201000; // 1 second past the 2nd of December 2020 UTC
let berlin_timezone_offset = 3600;

let actual = get_day_timestamp_millis(input);
let actual = get_day_timestamp_millis(input, berlin_timezone_offset);
let expected = 1606867200000;

assert_eq!(expected, actual);
Expand Down
31 changes: 23 additions & 8 deletions src/services/transactions_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::utils::cache::CacheExt;
use crate::utils::context::Context;
use crate::utils::errors::ApiResult;
use anyhow::Result;
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, Utc};
use chrono::{DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, Utc};
use itertools::Itertools;

pub fn get_history_transactions(
Expand All @@ -23,6 +23,11 @@ pub fn get_history_transactions(
timezone_offset: &Option<String>,
) -> ApiResult<Page<TransactionListItem>> {
let mut info_provider = DefaultInfoProvider::new(context);
let request_timezone_offset = timezone_offset
.as_ref()
.and_then(|it| it.parse::<i32>().ok())
.unwrap_or(0)
/ 1000;

let incoming_page_metadata =
PageMetadata::from_url_string(page_url.as_ref().unwrap_or(&"".to_string()));
Expand All @@ -33,8 +38,13 @@ pub fn get_history_transactions(
let backend_paged_txs = fetch_backend_paged_txs(context, safe_address, &extended_page_url)?;
let mut backend_txs_iter = backend_paged_txs.results.into_iter();
let prev_page_timestamp = if page_metadata.offset != 0 {
peek_timestamp_and_remove_item(&mut backend_txs_iter, &mut info_provider, safe_address)
.unwrap_or(-1)
peek_timestamp_and_remove_item(
&mut backend_txs_iter,
&mut info_provider,
safe_address,
request_timezone_offset,
)
.unwrap_or(-1)
} else {
-1
};
Expand All @@ -46,7 +56,8 @@ pub fn get_history_transactions(
service_txs.push(creation_tx);
}

let tx_list_items = service_txs_to_tx_list_items(service_txs, prev_page_timestamp)?;
let tx_list_items =
service_txs_to_tx_list_items(service_txs, prev_page_timestamp, request_timezone_offset)?;

Ok(Page {
next: build_page_url(
Expand Down Expand Up @@ -138,11 +149,12 @@ pub(super) fn backend_txs_to_summary_txs(
pub(super) fn service_txs_to_tx_list_items(
txs: Vec<TransactionSummary>,
last_timestamp: i64,
timezone_offset: i32,
) -> Result<Vec<TransactionListItem>> {
let mut tx_list_items = Vec::new();
for (date_timestamp, transaction_group) in &txs
.into_iter()
.group_by(|transaction| get_day_timestamp_millis(transaction.timestamp))
.group_by(|transaction| get_day_timestamp_millis(transaction.timestamp, timezone_offset))
{
if date_timestamp != last_timestamp {
tx_list_items.push(TransactionListItem::DateLabel {
Expand All @@ -163,6 +175,7 @@ pub(super) fn peek_timestamp_and_remove_item(
transactions: &mut dyn Iterator<Item = Transaction>,
info_provider: &mut dyn InfoProvider,
safe_address: &str,
timezone_offset: i32,
) -> Result<i64> {
let timestamp = transactions
.next()
Expand All @@ -172,14 +185,16 @@ pub(super) fn peek_timestamp_and_remove_item(
.ok_or(anyhow::anyhow!("empty transactions"))?
.timestamp;

Ok(get_day_timestamp_millis(timestamp))
Ok(get_day_timestamp_millis(timestamp, timezone_offset))
}

pub(super) fn get_day_timestamp_millis(timestamp_in_millis: i64) -> i64 {
pub(super) fn get_day_timestamp_millis(timestamp_in_millis: i64, timezone_offset: i32) -> i64 {
let date_time = DateTime::<Utc>::from_utc(
NaiveDateTime::from_timestamp(timestamp_in_millis / 1000, 0),
Utc,
);
)
.with_timezone(&FixedOffset::east(timezone_offset));

let date =
NaiveDate::from_ymd_opt(date_time.year(), date_time.month(), date_time.day()).unwrap();
date.and_hms_milli(0, 0, 0, 0).timestamp() * 1000
Expand Down

0 comments on commit f8d2028

Please sign in to comment.