diff --git a/.gitignore b/.gitignore index fa8add3..7e254c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ node_modules/ -your.db -data-export/oldfacebook.ts -OUTTEST +*.db +your.csv .gitSAFE -out.manifest -test.manifest \ No newline at end of file +*.DELETE-THIS-HAS-PII \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..88eccb3 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# base-data-manager + +A Typescript project for parsing through many types of data exports to tabular formats + +** This is heavily WIP, and mostly just a toy for myself ** + +### Installation + +* Install `jq` +* Install sqlite `csv.so` extension (Hardcoded to `/home/cobertos/sqlite-files/` currently) +* Install `node` + `pnpm i` +* See `main.ts` for current example usage + + +### Proposed Architecture + +The architecture runs in 2 steps. + +The first step is unopinionated in it's output format. It's meant to take the source data exactly as-is and output it as csv. All source data should pass through, but will be normalized in csv + +**TODO: It's not completely unopinionated, there is some normalization for names of columns I think we want to apply? Or maybe we apply that later...** + +An optional second step combines everything into a single SQLite database. From here we normalize many different types of data across multiple exports into a single opinionated output. For example, message threads/channels should all have the same table format, or end up in the same table + +**TODO: No idea if the second part should be a part of this project... but it currently is** diff --git a/data-export/discord-chat-exporter.ts b/data-export/discord-chat-exporter.ts new file mode 100644 index 0000000..ddfb673 --- /dev/null +++ b/data-export/discord-chat-exporter.ts @@ -0,0 +1,69 @@ +import { pipe, each, cmd, assignMeta, glob, read, branchGen, type PipelineOp } from "./task.ts"; + +/** + * Extracts the channel ID from the filename, e.g. + * "GuildName - Text Channels - ChannelName [0000000000000000].json" → "0000000000000000" + */ +function chatExporterChannelId(t: { path: string }): string { + const match = t.path.match(/\[([^\]]+)\]\.json$/); + return match?.[1] ?? t.path.split('/').pop()!; +} + +/** + * Channel metadata aggregate + messages table, one pair per exported JSON file. + * Unlike the native Discord export, DiscordChatExporter captures ALL authors' messages. + */ +function discord_chat_exporter_messages(): PipelineOp { + return branchGen(function* () { + // Channel-level metadata aggregated into a single table + yield pipe( + glob(`*.json`), + assignMeta({ idValue: t => `DiscordCE - Channel ${chatExporterChannelId(t)}` }), + read(), + each(t => t.clone().cmd(["jq", "-r", ` + ["${t.id}", .guild.name, .channel.name, .channel.type, (.channel.category // ""), (.channel.topic // ""), .messageCount] + | @csv + `])), + assignMeta({ + aggregate: true, + aggregateColumns: ["id", "guild_name", "channel_name", "channel_type", "channel_category", "channel_topic", "message_count"], + idValue: "DiscordCE - Messages Meta", + }) + ); + + // The messages — one table per exported file + yield pipe( + glob(`*.json`), + assignMeta({ idValue: t => `DiscordCE - Messages ${chatExporterChannelId(t)}` }), + read(), + cmd(["jq", "-r", ` + ["id", "timestamp", "author", "discriminator", "content", "attachment"], + ( + .messages[] + | [ + .id, + .timestamp, + .author.name, + (.author.discriminator // ""), + .content, + (.attachments[0].url // "") + ] + ) + | @csv + `]), + assignMeta({ + metaIdValue: "DiscordCE - Messages Meta", + columnMeta: ["any", "isodatetime", "sender", "any", "text", "url"], + perRowDescription: '"{4}" from {2} at {1}', + perRowTags: "discord,message", + }) + ); + }); +} + +export function discord_chat_exporter(): PipelineOp { + return pipe( + assignMeta({ idValue: t => `DiscordCE - ${t.basename}` }), + discord_chat_exporter_messages() + ); +} diff --git a/data-export/discord.ts b/data-export/discord.ts new file mode 100644 index 0000000..77b4d24 --- /dev/null +++ b/data-export/discord.ts @@ -0,0 +1,201 @@ +import { pipe, each, cmd, assignMeta, cd, glob, read, branchGen, type PipelineOp } from "./task.ts"; + +/** Extracts the channel ID directory name from paths like messages/{channelId}/messages.csv */ +function discordChannelId(t: { path: string }): string { + return t.path.split('/').slice(-2, -1)[0]; +} + +/** Linked third-party accounts (Steam, Twitch, etc.) from account/user.json */ +function discord_connections(): PipelineOp { + return pipe( + cmd(["jq", "-r", ` + ["type", "name", "id", "verified", "visibility"], + ( + .connections[]? + | [.type, .name, .id, .verified, .visibility] + ) + | @csv + `]), + assignMeta({ + idValue: "Discord - Connections", + columnMeta: ["text", "text", "any", "any", "any"], + perRowDescription: '{0} account "{1}"', + perRowTags: "discord", + }) + ); +} + +/** Friends, blocked users, and other relationships from account/user.json */ +function discord_relationships(): PipelineOp { + return pipe( + cmd(["jq", "-r", ` + ["username", "discriminator", "type"], + ( + .relationships[]? + | [.user.username, .user.discriminator, .type] + ) + | @csv + `]), + assignMeta({ + idValue: "Discord - Relationships", + columnMeta: ["text", "any", "any"], + perRowDescription: '{0}#{1} (relationship type {2})', + perRowTags: "discord", + }) + ); +} + +/** Purchase history from account/user.json */ +function discord_payments(): PipelineOp { + return pipe( + cmd(["jq", "-r", ` + ["created_at", "description", "amount", "currency", "status"], + ( + .payments[]? + | [.created_at, .description, .amount, .currency, .status] + ) + | @csv + `]), + assignMeta({ + idValue: "Discord - Payments", + columnMeta: ["isodatetime", "text", "numeric", "text", "any"], + perRowDescription: '{1}: {2} {3} on {0}', + perRowTags: "discord,payment", + }) + ); +} + +/** Application/game play-time statistics from account/user.json */ +function discord_activity_stats(): PipelineOp { + return pipe( + cmd(["jq", "-r", ` + ["application_id", "last_played_at", "total_duration"], + ( + .user_activity_application_statistics[]? + | [.application_id, .last_played_at, .total_duration] + ) + | @csv + `]), + assignMeta({ + idValue: "Discord - Activity Stats", + columnMeta: ["any", "isodatetime", "numeric"], + perRowDescription: 'App {0}: {2}s played, last at {1}', + perRowTags: "discord", + }) + ); +} + +/** + * Activity event logs from activity/{subdir}/events-*.json (NDJSON format). + * Each subdirectory (analytics, modeling, reporting, tns) becomes its own table. + * Fields chosen for what the user did: event type, when, where (channel/guild), + * which message, which game, and human-readable channel/guild names when available. + */ +function discord_activity_events(): PipelineOp { + return pipe( + glob(`activity/*/events-*.json`), + assignMeta({ idValue: t => `Discord - Activity ${t.path.split('/').slice(-2, -1)[0]}` }), + read(), + // NDJSON: use -n + inputs so jq processes all lines, emitting one header then N rows + cmd(["jq", "-rn", ` + ["event_type", "timestamp", "channel_id", "guild_id", "message_id", "game_name", "channel_name", "guild_name"], + ( + inputs + | [ + .event_type, + .timestamp, + (.channel_id // ""), + (.guild_id // ""), + (.message_id // ""), + (.game_name // ""), + (.channel_name // ""), + (.guild_name // "") + ] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime", "any", "any", "any", "text", "text", "text"], + perRowDescription: '{0} at {1}', + perRowTags: "discord,activity", + }) + ); +} + +/** Notes the user wrote on other users, keyed by user ID, from account/user.json */ +function discord_notes(): PipelineOp { + return pipe( + cmd(["jq", "-r", ` + ["user_id", "note"], + ( + .notes // {} + | to_entries[] + | [.key, .value] + ) + | @csv + `]), + assignMeta({ + idValue: "Discord - Notes", + columnMeta: ["any", "text"], + perRowDescription: 'Note on {0}: "{1}"', + perRowTags: "discord", + }) + ); +} + +/** + * Messages from messages/{channelId}/messages.csv and channel metadata from + * messages/{channelId}/channel.json. + * NOTE: The export only contains the exporting user's own messages. + */ +function discord_messages(): PipelineOp { + return branchGen(function* () { + // Channel-level metadata aggregated into a single table + yield pipe( + glob(`messages/*/channel.json`), + assignMeta({ idValue: t => `Discord - Channel ${discordChannelId(t)}` }), + read(), + each(t => t.clone().cmd(["jq", "-r", ` + ["${t.id}", .type, (.name // ""), (.guild.id // ""), (.guild.name // ""), ((.recipients // []) | join(","))] + | @csv + `])), + assignMeta({ + aggregate: true, + aggregateColumns: ["id", "type", "name", "guild_id", "guild_name", "recipients"], + idValue: "Discord - Messages Meta", + }) + ); + + // The messages themselves — one table per channel + yield pipe( + glob(`messages/*/messages.csv`), + assignMeta({ idValue: t => `Discord - Messages ${discordChannelId(t)}` }), + read(), + // Normalize the header row to lowercase names + cmd(["sed", "-e", "1s/.*/id,timestamp,content,attachment/"]), + assignMeta({ + metaIdValue: "Discord - Messages Meta", + columnMeta: ["any", "isodatetime", "text", "url"], + perRowDescription: '"{2}" at {1}', + perRowTags: "discord,message,content_by_me", + }) + ); + }); +} + +export function discord(): PipelineOp { + return pipe( + assignMeta({ idValue: t => `Discord - ${t.basename}` }), + branchGen(function* () { + yield discord_messages(); + + yield pipe(cd(`account/user.json`), read(), discord_connections()); + yield pipe(cd(`account/user.json`), read(), discord_relationships()); + yield pipe(cd(`account/user.json`), read(), discord_payments()); + yield pipe(cd(`account/user.json`), read(), discord_activity_stats()); + yield pipe(cd(`account/user.json`), read(), discord_notes()); + + yield discord_activity_events(); + }) + ); +} diff --git a/data-export/facebook.ts b/data-export/facebook.ts index cb0d656..54b5cae 100644 --- a/data-export/facebook.ts +++ b/data-export/facebook.ts @@ -1,158 +1,99 @@ -import { TaskTargetPipelineHelper } from "./task.ts"; - -declare module "../data-export/task.ts" { - interface TaskTargetPipelineHelper { - facebook: typeof facebook; - facebook_v2: typeof facebook_v2; - facebook_notifications_generic: typeof facebook_notifications_generic; - facebook_notifications_v1: typeof facebook_notifications_v1; - facebook_notifications_v2: typeof facebook_notifications_v2; - facebook_installed_apps_generic: typeof facebook_installed_apps_generic; - facebook_installed_apps_v1: typeof facebook_installed_apps_v1; - facebook_installed_apps_v2: typeof facebook_installed_apps_v2; - facebook_comments_generic: typeof facebook_comments_generic; - facebook_comments_v1: typeof facebook_comments_v1; - facebook_comments_v2: typeof facebook_comments_v2; - facebook_people_interactions_generic: typeof facebook_people_interactions_generic; - facebook_people_interactions_v1: typeof facebook_people_interactions_v1; - facebook_people_interactions_v2: typeof facebook_people_interactions_v2; - facebook_marketplace_items_sold_generic: typeof facebook_marketplace_items_sold_generic; - facebook_marketplace_items_sold_v1: typeof facebook_marketplace_items_sold_v1; - facebook_marketplace_items_sold_v2: typeof facebook_marketplace_items_sold_v2; - facebook_searches_generic: typeof facebook_searches_generic; - facebook_searches_v1: typeof facebook_searches_v1; - facebook_searches_v2: typeof facebook_searches_v2; - facebook_account_activity_generic: typeof facebook_account_activity_generic; - facebook_account_activity_v1: typeof facebook_account_activity_v1; - facebook_account_activity_v2: typeof facebook_account_activity_v2; - facebook_messages_generic: typeof facebook_messages_generic; - facebook_friends_generic: typeof facebook_friends_generic; - facebook_admin_records_generic: typeof facebook_admin_records_generic; - facebook_admin_records_v1: typeof facebook_admin_records_v1; - facebook_admin_records_v2: typeof facebook_admin_records_v2; - facebook_authorized_logins_generic: typeof facebook_authorized_logins_generic; - facebook_authorized_logins_v1: typeof facebook_authorized_logins_v1; - facebook_authorized_logins_v2: typeof facebook_authorized_logins_v2; - facebook_contact_verification_generic: typeof facebook_contact_verification_generic; - facebook_contact_verification_v1: typeof facebook_contact_verification_v1; - facebook_contact_verification_v2: typeof facebook_contact_verification_v2; - facebook_pages_unfollowed_generic: typeof facebook_pages_unfollowed_generic; - facebook_pages_unfollowed_v1: typeof facebook_pages_unfollowed_v1; - facebook_pages_unfollowed_v2: typeof facebook_pages_unfollowed_v2; - facebook_account_accesses_generic: typeof facebook_account_accesses_generic; - facebook_account_accesses_v1: typeof facebook_account_accesses_v1; - facebook_account_accesses_v2: typeof facebook_account_accesses_v2; - facebook_groups_joined_generic: typeof facebook_groups_joined_generic; - facebook_groups_joined_v1: typeof facebook_groups_joined_v1; - facebook_groups_joined_v2: typeof facebook_groups_joined_v2; - facebook_group_posts_v1: typeof facebook_group_posts_v1; - facebook_group_posts_v2: typeof facebook_group_posts_v2; - } -} - -Object.assign(TaskTargetPipelineHelper.prototype, { - facebook, - facebook_v2, - facebook_notifications_generic, - facebook_notifications_v1, - facebook_notifications_v2, - facebook_installed_apps_generic, - facebook_installed_apps_v1, - facebook_installed_apps_v2, - facebook_comments_generic, - facebook_comments_v1, - facebook_comments_v2, - facebook_people_interactions_generic, - facebook_people_interactions_v1, - facebook_people_interactions_v2, - facebook_marketplace_items_sold_generic, - facebook_marketplace_items_sold_v1, - facebook_marketplace_items_sold_v2, - facebook_searches_generic, - facebook_searches_v1, - facebook_searches_v2, - facebook_account_activity_generic, - facebook_account_activity_v1, - facebook_account_activity_v2, - facebook_admin_records_generic, - facebook_admin_records_v1, - facebook_admin_records_v2, - facebook_authorized_logins_generic, - facebook_authorized_logins_v1, - facebook_authorized_logins_v2, - facebook_contact_verification_generic, - facebook_contact_verification_v1, - facebook_contact_verification_v2, - facebook_account_accesses_generic, - facebook_account_accesses_v1, - facebook_account_accesses_v2, - facebook_pages_unfollowed_generic, - facebook_pages_unfollowed_v1, - facebook_pages_unfollowed_v2, - facebook_groups_joined_generic, - facebook_groups_joined_v1, - facebook_groups_joined_v2, - facebook_messages_generic, - facebook_friends_generic, - facebook_group_posts_v1, - facebook_group_posts_v2, -}); +import { pipe, branch, each, cmd, assignMeta, cd, glob, read, branchGen, type PipelineOp } from "./task.ts"; /**Parses about_you/notifications.json in the old format * or logged_information/notifications.json in the new format*/ -function facebook_notifications_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", `["timestamp","unread","href","text"], - ( - .${prop}[] - | [(.timestamp | todateiso8601), .unread, .href, .text] - ) - | @csv`]) - .types(["time", "text", "text", "text"]); +function facebook_notifications_generic(prop: string): PipelineOp { + return pipe( + cmd(["jq", "-r", `["timestamp","unread","href","text"], + ( + .${prop}[] + | [(.timestamp | todateiso8601), .unread, .href, .text] + ) + | @csv`]), + assignMeta({ + columnMeta: ["isodatetime", "any", "url", "text"], + perRowDescription: 'Notification at {0}: "{3}"', + perRowTags: "facebook,initiated_by_third_party", + }) + ); } -function facebook_notifications_v1(this: TaskTargetPipelineHelper) { - return this.facebook_notifications_generic("notifications"); +function facebook_notifications_v1(): PipelineOp { + return facebook_notifications_generic("notifications"); } -function facebook_notifications_v2(this: TaskTargetPipelineHelper) { - return this.facebook_notifications_generic("notifications_v2"); +function facebook_notifications_v2(): PipelineOp { + return facebook_notifications_generic("notifications_v2"); } /**Installed apps*/ -function facebook_installed_apps_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` +function facebook_installed_apps_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` ["name","added_timestamp"], ( .${prop}[] | [.name, (.added_timestamp | todateiso8601)] ) | @csv - `]) - .types(["text", "time"]); + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'App "{0}" added on {1}', + perRowTags: "facebook", + }) + ); } -function facebook_installed_apps_v1(this: TaskTargetPipelineHelper) { - return this.facebook_installed_apps_generic("installed_apps"); +function facebook_installed_apps_v1() { + return facebook_installed_apps_generic("installed_apps"); } -function facebook_installed_apps_v2(this: TaskTargetPipelineHelper) { +function facebook_installed_apps_v2() { // TODO: There's a few more properties in here for v2 - return this.facebook_installed_apps_generic("installed_apps_v2"); + return facebook_installed_apps_generic("installed_apps_v2"); } -function facebook_messages_generic(this: TaskTargetPipelineHelper) { - // This most assuredly does not handle certain things like pictures and such - // There are messages .type and then they have other thing in them? - // there's also is_unsent: false - return this.cmd(["jq", "-r", ` - ["from","to","timestamp","content"], - ( - .messages[] - | [.sender_name, "", ((.timestamp_ms / 1000) | round | todateiso8601), .content] - ) - | @csv - `]) +function facebook_messages_generic() { + return branchGen(function*(){ + // This most assuredly does not handle certain things like pictures and such + // There are messages .type and then they have other thing in them? + + // Conversation-level information aggregated into a single place + // TODO: This will result in MULTIPLE rows for a single thread if there is multiple .jsons for a single + // chat in one directory. Ughhhhhhhhhhhhhhh. For now this is just a limiation + yield pipe( + each(t => + t.clone().cmd(["jq", "-r", ` + ["${t.id}", .title, .is_still_participant, .thread_type, .thread_path, (.participants | map(.name) | join(", "))] + | @csv + `]) + ), + assignMeta({ + aggregate: true, + aggregateColumns: ["id", "title", "is_still_participant", "thread_type", "thread_path", "participants"], + idValue: "Facebook - Messages Meta", + }) + ); + // The conversation itself + yield pipe( + cmd(["jq", "-r", ` + ["from","to","timestamp","content"], + ( + .messages[] + | [.sender_name, "", ((.timestamp_ms / 1000) | round | todateiso8601), .content] + ) + | @csv + `]), + assignMeta({ + metaIdValue: "Facebook - Messages Meta", + columnMeta: ["sender", "receiver", "isodatetime", "text"], + perRowDescription: '"{3}" from {0} at {2}', + perRowTags: "facebook,message", + }) + ); + }); } /**Comments*/ -function facebook_comments_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_comments_generic(prop: string) { // TODO: .data is an array that has items, but usually just one // "data": [ // { @@ -165,623 +106,837 @@ function facebook_comments_generic(this: TaskTargetPipelineHelper, prop: string) // } // ], // TODO: there's also attachments (media) - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["timestamp","data", "title"], ( .${prop}[]? | [(.timestamp | todateiso8601), "TODO", .title] ) | @csv - `]) - .types(["time", "text", "text"]) + `]), + assignMeta({ + columnMeta: ["isodatetime", "TODO", "text"], + perRowDescription: 'Comment on "{2}" at {0}', + perRowTags: "facebook", + }) + ); } -function facebook_comments_v1(this: TaskTargetPipelineHelper) { - return this.facebook_comments_generic("comments"); +function facebook_comments_v1() { + return facebook_comments_generic("comments"); } -function facebook_comments_v2(this: TaskTargetPipelineHelper) { +function facebook_comments_v2() { // TODO: I don't see any difference between v1 and v2? Perhaps it's in the data? - return this.facebook_comments_generic("comments_v2"); + return facebook_comments_generic("comments_v2"); } -function facebook_friends_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` - ["name", "timestamp"], - ( - .${prop}[] - | [.name, (.timestamp | todateiso8601)] - ) - | @csv - `]); +function facebook_friends_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` + ["name", "timestamp"], + ( + .${prop}[] + | [.name, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: '{0} at {1}', + perRowTags: "facebook", + }) + ); } -function facebook_people_interactions_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` - ["name", "uri", "timestamp"], - ( - .${prop}[].entries[] - | [.data.name, .data.uri, (.timestamp | todateiso8601)] - ) - | @csv - `]) +function facebook_people_interactions_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` + ["name", "uri", "timestamp"], + ( + .${prop}[].entries[] + | [.data.name, .data.uri, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "url", "isodatetime"], + perRowDescription: 'Interaction with {0} at {2}', + perRowTags: "facebook", + }) + ); } -function facebook_people_interactions_v1(this: TaskTargetPipelineHelper) { - return this.facebook_people_interactions_generic("people_interactions"); +function facebook_people_interactions_v1() { + return facebook_people_interactions_generic("people_interactions"); } -function facebook_people_interactions_v2(this: TaskTargetPipelineHelper) { - return this.facebook_people_interactions_generic("people_interactions_v2"); +function facebook_people_interactions_v2() { + return facebook_people_interactions_generic("people_interactions_v2"); } -function facebook_marketplace_items_sold_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_marketplace_items_sold_generic(prop: string) { // TODO: Updated_timestamp may not exist so it's removed for now - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title", "price", "seller", "created_timestamp", "latitude", "longitude", "description"], ( .${prop}[] | [.title, .price, .seller, (.created_timestamp | todateiso8601), .location.coordinate.latitude, .location.coordinate.longitude, .description] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "numeric", "sender", "isodatetime", "lat", "lng", "text"], + perRowDescription: 'Sold "{0}" for {1} on {3}', + perRowTags: "facebook,marketplace", + }) + ); } -function facebook_marketplace_items_sold_v1(this: TaskTargetPipelineHelper) { - return this.facebook_marketplace_items_sold_generic("items_selling"); +function facebook_marketplace_items_sold_v1() { + return facebook_marketplace_items_sold_generic("items_selling"); } -function facebook_marketplace_items_sold_v2(this: TaskTargetPipelineHelper) { - return this.facebook_marketplace_items_sold_generic("items_selling_v2"); +function facebook_marketplace_items_sold_v2() { + return facebook_marketplace_items_sold_generic("items_selling_v2"); } -function facebook_searches_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_searches_generic(prop: string) { // TODO: Data and attachments, both only contain one "text" field inside the // first object of the array... Same data, do they ever differ? - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title","data","timestamp"], ( .${prop}[] | [.title, .data[0].text, (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "text", "isodatetime"], + perRowDescription: 'Searched for "{1}" at {2}', + perRowTags: "facebook,initiated_by_me,content_by_me", + }) + ); } -function facebook_searches_v1(this: TaskTargetPipelineHelper) { - return this.facebook_searches_generic("searches"); +function facebook_searches_v1() { + return facebook_searches_generic("searches"); } -function facebook_searches_v2(this: TaskTargetPipelineHelper) { - return this.facebook_searches_generic("searches_v2"); +function facebook_searches_v2() { + return facebook_searches_generic("searches_v2"); } -function facebook_account_activity_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` +function facebook_account_activity_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` ["action", "ip", "user_agent", "datr_cookie", "city", "region", "country", "site_name","timestamp"], ( .${prop}[] | [.action, .ip_address, .user_agent, .datr_cookie, .city, .region, .country, .site_name, (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "text", "text", "text", "text", "text", "text", "text", "isodatetime"], + perRowDescription: '{0} from {4}, {6} on {8}', + perRowTags: "facebook,security", + }) + ); } -function facebook_account_activity_v1(this: TaskTargetPipelineHelper) { - return this.facebook_account_activity_generic("account_activity"); +function facebook_account_activity_v1() { + return facebook_account_activity_generic("account_activity"); } -function facebook_account_activity_v2(this: TaskTargetPipelineHelper) { - return this.facebook_account_activity_generic("account_activity_v2"); +function facebook_account_activity_v2() { + return facebook_account_activity_generic("account_activity_v2"); } -function facebook_admin_records_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` +function facebook_admin_records_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` ["event","created_timestamp","ip_address","user_agent","datr_cookie"], ( .${prop}[] | [.event, (.session.created_timestamp | todateiso8601), .ip_address, .user_agent, .datr_cookie] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "isodatetime", "text", "text", "text"], + perRowDescription: '{0} at {1} from {2}', + perRowTags: "facebook,security", + }) + ); } -function facebook_admin_records_v1(this: TaskTargetPipelineHelper) { - return this.facebook_admin_records_generic("admin_records"); +function facebook_admin_records_v1() { + return facebook_admin_records_generic("admin_records"); } -function facebook_admin_records_v2(this: TaskTargetPipelineHelper) { - return this.facebook_admin_records_generic("admin_records_v2"); +function facebook_admin_records_v2() { + return facebook_admin_records_generic("admin_records_v2"); } -function facebook_authorized_logins_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_authorized_logins_generic(prop: string) { // I don't think .location, .app, .session_type are in v1? So I've made them nullable, but I only have // 1 v1 entry to actually compare against... - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["name","created_timestamp","updated_timestamp","ip_address","user_agent","location","app", "session_type", "datr_cookie"], ( .${prop}[] | [.name, (.created_timestamp | todateiso8601), (.updated_timestamp | todateiso8601), .ip_address, .user_agent, .location // "", .app // "", .session_type // "", .datr_cookie] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "isodatetime", "isodatetime", "text", "text", "text", "text", "text", "text"], + perRowDescription: 'Session "{0}" from {5} on {1}', + perRowTags: "facebook,security", + }) + ); } -function facebook_authorized_logins_v1(this: TaskTargetPipelineHelper) { - return this.facebook_authorized_logins_generic("recognized_devices"); +function facebook_authorized_logins_v1() { + return facebook_authorized_logins_generic("recognized_devices"); } -function facebook_authorized_logins_v2(this: TaskTargetPipelineHelper) { - return this.facebook_authorized_logins_generic("active_sessions_v2"); +function facebook_authorized_logins_v2() { + return facebook_authorized_logins_generic("active_sessions_v2"); } -function facebook_contact_verification_generic(this: TaskTargetPipelineHelper, prop: string) { - return this.cmd(["jq", "-r", ` +function facebook_contact_verification_generic(prop: string) { + return pipe( + cmd(["jq", "-r", ` ["timestamp", "email", "contact_type"], ( .${prop}[] | [(.verification_time | todateiso8601), .contact, .contact_type] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["isodatetime", "text", "text"], + perRowDescription: '{2} verification of {1} at {0}', + perRowTags: "facebook,security", + }) + ); } -function facebook_contact_verification_v1(this: TaskTargetPipelineHelper) { - return this.facebook_contact_verification_generic("contact_verifications"); +function facebook_contact_verification_v1() { + return facebook_contact_verification_generic("contact_verifications"); } -function facebook_contact_verification_v2(this: TaskTargetPipelineHelper) { - return this.facebook_contact_verification_generic("contact_verifications_v2"); +function facebook_contact_verification_v2() { + return facebook_contact_verification_generic("contact_verifications_v2"); } -function facebook_account_accesses_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_account_accesses_generic(prop: string) { // TODO: there's a updated_timestamp doesn't always exist - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["action", "timestamp", "site", "ip_address"], ( .${prop}[] | [.action, (.timestamp | todateiso8601), .site, .ip_address] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "isodatetime", "text", "text"], + perRowDescription: '{0} on {2} at {1} from {3}', + perRowTags: "facebook,security", + }) + ); } -function facebook_account_accesses_v1(this: TaskTargetPipelineHelper) { - return this.facebook_account_accesses_generic("account_accesses"); +function facebook_account_accesses_v1() { + return facebook_account_accesses_generic("account_accesses"); } -function facebook_account_accesses_v2(this: TaskTargetPipelineHelper) { - return this.facebook_account_accesses_generic("account_accesses_v2"); +function facebook_account_accesses_v2() { + return facebook_account_accesses_generic("account_accesses_v2"); } -function facebook_pages_unfollowed_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_pages_unfollowed_generic(prop: string) { // TODO: This is missing the .data field, but it only looks like the "name" on the only record I have - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title", "timestamp"], ( .${prop}[] | [.title, (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Unfollowed "{0}" at {1}', + perRowTags: "facebook,initiated_by_me", + }) + ); } -function facebook_pages_unfollowed_v1(this: TaskTargetPipelineHelper) { - return this.facebook_pages_unfollowed_generic("pages_unfollowed"); +function facebook_pages_unfollowed_v1() { + return facebook_pages_unfollowed_generic("pages_unfollowed"); } -function facebook_pages_unfollowed_v2(this: TaskTargetPipelineHelper) { - return this.facebook_pages_unfollowed_generic("pages_unfollowed_v2"); +function facebook_pages_unfollowed_v2() { + return facebook_pages_unfollowed_generic("pages_unfollowed_v2"); } -function facebook_groups_joined_generic(this: TaskTargetPipelineHelper, prop: string) { +function facebook_groups_joined_generic(prop: string) { // this has a data property but it is redundant, ONLY IN v2 - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title", "timestamp"], ( .${prop}[] | [.title, (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Joined group "{0}" at {1}', + perRowTags: "facebook,initiated_by_me", + }) + ); } -function facebook_groups_joined_v1(this: TaskTargetPipelineHelper) { - return this.facebook_groups_joined_generic("groups_joined"); +function facebook_groups_joined_v1() { + return facebook_groups_joined_generic("groups_joined"); } -function facebook_groups_joined_v2(this: TaskTargetPipelineHelper) { - return this.facebook_groups_joined_generic("groups_joined_v2"); +function facebook_groups_joined_v2() { + return facebook_groups_joined_generic("groups_joined_v2"); } -function facebook_group_posts_v1(this: TaskTargetPipelineHelper) { +function facebook_group_posts_v1() { // TODO: Attachments metadata, maybe another timestamp in the data field too (but it looks like the same everywhere) - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title", "data", "timestamp"], ( .group_posts.activity_log_data[] | [.title, "TODO", (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "TODO", "isodatetime"], + perRowDescription: 'Group post "{0}" at {2}', + perRowTags: "facebook", + }) + ); } -function facebook_group_posts_v2(this: TaskTargetPipelineHelper) { +function facebook_group_posts_v2() { // TODO: Still a data and attachments to pull out - return this.cmd(["jq", "-r", ` + return pipe( + cmd(["jq", "-r", ` ["title", "data", "timestamp"], ( .group_posts_v2[] | [.title, "TODO", (.timestamp | todateiso8601)] ) | @csv - `]) + `]), + assignMeta({ + columnMeta: ["text", "TODO", "isodatetime"], + perRowDescription: 'Group post "{0}" at {2}', + perRowTags: "facebook", + }) + ); } -function facebook_v2(this: TaskTargetPipelineHelper) { - const p = this.setId(t=>`Facebookv2 - ${t.basename}`); // Generic ID for everything in here - const col: Set = new Set(); +export function facebook_v2() { + return pipe( + // Generic ID for everything in here + assignMeta({ idValue: t=>`Facebookv2 - ${t.basename}` }), + branchGen(function*() { + // No correlary to accounts_and_profiles.json + // No correlary for your_off-facebook_activity.json + yield pipe(cd(`apps_and_websites_off_of_facebook/connected_apps_and_websites.json`), read(), facebook_installed_apps_v2()); + yield pipe(cd(`your_facebook_activity/comments_and_reactions/comments.json`),read(),facebook_comments_v2()); + yield pipe( + glob(`your_facebook_activity/messages/*/**/*.json`), // Messages files are in the FOLDERS inside messages (archived_threads, e2ee_cutover, etc...) + assignMeta({ idValue: t=>`Facebookv2 - Messages ${t.basenameN(2)}` }), // 1, 2, etc is not specific enough, include the convo name + read(), + facebook_messages_generic() + ); - // No correlary to accounts_and_profiles.json - // No correlary for your_off-facebook_activity.json - p.collect(col).cd(`apps_and_websites_off_of_facebook/connected_apps_and_websites.json`).read().facebook_installed_apps_v2(); - p.collect(col).cd(`your_facebook_activity/comments_and_reactions/comments.json`).read().facebook_comments_v2(); - p.collect(col).glob(`your_facebook_activity/messages/*/**/*.json`) // Messages files are in the FOLDERS inside messages (archived_threads, e2ee_cutover, etc...) - .setId(t=>`Facebookv2 - Messages ${t.basenameN(2)}`) // 1, 2, etc is not specific enough, include the convo name - .read() - .facebook_messages_generic() + yield pipe( + cd(`your_facebook_activity/other_activity/time_spent_on_facebook.json`), + read(), + cmd(["jq", "-r", ` + ["start","end"], + ( + .label_values[] + | select(.label == "Intervals") + | .vec[] + | [ + (.dict[0].timestamp_value | todateiso8601), + (.dict[1].timestamp_value | todateiso8601) + ] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["isodatetime", "isodatetime"], + perRowDescription: 'Active from {0} to {1}', + perRowTags: "facebook", + }) + ); + yield pipe(cd(`your_facebook_activity/groups/your_group_membership_activity.json`), read(), facebook_groups_joined_v2()); + yield pipe(cd(`your_facebook_activity/groups/group_posts_and_comments.json`), read(), facebook_group_posts_v2()); + yield pipe(cd(`your_facebook_activity/pages/pages_and_profiles_you've_unfollowed.json`), read(), facebook_pages_unfollowed_v2()); - p.collect(col).cd(`your_facebook_activity/other_activity/time_spent_on_facebook.json`).read() - .cmd(["jq", "-r", ` - ["start","end"], - ( - .label_values[] - | select(.label == "Intervals") - | .vec[] - | [ - (.dict[0].timestamp_value | todateiso8601), - (.dict[1].timestamp_value | todateiso8601) - ] - ) - | @csv - `]) - p.collect(col).cd(`your_facebook_activity/groups/your_group_membership_activity.json`).read().facebook_groups_joined_v2(); - p.collect(col).cd(`your_facebook_activity/groups/group_posts_and_comments.json`).read().facebook_group_posts_v2(); - p.collect(col).cd(`your_facebook_activity/pages/pages_and_profiles_you've_unfollowed.json`).read().facebook_pages_unfollowed_v2(); + yield pipe(cd(`connections/friends/your_friends.json`), read(), facebook_friends_generic("friends_v2")); + yield pipe(cd(`connections/friends/rejected_friend_requests.json`), read(), facebook_friends_generic("rejected_requests_v2")); + yield pipe(cd(`connections/friends/received_friend_requests.json`), read(), facebook_friends_generic("received_requests_v2")); - p.collect(col).cd(`connections/friends/your_friends.json`).read().facebook_friends_generic("friends_v2"); - p.collect(col).cd(`connections/friends/rejected_friend_requests.json`).read().facebook_friends_generic("rejected_requests_v2"); - p.collect(col).cd(`connections/friends/received_friend_requests.json`).read().facebook_friends_generic("received_requests_v2"); + yield pipe(cd(`logged_information/activity_messages/people_and_friends.json`), read(), facebook_people_interactions_v2()); + yield pipe(cd(`logged_information/search/your_search_history.json`), read(), facebook_searches_v2()); + yield pipe(cd(`logged_information/notifications/notifications.json`), read(), facebook_notifications_v2()); - p.collect(col).cd(`logged_information/activity_messages/people_and_friends.json`).read().facebook_people_interactions_v2() - p.collect(col).cd(`logged_information/search/your_search_history.json`).read().facebook_searches_v2() - p.collect(col).cd(`logged_information/notifications/notifications.json`).read().facebook_notifications_v2(); + yield pipe(cd(`security_and_login_information/account_activity.json`), read(), facebook_account_activity_v2()); + yield pipe(cd(`security_and_login_information/record_details.json`), read(), facebook_admin_records_v2()); + yield pipe(cd(`security_and_login_information/where_you're_logged_in.json`), read(), facebook_authorized_logins_v2()); + yield pipe(cd(`security_and_login_information/email_address_verifications.json`), read(), facebook_contact_verification_v2()); + yield pipe(cd(`security_and_login_information/logins_and_logouts.json`), read(), facebook_account_accesses_v2()); - p.collect(col).cd(`security_and_login_information/account_activity.json`).read().facebook_account_activity_v2() - p.collect(col).cd(`security_and_login_information/record_details.json`).read().facebook_admin_records_v2() - p.collect(col).cd(`security_and_login_information/where_you're_logged_in.json`).read().facebook_authorized_logins_v2() - p.collect(col).cd(`security_and_login_information/email_address_verifications.json`).read().facebook_contact_verification_v2() - p.collect(col).cd(`security_and_login_information/logins_and_logouts.json`).read().facebook_account_accesses_v2() - - p.collect(col).cd(`your_facebook_activity/facebook_marketplace/items_sold.json`).read().facebook_marketplace_items_sold_v2() - - const final = Array.from(col).flat(); - return TaskTargetPipelineHelper.pipeline(final); + yield pipe(cd(`your_facebook_activity/facebook_marketplace/items_sold.json`), read(), facebook_marketplace_items_sold_v2()); + }) + ); } -function facebook(this: TaskTargetPipelineHelper){ - const p = this.setId(t=>`Facebook - ${t.basename}`); // Generic ID for everything in here - const col: Set = new Set(); +export function facebook(){ + return pipe( + // Generic ID for everything in here + assignMeta({ idValue: t=>`Facebook - ${t.basename}` }), + branchGen(function*() { - p.collect(col).cd(`about_you/notifications.json`).read().facebook_notifications_v1() - //TODO: .fork().skip('face_recognition.json').reason("Not a table, no idea how to use") - //TODO: .fork().skip('friend_peer_group.json').reason("Not a table, very small file") - //TODO:.fork().skip('messenger.json').reason("Not a table, but might have some juicy stuff for future") - //TODO: .fork().todo('preferences.json').reason("Too complex for now") - //TODO:.fork().todo('visited.json').reason("Too complex for now") - //TODO:.fork().todo('viewed.json').reason("Too complex for now") + yield pipe(cd(`about_you/notifications.json`), read(), facebook_notifications_v1()); + //TODO: .fork().skip('face_recognition.json').reason("Not a table, no idea how to use") + //TODO: .fork().skip('friend_peer_group.json').reason("Not a table, very small file") + //TODO:.fork().skip('messenger.json').reason("Not a table, but might have some juicy stuff for future") + //TODO: .fork().todo('preferences.json').reason("Too complex for now") + //TODO:.fork().todo('visited.json').reason("Too complex for now") + //TODO:.fork().todo('viewed.json').reason("Too complex for now") - p.collect(col).cd(`accounts_center/accounts_and_profiles.json`).read() - .cmd(["jq", "-r", `["service_name","native_app_id","username","email", "phone_number", "name"], - ( - .linked_accounts[] - | [.service_name, .native_app_id, .username, .email, .phone_number, .name] - ) - | @csv`]) - .csvSink() + yield pipe( + cd(`accounts_center/accounts_and_profiles.json`), + read(), + cmd(["jq", "-r", `["service_name","native_app_id","username","email", "phone_number", "name"], + ( + .linked_accounts[] + | [.service_name, .native_app_id, .username, .email, .phone_number, .name] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "text", "text", "text", "text", "text"], + perRowDescription: '{0} account "{2}"', + perRowTags: "facebook", + }) + ); - p.collect(col).cd(`ads_and_businesses/your_off-facebook_activity.json`).read() - .cmd(["jq", "-r", ` - ["name","id","type","timestamp"], - ( - .off_facebook_activity[] - | .name as $name - | .events[] - | [$name, .id, .type, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - //TODO: .fork().todo('advertisers_who_uploaded_a_contact_list_with_your_information.json') + yield pipe( + cd(`ads_and_businesses/your_off-facebook_activity.json`), + read(), + cmd(["jq", "-r", ` + ["name","id","type","timestamp"], + ( + .off_facebook_activity[] + | .name as $name + | .events[] + | [$name, .id, .type, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "any", "text", "isodatetime"], + perRowDescription: '{2} event from {0} at {3}', + perRowTags: "facebook", + }) + ); + //TODO: .fork().todo('advertisers_who_uploaded_a_contact_list_with_your_information.json') - p.collect(col).cd(`apps_and_websites/apps_and_websites.json`).read().facebook_installed_apps_v1() + yield pipe(cd(`apps_and_websites/apps_and_websites.json`), read(), facebook_installed_apps_v1()); - // `${facebookRoot}/archive` - no data in my export - // `${facebookRoot}/campus` - no data in my export + // `${facebookRoot}/archive` - no data in my export + // `${facebookRoot}/campus` - no data in my export - p.collect(col).cd(`comments/comments.json`).read().facebook_comments_v1() + yield pipe(cd(`comments/comments.json`), read(), facebook_comments_v1()); - p.collect(col).glob(`dating/messages/*.json`) // Files are 0.json, 1.json, etc - .setId(t=>`Facebook - Dating Messages ${t.basename}`) // Slightly more specific message - .read() - .cmd(["jq", "-r", ` - ["from","to","timestamp","body"], - .recipient as $to - | ( - .messages[] - | ["Me", $to, (.timestamp | todateiso8601), .body] - ) - | @csv - `]) - .csvSink();//[["timestamp", "numeric"]]) - //todo: your_dating_activity.json, but it only has a few lines and not super useful - //todo: the other dating files are also just, small + yield pipe( + glob(`dating/messages/*.json`), // Files are 0.json, 1.json, etc + assignMeta({ idValue: t=>`Facebook - Dating Messages ${t.basename}` }), // Slightly more specific message + read(), + cmd(["jq", "-r", ` + ["from","to","timestamp","body"], + ( + .recipient as $to + | ( + .messages[] + | ["Me", $to, (.timestamp | todateiso8601), .body] + ) + ) + | @csv + `]), + assignMeta({ + columnMeta: ["sender", "receiver", "isodatetime", "text"], + perRowDescription: '"{3}" from {0} to {1} at {2}', + perRowTags: "facebook,message,dating,content_by_me", + }) + ); + //todo: your_dating_activity.json, but it only has a few lines and not super useful + //todo: the other dating files are also just, small - // TODO: events -// rcd(`events`); -// localCollect('event_invitations.json', json, sspawn('jq', [` -// .events_invited[] |= ( -// .start_timestamp |= todateiso8601 | -// .end_timestamp |= todateiso8601 -// ) -// `])); -// localCollect('your_event_responses.json', json, sspawn('jq', [` -// .event_responses.events_joined[] |= ( -// .start_timestamp |= todateiso8601 | -// .end_timestamp |= todateiso8601 -// ) | -// .event_responses.events_declined[] |= ( -// .start_timestamp |= todateiso8601 | -// .end_timestamp |= todateiso8601 -// ) | -// .event_responses.events_interested[] |= ( -// .start_timestamp |= todateiso8601 | -// .end_timestamp |= todateiso8601 -// ) -// `])); + // TODO: events + // rcd(`events`); + // localCollect('event_invitations.json', json, sspawn('jq', [` + // .events_invited[] |= ( + // .start_timestamp |= todateiso8601 | + // .end_timestamp |= todateiso8601 + // ) + // `])); + // localCollect('your_event_responses.json', json, sspawn('jq', [` + // .event_responses.events_joined[] |= ( + // .start_timestamp |= todateiso8601 | + // .end_timestamp |= todateiso8601 + // ) | + // .event_responses.events_declined[] |= ( + // .start_timestamp |= todateiso8601 | + // .end_timestamp |= todateiso8601 + // ) | + // .event_responses.events_interested[] |= ( + // .start_timestamp |= todateiso8601 | + // .end_timestamp |= todateiso8601 + // ) + // `])); - p.collect(col).cd(`facebook_gaming/instant_games.json`) - .read() - .cmd(["jq", "-r", ` - ["game", "added_timestamp"], - ( - .instant_games_played[] - | [.name, (.added_timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["added_timestamp", "numeric"]]) + yield pipe( + cd(`facebook_gaming/instant_games.json`), + read(), + cmd(["jq", "-r", ` + ["game", "added_timestamp"], + ( + .instant_games_played[] + | [.name, (.added_timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Played "{0}" starting {1}', + perRowTags: "facebook,gaming", + }) + ); - p.collect(col).cd(`following_and_followers/unfollowed_pages.json`).read().facebook_pages_unfollowed_v1() - p.collect(col).cd(`following_and_followers/following.json`) - .read() - .cmd(["jq", "-r", ` - ["name", "timestamp"], - ( - .following[] - | [.name, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - p.collect(col).cd(`following_and_followers/followers.json`) - .read() - .cmd(["jq", "-r", ` - ["name"], - ( - .followers[] - | [.name] - ) - | @csv - `]) - .csvSink() + yield pipe(cd(`following_and_followers/unfollowed_pages.json`), read(), facebook_pages_unfollowed_v1()); + yield pipe( + cd(`following_and_followers/following.json`), + read(), + cmd(["jq", "-r", ` + ["name", "timestamp"], + ( + .following[] + | [.name, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["receiver", "isodatetime"], + perRowDescription: 'Followed "{0}" at {1}', + perRowTags: "facebook", + }) + ); + yield pipe( + cd(`following_and_followers/followers.json`), + read(), + cmd(["jq", "-r", ` + ["name"], + ( + .followers[] + | [.name] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["sender"], + perRowDescription: '{0} follows you', + perRowTags: "facebook", + }) + ); - p.collect(col).cd(`friends/sent_friend_requests.json`).read().facebook_friends_generic("sent_requests") - p.collect(col).cd(`friends/removed_friends.json`).read().facebook_friends_generic("deleted_friends") - p.collect(col).cd(`friends/rejected_friend_requests.json`).read().facebook_friends_generic("rejected_requests") - p.collect(col).cd(`friends/received_friend_requests.json`).read().facebook_friends_generic("received_requests") - p.collect(col).cd(`friends/friends.json`).read().facebook_friends_generic("friends") + yield pipe(cd(`friends/sent_friend_requests.json`), read(), facebook_friends_generic("sent_requests")); + yield pipe(cd(`friends/removed_friends.json`), read(), facebook_friends_generic("deleted_friends")); + yield pipe(cd(`friends/rejected_friend_requests.json`), read(), facebook_friends_generic("rejected_requests")); + yield pipe(cd(`friends/received_friend_requests.json`), read(), facebook_friends_generic("received_requests")); + yield pipe(cd(`friends/friends.json`), read(), facebook_friends_generic("friends")); - p.collect(col).cd(`groups/your_group_membership_activity.json`).read().facebook_groups_joined_v1(); - p.collect(col).cd(`groups/your_posts_and_comments_in_groups.json`).read().facebook_group_posts_v1(); + yield pipe(cd(`groups/your_group_membership_activity.json`), read(), facebook_groups_joined_v1()); + yield pipe(cd(`groups/your_posts_and_comments_in_groups.json`), read(), facebook_group_posts_v1()); - // there's also groups.json and events.json but neither has timestamp so they're - // not really useful right now - p.collect(col).cd(`interactions/people.json`).read().facebook_people_interactions_v1() + // there's also groups.json and events.json but neither has timestamp so they're + // not really useful right now + yield pipe(cd(`interactions/people.json`), read(), facebook_people_interactions_v1()); - // `${facebookRoot}/journalist_registration` - no data in my export + // `${facebookRoot}/journalist_registration` - no data in my export - p.collect(col).cd(`likes_and_reactions/pages.json`) - .read() - .cmd(["jq", "-r", ` - ["name", "timestamp"], - ( - .page_likes[] - | [.name, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - p.collect(col).cd(`likes_and_reactions/posts_and_comments.json`) - .read() - .cmd(["jq", "-r", ` - ["title", "timestamp", "reaction"], - ( - .reactions[] - | [.name, (.timestamp | todateiso8601), .data[0].reaction.reaction] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) + yield pipe( + cd(`likes_and_reactions/pages.json`), + read(), + cmd(["jq", "-r", ` + ["name", "timestamp"], + ( + .page_likes[] + | [.name, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Liked page "{0}" at {1}', + perRowTags: "facebook", + }) + ); + yield pipe( + cd(`likes_and_reactions/posts_and_comments.json`), + read(), + cmd(["jq", "-r", ` + ["title", "timestamp", "reaction"], + ( + .reactions[] + | [.name, (.timestamp | todateiso8601), .data[0].reaction.reaction] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime", "text"], + perRowDescription: '{2} on "{0}" at {1}', + perRowTags: "facebook", + }) + ); - // TODO: - // rcd(`location`); - // localCollect('primary_location.json', json); - // localCollect('primary_public_location.json', json); - // localCollect('timezone.json', json); + // TODO: + // rcd(`location`); + // localCollect('primary_location.json', json); + // localCollect('primary_public_location.json', json); + // localCollect('timezone.json', json); - p.collect(col).cd(`marketplace/items_sold.json`).read().facebook_marketplace_items_sold_v1() + yield pipe(cd(`marketplace/items_sold.json`), read(), facebook_marketplace_items_sold_v1()); - p.collect(col).glob(`messages/**/*.json`) // Files are message_1.json, etc - .setId(t=>`Facebook - Messages ${t.basenameN(2)}`) // 1, 2, etc is not specific enough, include the convo name - .read() - .facebook_messages_generic() + yield pipe( + glob(`messages/**/*.json`), // Files are message_1.json, etc + assignMeta({ idValue: t=>`Facebook - Messages ${t.basenameN(2)}` }), // 1, 2, etc is not specific enough, include the convo name + read(), + facebook_messages_generic() + ); + + // `${facebookRoot}/music_recommendations` - no data + + // rcd(`news`); + // localCollect('your_locations.json', json); + + yield pipe( + cd(`other_activity/pokes.json`), + read(), + cmd(["jq", "-r", ` + ["from", "to","rank","timestamp"], + ( + .pokes.data[] + | [.poker, .pokee, .rank, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["sender", "receiver", "numeric", "isodatetime"], + perRowDescription: '{0} poked {1} at {3}', + perRowTags: "facebook", + }) + ); + yield pipe( + cd(`other_activity/support_correspondences.json`), + read(), + // TODO: I'm seeing blanks in .from and .to when the replier was Facebook + // themselves. Perhaps it's broken? + // TODO: Attachments + cmd(["jq", "-r", ` + ["from", "to", "subject", "message", "timestamp"], + ( + .support_correspondence[].messages[] + | [.from, .to, .subject, .message, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["sender", "receiver", "text", "text", "isodatetime"], + perRowDescription: '"{2}" from {0} to {1} at {4}', + perRowTags: "facebook", + }) + ); - // `${facebookRoot}/music_recommendations` - no data + // `${facebookRoot}/pages` - no data - // rcd(`news`); - // localCollect('your_locations.json', json); + yield pipe( + cd(`payment_history/payment_history.json`), + read(), + cmd(["jq", "-r", ` + ["from", "to","amount","currency", "type","status","payment_method", "created_timestamp"], + ( + .payments.payments[] + | [.sender, .receiver, .amount, .currency, .type, .status, .payment_method, (.created_timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["sender", "receiver", "numeric", "text", "text", "text", "text", "isodatetime"], + perRowDescription: '{2} {3} from {0} to {1} on {7}', + perRowTags: "facebook,payment", + }) + ); - p.collect(col).cd(`other_activity/pokes.json`) - .read() - .cmd(["jq", "-r", ` - ["from", "to","rank","timestamp"], - ( - .pokes.data[] - | [.poker, .pokee, .rank, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]); - p.collect(col).cd(`other_activity/support_correspondences.json`) - .read() - // TODO: I'm seeing blanks in .from and .to when the replier was Facebook - // themselves. Perhaps it's broken? - // TODO: Attachments - .cmd(["jq", "-r", ` - ["from", "to", "subject", "message", "timestamp"], - ( - .support_correspondence[].messages[] - | [.from, .to, .subject, .message, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) + // TODO: There's also photos_and_videos/your_videos.json + // TODO: There's a media_metadata in each of the images too to convert as well as external files + yield pipe( + glob(`photos_and_videos/album/*.json`), + // Could use a better name, currently 0.json, 1.json, etc... + assignMeta({ idValue: t=>`Facebook - Album ${t.basename}` }), //slightly more speciifc name, it woudl be better if we could use the album name + read(), + cmd(["jq", "-r", ` + ["album","uri","creation_timestamp"], + ( + .photos[] + | [.title, .uri, (.creation_timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "url", "isodatetime"], + perRowDescription: 'Photo in "{0}" at {2}', + perRowTags: "facebook,photo", + }) + ); + + yield pipe(cd(`posts/your_pinned_posts.json`), + read(), + cmd(["jq", "-r", ` + ["name","uri","timestamp"], + ( + .pinned_posts[].entries[] + | [.data.name, .data.uri, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "url", "isodatetime"], + perRowDescription: 'Pinned post "{0}" at {2}', + perRowTags: "facebook", + }) + ); + // TODO: Glob? I never posted a lot on FB + yield pipe( + cd(`posts/your_posts_1.json`), + read(), + // TODO: Data is an array with objects. .post, .updated_timestamp, separately?? + // TODO: Also attachments + cmd(["jq", "-r", ` + ["title","data","timestamp"], + ( + .[] + | [.title, "TODO: data", (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "TODO", "isodatetime"], + perRowDescription: 'Post "{0}" at {2}', + perRowTags: "facebook", + }) + ); + + // `${facebookRoot}/privacy_checkup` - no data + + // TODO: Shape is non-tabular, but maybe we should handle it? + // Looks mostly like dupes from other places + // './profile_information.json': undefined, + // The minimum amount of data is just .title and .timestamp + // TODO: HAndle data and attachments + yield pipe( + cd(`profile_information/profile_update_history.json`), + read(), + cmd(["jq", "-r", ` + ["title","timestamp"], + ( + .profile_updates[] + | [.title, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Profile update "{0}" at {1}', + perRowTags: "facebook", + }) + ); + + // `${facebookRoot}/rewards` - no data + // `${facebookRoot}/saved_items_and_collections` - no data + + yield pipe(cd(`search_history/your_search_history.json`), read(), facebook_searches_v1()); + + yield pipe( + cd(`security_and_login_information/account_status_changes.json`), + read(), + cmd(["jq", "-r", ` + ["status","timestamp"], + ( + .account_status_changes[] + | [.status, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Account {0} at {1}', + perRowTags: "facebook,security", + }) + ); + yield pipe(cd(`security_and_login_information/account_activity.json`), read(), facebook_account_activity_v1()); + yield pipe(cd(`security_and_login_information/administrative_records.json`), read(), facebook_admin_records_v1()); + yield pipe(cd(`security_and_login_information/authorized_logins.json`), read(), facebook_authorized_logins_v1()); + yield pipe(cd(`security_and_login_information/contact_verifications.json`), read(), facebook_contact_verification_v1()); + yield pipe(cd(`security_and_login_information/logins_and_logouts.json`), read(), facebook_account_accesses_v1()); + // TODO: datr_cookie_info, looks like a bunch of timestamps + // a.fork().cd(`login_protection_data.json`) + // .read() + // // TODO: updated_timestamp doesn't always exist + // .cmd(["jq", "-r", ` + // ["name", "created_timestamp", "updated_timestamp", "ip_address"], + // ( + // .login_protection_data[] + // | [.name, (.created_timestamp | todateiso8601), (.updated_timestamp | todateiso8601), .ip_address] + // ) + // | @csv + // `]) + // TODO: mobile_devices, only a couple entries + // TODO: used_ip_addresses + // TODO: where_you've logged in + // TODO: your_facebook_activity, useless and small - // `${facebookRoot}/pages` - no data + // `${facebookRoot}/short_videos` - no data in my export + // `${facebookRoot}/saved_items_and_collections` - no data in my export - p.collect(col).cd(`payment_history/payment_history.json`) - .read() - .cmd(["jq", "-r", ` - ["from", "to","amount","currency", "type","status","payment_method", "created_timestamp"], - ( - .payments.payments[] - | [.sender, .receiver, .amount, .currency, .type, .status, .payment_method, (.created_timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["created_timestamp", "numeric"]]); + yield pipe( + cd(`stories/story_reactions.json`), + read(), + cmd(["jq", "-r", ` + ["title", "timestamp"], + ( + .stories_feedback[] + | [.title, (.timestamp | todateiso8601)] + ) + | @csv + `]), + assignMeta({ + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Story reaction on "{0}" at {1}', + perRowTags: "facebook", + }) + ); - // TODO: There's also photos_and_videos/your_videos.json - // TODO: There's a media_metadata in each of the images too to convert as well as external files - p.collect(col).glob(`photos_and_videos/album/*.json`) - // Could use a better name, currently 0.json, 1.json, etc... - .setId(t=>`Facebook - Album ${t.basename}`) //slightly more speciifc name, it woudl be better if we could use the album name - .read() - .cmd(["jq", "-r", ` - ["album","uri","creation_timestamp"], - ( - .photos[] - | [.title, .uri, (.creation_timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["creation_timestamp", "numeric"]]) - - p.collect(col).cd(`posts/your_pinned_posts.json`) - .read() - .cmd(["jq", "-r", ` - ["name","uri","timestamp"], - ( - .pinned_posts[].entries[] - | [.data.name, .data.uri, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - // TODO: Glob? I never posted a lot on FB - p.collect(col).cd(`posts/your_posts_1.json`) - .read() - // TODO: Data is an array with objects. .post, .updated_timestamp, separately?? - // TODO: Also attachments - .cmd(["jq", "-r", ` - ["title","data","timestamp"], - ( - .[] - | [.title, "TODO: data", (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - - // `${facebookRoot}/privacy_checkup` - no data - - // TODO: Shape is non-tabular, but maybe we should handle it? - // Looks mostly like dupes from other places - // './profile_information.json': undefined, - // The minimum amount of data is just .title and .timestamp - // TODO: HAndle data and attachments - p.collect(col).cd(`profile_information/profile_update_history.json`) - .read() - .cmd(["jq", "-r", ` - ["title","timestamp"], - ( - .profile_updates[] - | [.title, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - - // `${facebookRoot}/rewards` - no data - // `${facebookRoot}/saved_items_and_collections` - no data - - p.collect(col).cd(`search_history/your_search_history.json`).read().facebook_searches_v1() - - p.collect(col).cd(`security_and_login_information/account_status_changes.json`) - .read() - .cmd(["jq", "-r", ` - ["status","timestamp"], - ( - .account_status_changes[] - | [.status, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - p.collect(col).cd(`security_and_login_information/account_activity.json`).read().facebook_account_activity_v1() - p.collect(col).cd(`security_and_login_information/administrative_records.json`).read().facebook_admin_records_v1() - p.collect(col).cd(`security_and_login_information/authorized_logins.json`).read().facebook_authorized_logins_v1() - p.collect(col).cd(`security_and_login_information/contact_verifications.json`).read().facebook_contact_verification_v1() - p.collect(col).cd(`security_and_login_information/logins_and_logouts.json`).read().facebook_account_accesses_v1() - // TODO: datr_cookie_info, looks like a bunch of timestamps - // a.fork().cd(`login_protection_data.json`) - // .read() - // // TODO: updated_timestamp doesn't always exist - // .cmd(["jq", "-r", ` - // ["name", "created_timestamp", "updated_timestamp", "ip_address"], - // ( - // .login_protection_data[] - // | [.name, (.created_timestamp | todateiso8601), (.updated_timestamp | todateiso8601), .ip_address] - // ) - // | @csv - // `]) - // TODO: mobile_devices, only a couple entries - // TODO: used_ip_addresses - // TODO: where_you've logged in - // TODO: your_facebook_activity, useless and small - - - // `${facebookRoot}/short_videos` - no data in my export - // `${facebookRoot}/saved_items_and_collections` - no data in my export - - p.collect(col).cd(`stories/story_reactions.json`) - .read() - .cmd(["jq", "-r", ` - ["title", "timestamp"], - ( - .stories_feedback[] - | [.title, (.timestamp | todateiso8601)] - ) - | @csv - `]) - .csvSink([["timestamp", "numeric"]]) - - // `${facebookRoot}/trash` - no data in my export - // `${facebookRoot}/voice_recording_and_transcription` - no data in my export - // `${facebookRoot}/volunteering` - no data in my export - // `${facebookRoot}/voting_location_and_reminders` - only small 1-property things - // `${facebookRoot}/your_places` - no data in my export - // `${facebookRoot}/your_topics` - no data in my export - - const final = Array.from(col).flat(); - return TaskTargetPipelineHelper.pipeline(final); + // `${facebookRoot}/trash` - no data in my export + // `${facebookRoot}/voice_recording_and_transcription` - no data in my export + // `${facebookRoot}/volunteering` - no data in my export + // `${facebookRoot}/voting_location_and_reminders` - only small 1-property things + // `${facebookRoot}/your_places` - no data in my export + // `${facebookRoot}/your_topics` - no data in my export + }) + ); }; - diff --git a/data-export/fitbit.ts b/data-export/fitbit.ts new file mode 100644 index 0000000..560c94d --- /dev/null +++ b/data-export/fitbit.ts @@ -0,0 +1,747 @@ +import { pipe, cmd, assignMeta, cd, glob, read, branchGen, branch, type PipelineOp } from "./task.ts"; + +/** Single CSV passthrough — cd to file, read, set id */ +function csvOne(filePath: string, id: string, tags = "fitbit"): PipelineOp { + return pipe( + cd(filePath), read(), + assignMeta({ idValue: id, perRowTags: tags }) + ); +} + +/** + * Aggregate multiple date/number-suffixed CSV files into one table. + * strips each file's own header row with `tail -n +2`; + * the framework emits `aggregateColumns` as the single header. + */ +function csvMany(pattern: string, id: string, columns: string[], tags = "fitbit"): PipelineOp { + return pipe( + glob(pattern), + assignMeta({ idValue: id, aggregate: true, aggregateColumns: columns, perRowTags: tags }), + read(), + cmd(["tail", "-n", "+2"]) + ); +} + +/** + * Aggregate multiple date/number-suffixed JSON array files into one table. + * `jqBody` should output CSV rows only (no header line); the framework + * emits `aggregateColumns` as the single header. + */ +function jsonMany(pattern: string, id: string, columns: string[], jqBody: string, tags = "fitbit"): PipelineOp { + return pipe( + glob(pattern), + assignMeta({ idValue: id, aggregate: true, aggregateColumns: columns, perRowTags: tags }), + read(), + cmd(["jq", "-r", jqBody]) + ); +} + +// ============================================================ +// Application +// ============================================================ + +function fitbit_account_access_events(): PipelineOp { + // Files are paginated: Account_Access_Events_1.csv, Account_Access_Events_2.csv, ... + return csvMany( + "Application/Account_Access_Events_*.csv", + "Fitbit - Account Access Events", + ["timestamp", "event_name", "email", "location", "ip", "outcome", "reason", "application", "device_info"] + ); +} + +function fitbit_account_management_events(): PipelineOp { + return csvMany( + "Application/Account_Management_Events_*.csv", + "Fitbit - Account Management Events", + ["timestamp", "event_name", "email", "location", "ip", "outcome", "reason"] + ); +} + +function fitbit_email_audit(): PipelineOp { + return csvOne("Application/User_Email_Audit_Entry.csv", "Fitbit - Email Audit"); +} + +function fitbit_retired_passwords(): PipelineOp { + return csvOne("Application/User_Retired_Password.csv", "Fitbit - Retired Passwords"); +} + +// ============================================================ +// Heart +// ============================================================ + +function fitbit_afib_enrollment(): PipelineOp { + return csvOne("Heart/afib_ppg_enrollment.csv", "Fitbit - AFib Enrollment"); +} + +function fitbit_hr_notification_alerts(): PipelineOp { + return csvOne("Heart/Heart Rate Notifications Alerts.csv", "Fitbit - HR Notification Alerts"); +} + +function fitbit_hr_notification_profile(): PipelineOp { + return csvOne("Heart/Heart Rate Notifications Profile.csv", "Fitbit - HR Notification Profile"); +} + +// ============================================================ +// Menstrual Health +// ============================================================ + +function fitbit_menstrual_cycles(): PipelineOp { + return csvOne("Menstrual Health/menstrual_health_cycles.csv", "Fitbit - Menstrual Cycles"); +} + +function fitbit_menstrual_symptoms(): PipelineOp { + return csvOne("Menstrual Health/menstrual_health_symptoms.csv", "Fitbit - Menstrual Symptoms"); +} + +function fitbit_menstrual_birth_control(): PipelineOp { + return csvOne("Menstrual Health/menstrual_health_birth_control.csv", "Fitbit - Menstrual Birth Control"); +} + +function fitbit_menstrual_settings(): PipelineOp { + return csvOne("Menstrual Health/menstrual_health_settings.csv", "Fitbit - Menstrual Settings"); +} + +// ============================================================ +// Other +// ============================================================ + +function fitbit_oxygen_variation(): PipelineOp { + return csvMany( + "Other/estimated_oxygen_variation-*.csv", + "Fitbit - Estimated Oxygen Variation", + ["timestamp", "Infrared to Red Signal Ratio"] + ); +} + +// ============================================================ +// Personal & Account +// ============================================================ + +function fitbit_profile(): PipelineOp { + return csvOne("Personal & Account/Profile.csv", "Fitbit - Profile"); +} + +function fitbit_devices(): PipelineOp { + return csvOne("Personal & Account/Devices.csv", "Fitbit - Devices"); +} + +function fitbit_trackers(): PipelineOp { + return csvOne("Personal & Account/Trackers.csv", "Fitbit - Trackers"); +} + +function fitbit_scales(): PipelineOp { + return csvOne("Personal & Account/Scales.csv", "Fitbit - Scales"); +} + +function fitbit_tracker_config(): PipelineOp { + return csvOne("Personal & Account/Tracker Optional Configuration.csv", "Fitbit - Tracker Optional Configuration"); +} + +function fitbit_ios_notifications(): PipelineOp { + return csvOne("Personal & Account/iOS App Notification Settings.csv", "Fitbit - iOS App Notification Settings"); +} + +function fitbit_height(): PipelineOp { + return jsonMany( + "Personal & Account/height-*.json", + "Fitbit - Height", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_weight(): PipelineOp { + return jsonMany( + "Personal & Account/weight-*.json", + "Fitbit - Weight", + ["logId", "weight", "bmi", "date", "time", "source"], + `.[] | [.logId, .weight, .bmi, .date, .time, .source] | @csv` + ); +} + +// ============================================================ +// Physical Activity +// ============================================================ + +function fitbit_active_zone_minutes(): PipelineOp { + return csvMany( + "Physical Activity/Active Zone Minutes - *.csv", + "Fitbit - Active Zone Minutes", + ["date_time", "heart_zone_id", "total_minutes"] + ); +} + +function fitbit_activity_goals(): PipelineOp { + return csvOne("Physical Activity/Activity Goals.csv", "Fitbit - Activity Goals"); +} + +function fitbit_daily_readiness(): PipelineOp { + return csvMany( + "Physical Activity/Daily Readiness User Properties - *.csv", + "Fitbit - Daily Readiness User Properties", + ["property_type", "value", "last_update"] + ); +} + +function fitbit_calories(): PipelineOp { + return jsonMany( + "Physical Activity/calories-*.json", + "Fitbit - Calories", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_vo2_max(): PipelineOp { + return jsonMany( + "Physical Activity/demographic_vo2_max-*.json", + "Fitbit - Demographic VO2 Max", + ["dateTime", "demographicVO2Max", "demographicVO2MaxError", "filteredDemographicVO2Max", "filteredDemographicVO2MaxError"], + `.[] | [.dateTime, .value.demographicVO2Max, .value.demographicVO2MaxError, .value.filteredDemographicVO2Max, .value.filteredDemographicVO2MaxError] | @csv` + ); +} + +function fitbit_distance(): PipelineOp { + return jsonMany( + "Physical Activity/distance-*.json", + "Fitbit - Distance", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_exercises(): PipelineOp { + return jsonMany( + "Physical Activity/exercise-*.json", + "Fitbit - Exercises", + ["logId", "activityName", "activityTypeId", "averageHeartRate", "calories", "duration", "activeDuration", "steps", "logType", "startTime", "hasGps"], + `.[] | [.logId, .activityName, .activityTypeId, (.averageHeartRate // ""), .calories, .duration, .activeDuration, (.steps // ""), .logType, .startTime, .hasGps] | @csv` + ); +} + +function fitbit_heart_rate(): PipelineOp { + return jsonMany( + "Physical Activity/heart_rate-*.json", + "Fitbit - Heart Rate", + ["dateTime", "bpm", "confidence"], + `.[] | [.dateTime, .value.bpm, .value.confidence] | @csv` + ); +} + +function fitbit_lightly_active_minutes(): PipelineOp { + return jsonMany( + "Physical Activity/lightly_active_minutes-*.json", + "Fitbit - Lightly Active Minutes", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_moderately_active_minutes(): PipelineOp { + return jsonMany( + "Physical Activity/moderately_active_minutes-*.json", + "Fitbit - Moderately Active Minutes", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_resting_heart_rate(): PipelineOp { + // Some entries have null value; filter those out + return jsonMany( + "Physical Activity/resting_heart_rate-*.json", + "Fitbit - Resting Heart Rate", + ["dateTime", "value", "error"], + `.[] | select(.value != null and .value.value != null) | [.dateTime, .value.value, .value.error] | @csv` + ); +} + +function fitbit_sedentary_minutes(): PipelineOp { + return jsonMany( + "Physical Activity/sedentary_minutes-*.json", + "Fitbit - Sedentary Minutes", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_steps(): PipelineOp { + return jsonMany( + "Physical Activity/steps-*.json", + "Fitbit - Steps", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +function fitbit_swim_lengths(): PipelineOp { + return jsonMany( + "Physical Activity/swim_lengths_data-*.json", + "Fitbit - Swim Lengths", + ["dateTime", "lapDurationSec", "strokeCount", "swimStrokeType", "swimAlgorithmType"], + `.[] | [.dateTime, .value.lapDurationSec, .value.strokeCount, .value.swimStrokeType, .value.swimAlgorithmType] | @csv` + ); +} + +function fitbit_time_in_hr_zones(): PipelineOp { + return jsonMany( + "Physical Activity/time_in_heart_rate_zones-*.json", + "Fitbit - Time in Heart Rate Zones", + ["dateTime", "BELOW_DEFAULT_ZONE_1", "IN_DEFAULT_ZONE_1", "IN_DEFAULT_ZONE_2", "IN_DEFAULT_ZONE_3"], + `.[] | [.dateTime, (.value.valuesInZones.BELOW_DEFAULT_ZONE_1 // ""), (.value.valuesInZones.IN_DEFAULT_ZONE_1 // ""), (.value.valuesInZones.IN_DEFAULT_ZONE_2 // ""), (.value.valuesInZones.IN_DEFAULT_ZONE_3 // "")] | @csv` + ); +} + +function fitbit_very_active_minutes(): PipelineOp { + return jsonMany( + "Physical Activity/very_active_minutes-*.json", + "Fitbit - Very Active Minutes", + ["dateTime", "value"], + `.[] | [.dateTime, .value] | @csv` + ); +} + +// ============================================================ +// Sleep +// ============================================================ + +function fitbit_sleep(): PipelineOp { + return jsonMany( + "Sleep/sleep-*.json", + "Fitbit - Sleep", + ["logId", "dateOfSleep", "startTime", "endTime", "duration", "minutesToFallAsleep", "minutesAsleep", "minutesAwake", "minutesAfterWakeup", "timeInBed", "efficiency", "type", "infoCode", "logType", "mainSleep", "deepMinutes", "wakeMinutes", "lightMinutes", "remMinutes"], + `.[] | [.logId, .dateOfSleep, .startTime, .endTime, .duration, .minutesToFallAsleep, .minutesAsleep, .minutesAwake, .minutesAfterWakeup, .timeInBed, .efficiency, .type, .infoCode, .logType, (.mainSleep | tostring), (.levels.summary.deep.minutes // ""), (.levels.summary.wake.minutes // ""), (.levels.summary.light.minutes // ""), (.levels.summary.rem.minutes // "")] | @csv` + ); +} + +function fitbit_sleep_score(): PipelineOp { + return csvOne("Sleep/sleep_score.csv", "Fitbit - Sleep Score"); +} + +function fitbit_daily_spo2(): PipelineOp { + return csvMany( + "Sleep/Daily SpO2 - *.csv", + "Fitbit - Daily SpO2", + ["timestamp", "average_value", "lower_bound", "upper_bound"] + ); +} + +function fitbit_minute_spo2(): PipelineOp { + return csvMany( + "Sleep/Minute SpO2 - *.csv", + "Fitbit - Minute SpO2", + ["timestamp", "value"] + ); +} + +function fitbit_device_temperature(): PipelineOp { + return csvMany( + "Sleep/Device Temperature - *.csv", + "Fitbit - Device Temperature", + ["recorded_time", "temperature", "sensor_type"] + ); +} + +// ============================================================ +// Social +// ============================================================ + +function fitbit_badges(): PipelineOp { + return pipe( + cd("Social/badge.json"), read(), + cmd(["jq", "-r", ` + ["encodedId", "badgeType", "value", "timesAchieved", "dateTime", "name", "shortName", "category"], + (.[] | [.encodedId, .badgeType, .value, .timesAchieved, .dateTime, .name, .shortName, .category]) + | @csv + `]), + assignMeta({ + idValue: "Fitbit - Badges", + columnMeta: ["any", "text", "numeric", "numeric", "isodatetime", "text", "text", "text"], + perRowTags: "fitbit", + }) + ); +} + +// ============================================================ +// Stress +// ============================================================ + +function fitbit_stress_score(): PipelineOp { + return csvOne("Stress/Stress Score.csv", "Fitbit - Stress Score"); +} + +// ============================================================ +// Google Data / Health Fitness Data +// ============================================================ + +function fitbit_google_calibration(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad.csv", "Fitbit - Google Calibration Status"); +} + +function fitbit_google_goal_settings(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/GoalSettingsHistory.csv", "Fitbit - Google Goal Settings History"); +} + +function fitbit_google_irn_state(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/TakeoutIrnUserState.csv", "Fitbit - Google IRN User State"); +} + +function fitbit_google_app_settings(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/UserAppSettingData.csv", "Fitbit - Google App Setting Data"); +} + +function fitbit_google_demographic(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/UserDemographicData.csv", "Fitbit - Google Demographic Data"); +} + +function fitbit_google_legacy_settings(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/UserLegacySettingData.csv", "Fitbit - Google Legacy Setting Data"); +} + +function fitbit_google_mbd(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/UserMBDData.csv", "Fitbit - Google MBD Data"); +} + +function fitbit_google_profile(): PipelineOp { + return csvOne("Google Data/Health Fitness Data/UserProfileData.csv", "Fitbit - Google Profile Data"); +} + +function fitbit_google_exercises(): PipelineOp { + // Extension-less date-suffixed files (CSVs without .csv extension) + return csvMany( + "Google Data/Health Fitness Data/UserExercises_*", + "Fitbit - Google Exercises", + ["exercise_id", "exercise_start", "exercise_end", "utc_offset", "exercise_created", "exercise_last_updated", "activity_name", "log_type", "pool_length", "pool_length_unit", "intervals", "distance_units", "tracker_total_calories", "tracker_total_steps", "tracker_total_distance_mm", "tracker_total_altitude_mm", "tracker_avg_heart_rate", "tracker_peak_heart_rate", "tracker_avg_pace_mm_per_second", "tracker_avg_speed_mm_per_second", "tracker_peak_speed_mm_per_second", "tracker_auto_stride_run_mm", "tracker_auto_stride_walk_mm", "tracker_swim_lengths", "tracker_pool_length", "tracker_pool_length_unit", "tracker_cardio_load", "manually_logged_total_calories", "manually_logged_total_steps", "manually_logged_total_distance_mm", "manually_logged_pool_length", "manually_logged_pool_length_unit", "events", "activity_type_probabilities", "autodetected_confirmed", "autodetected_start_timestamp", "autodetected_end_timestamp", "autodetected_utc_offset", "autodetected_activity_name", "autodetected_sensor_based_activity_name", "deletion_reason", "activity_label", "suggested_start_timestamp", "suggested_end_timestamp", "reconciliation_status"] + ); +} + +function fitbit_google_sleep_scores(): PipelineOp { + return csvMany( + "Google Data/Health Fitness Data/UserSleepScores_*", + "Fitbit - Google Sleep Scores", + ["sleep_id", "sleep_score_id", "data_source", "score_utc_offset", "score_time", "overall_score", "duration_score", "composition_score", "revitalization_score", "sleep_time_minutes", "deep_sleep_minutes", "rem_sleep_percent", "resting_heart_rate", "sleep_goal_minutes", "waso_count_long_wakes", "waso_count_all_wake_time", "restlessness_normalized", "hr_below_resting_hr", "sleep_score_created", "sleep_score_last_updated"] + ); +} + +function fitbit_google_sleep_stages(): PipelineOp { + return csvMany( + "Google Data/Health Fitness Data/UserSleepStages_*", + "Fitbit - Google Sleep Stages", + ["sleep_id", "sleep_stage_id", "sleep_stage_type", "start_utc_offset", "sleep_stage_start", "end_utc_offset", "sleep_stage_end", "data_source", "sleep_stage_created", "sleep_stage_last_updated"] + ); +} + +function fitbit_google_sleeps(): PipelineOp { + return csvMany( + "Google Data/Health Fitness Data/UserSleeps_*", + "Fitbit - Google Sleeps", + ["sleep_id", "sleep_type", "minutes_in_sleep_period", "minutes_after_wake_up", "minutes_to_fall_asleep", "minutes_asleep", "minutes_awake", "minutes_longest_awakening", "minutes_to_persistent_sleep", "start_utc_offset", "sleep_start", "end_utc_offset", "sleep_end", "data_source", "sleep_created", "sleep_last_updated"] + ); +} + +// ============================================================ +// Google Data / Physical Activity +// ============================================================ + +function fitbit_google_active_minutes(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/active_minutes_[0-9]*.csv", + "Fitbit - Google Active Minutes", + ["timestamp", "light", "moderate", "very", "data source"] + ); +} + +function fitbit_google_active_zone_minutes(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/active_zone_minutes_[0-9]*.csv", + "Fitbit - Google Active Zone Minutes", + ["timestamp", "heart rate zone", "total minutes", "data source"] + ); +} + +function fitbit_google_activity_level(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/activity_level_[0-9]*.csv", + "Fitbit - Google Activity Level", + ["timestamp", "level", "data source"] + ); +} + +function fitbit_google_body_temperature(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/body_temperature_[0-9]*.csv", + "Fitbit - Google Body Temperature", + ["timestamp", "temperature celsius", "data source"] + ); +} + +function fitbit_google_calories(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/calories_[0-9]*.csv", + "Fitbit - Google Calories", + ["timestamp", "calories", "data source"] + ); +} + +function fitbit_google_calories_in_hr_zone(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/calories_in_heart_rate_zone_[0-9]*.csv", + "Fitbit - Google Calories in HR Zone", + ["timestamp", "heart rate zone type", "kcal", "data source"] + ); +} + +function fitbit_google_cardio_load(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/cardio_load_[0-9]*.csv", + "Fitbit - Google Cardio Load", + ["timestamp", "workout", "background", "total", "data source"] + ); +} + +function fitbit_google_daily_oxygen_saturation(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/daily_oxygen_saturation_[0-9]*.csv", + "Fitbit - Google Daily Oxygen Saturation", + ["timestamp", "average percentage", "lower bound percentage", "upper bound percentage", "baseline percentage", "standard deviation percentage", "data source"] + ); +} + +function fitbit_google_distance(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/distance_[0-9]*.csv", + "Fitbit - Google Distance", + ["timestamp", "distance", "data source"] + ); +} + +function fitbit_google_heart_rate(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/heart_rate_[0-9]*.csv", + "Fitbit - Google Heart Rate", + ["timestamp", "beats per minute", "data source"] + ); +} + +function fitbit_google_heart_rate_variability(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/heart_rate_variability_[0-9]*.csv", + "Fitbit - Google Heart Rate Variability", + ["timestamp", "root mean square of successive differences milliseconds", "standard deviation milliseconds", "data source"] + ); +} + +function fitbit_google_live_pace(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/live_pace_[0-9]*.csv", + "Fitbit - Google Live Pace", + ["timestamp", "steps", "distance millimeters", "altitude gain millimeters", "data source"] + ); +} + +function fitbit_google_oxygen_saturation(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/oxygen_saturation_[0-9]*.csv", + "Fitbit - Google Oxygen Saturation", + ["timestamp", "oxygen saturation percentage", "data source"] + ); +} + +function fitbit_google_respiratory_rate_sleep(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/respiratory_rate_sleep_summary_[0-9]*.csv", + "Fitbit - Google Respiratory Rate Sleep Summary", + ["timestamp", "deep sleep stats - milli breaths per minute", "deep sleep stats - standard deviation milli breaths per minute", "deep sleep stats - signal to noise", "light sleep stats - milli breaths per minute", "light sleep stats - standard deviation milli breaths per minute", "light sleep stats - signal to noise", "rem sleep stats - milli breaths per minute", "rem sleep stats - standard deviation milli breaths per minute", "rem sleep stats - signal to noise", "full sleep stats - milli breaths per minute", "full sleep stats - standard deviation milli breaths per minute", "full sleep stats - signal to noise", "data source"] + ); +} + +function fitbit_google_sedentary_period(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/sedentary_period_[0-9]*.csv", + "Fitbit - Google Sedentary Period", + ["start time", "end time", "data source"] + ); +} + +function fitbit_google_steps(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/steps_[0-9]*.csv", + "Fitbit - Google Steps", + ["timestamp", "steps", "data source"] + ); +} + +function fitbit_google_swim_lengths(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/swim_lengths_data_[0-9]*.csv", + "Fitbit - Google Swim Lengths", + ["timestamp", "lap time", "stroke count", "stroke type", "data source"] + ); +} + +function fitbit_google_time_in_hr_zone(): PipelineOp { + return csvMany( + "Google Data/Physical Activity/time_in_heart_rate_zone_[0-9]*.csv", + "Fitbit - Google Time in HR Zone", + ["timestamp", "heart rate zone type", "data source"] + ); +} + +// Single (non-date-suffixed) Google Physical Activity files +function fitbit_google_cardio_acute_chronic(): PipelineOp { + return csvOne("Google Data/Physical Activity/cardio_acute_chronic_workload_ratio.csv", "Fitbit - Google Cardio Acute Chronic Workload Ratio"); +} + +function fitbit_google_cardio_load_observed(): PipelineOp { + return csvOne("Google Data/Physical Activity/cardio_load_observed_interval.csv", "Fitbit - Google Cardio Load Observed Interval"); +} + +function fitbit_google_daily_hrv(): PipelineOp { + return csvOne("Google Data/Physical Activity/daily_heart_rate_variability.csv", "Fitbit - Google Daily Heart Rate Variability"); +} + +function fitbit_google_daily_hr_zones(): PipelineOp { + return csvOne("Google Data/Physical Activity/daily_heart_rate_zones.csv", "Fitbit - Google Daily Heart Rate Zones"); +} + +function fitbit_google_daily_readiness(): PipelineOp { + return csvOne("Google Data/Physical Activity/daily_readiness.csv", "Fitbit - Google Daily Readiness"); +} + +function fitbit_google_daily_respiratory_rate(): PipelineOp { + return csvOne("Google Data/Physical Activity/daily_respiratory_rate.csv", "Fitbit - Google Daily Respiratory Rate"); +} + +function fitbit_google_daily_resting_hr(): PipelineOp { + return csvOne("Google Data/Physical Activity/daily_resting_heart_rate.csv", "Fitbit - Google Daily Resting Heart Rate"); +} + +function fitbit_google_demographic_vo2max(): PipelineOp { + return csvOne("Google Data/Physical Activity/demographic_vo2max.csv", "Fitbit - Google Demographic VO2 Max"); +} + +function fitbit_google_height(): PipelineOp { + return csvOne("Google Data/Physical Activity/height.csv", "Fitbit - Google Height"); +} + +function fitbit_google_weight(): PipelineOp { + return csvOne("Google Data/Physical Activity/weight.csv", "Fitbit - Google Weight"); +} + +// ============================================================ +// Main export +// ============================================================ + +export function fitbit(): PipelineOp { + return pipe( + assignMeta({ idValue: t => `Fitbit - ${t.basename}` }), + branchGen(function* () { + // Application + yield fitbit_account_access_events(); + yield fitbit_account_management_events(); + yield fitbit_email_audit(); + yield fitbit_retired_passwords(); + + // Heart + yield fitbit_afib_enrollment(); + yield fitbit_hr_notification_alerts(); + yield fitbit_hr_notification_profile(); + + // Menstrual Health + yield fitbit_menstrual_cycles(); + yield fitbit_menstrual_symptoms(); + yield fitbit_menstrual_birth_control(); + yield fitbit_menstrual_settings(); + + // Other + yield fitbit_oxygen_variation(); + + // Personal & Account + yield fitbit_profile(); + yield fitbit_devices(); + yield fitbit_trackers(); + yield fitbit_scales(); + yield fitbit_tracker_config(); + yield fitbit_ios_notifications(); + yield fitbit_height(); + yield fitbit_weight(); + + // Physical Activity + yield fitbit_active_zone_minutes(); + yield fitbit_activity_goals(); + yield fitbit_daily_readiness(); + yield fitbit_calories(); + yield fitbit_vo2_max(); + yield fitbit_distance(); + yield fitbit_exercises(); + yield fitbit_heart_rate(); + yield fitbit_lightly_active_minutes(); + yield fitbit_moderately_active_minutes(); + yield fitbit_resting_heart_rate(); + yield fitbit_sedentary_minutes(); + yield fitbit_steps(); + yield fitbit_swim_lengths(); + yield fitbit_time_in_hr_zones(); + yield fitbit_very_active_minutes(); + + // Sleep + yield fitbit_sleep(); + yield fitbit_sleep_score(); + yield fitbit_daily_spo2(); + yield fitbit_minute_spo2(); + yield fitbit_device_temperature(); + + // Social + yield fitbit_badges(); + + // Stress + yield fitbit_stress_score(); + + // Google Data / Health Fitness Data + yield fitbit_google_calibration(); + yield fitbit_google_goal_settings(); + yield fitbit_google_irn_state(); + yield fitbit_google_app_settings(); + yield fitbit_google_demographic(); + yield fitbit_google_legacy_settings(); + yield fitbit_google_mbd(); + yield fitbit_google_profile(); + yield fitbit_google_exercises(); + yield fitbit_google_sleep_scores(); + yield fitbit_google_sleep_stages(); + yield fitbit_google_sleeps(); + + // Google Data / Physical Activity (date-suffixed) + yield fitbit_google_active_minutes(); + yield fitbit_google_active_zone_minutes(); + yield fitbit_google_activity_level(); + yield fitbit_google_body_temperature(); + yield fitbit_google_calories(); + yield fitbit_google_calories_in_hr_zone(); + yield fitbit_google_cardio_load(); + yield fitbit_google_daily_oxygen_saturation(); + yield fitbit_google_distance(); + yield fitbit_google_heart_rate(); + yield fitbit_google_heart_rate_variability(); + yield fitbit_google_live_pace(); + yield fitbit_google_oxygen_saturation(); + yield fitbit_google_respiratory_rate_sleep(); + yield fitbit_google_sedentary_period(); + yield fitbit_google_steps(); + yield fitbit_google_swim_lengths(); + yield fitbit_google_time_in_hr_zone(); + + // Google Data / Physical Activity (single files) + yield fitbit_google_cardio_acute_chronic(); + yield fitbit_google_cardio_load_observed(); + yield fitbit_google_daily_hrv(); + yield fitbit_google_daily_hr_zones(); + yield fitbit_google_daily_readiness(); + yield fitbit_google_daily_respiratory_rate(); + yield fitbit_google_daily_resting_hr(); + yield fitbit_google_demographic_vo2max(); + yield fitbit_google_height(); + yield fitbit_google_weight(); + }) + ); +} diff --git a/data-export/google.ts b/data-export/google.ts index bffe82b..05c7bfe 100644 --- a/data-export/google.ts +++ b/data-export/google.ts @@ -1,107 +1,115 @@ -import { TaskTargetPipelineHelper } from "./task.ts"; +import { pipe, branch, cmd, assignMeta, cd, glob, read, branchGen, type PipelineOp } from "./task.ts"; import { htmlSelectorChunkedDuplex } from "./html.ts"; -export function google(this: TaskTargetPipelineHelper){ - const p = this.setId(t=>`Google - ${t.basename}`); // Generic ID for everything in here - const col: Set = new Set(); - - // TODO: There is a root takeout folder +export function google(){ + return pipe( + // Generic ID for everything in here + assignMeta({ idValue: t=>`Google - ${t.basename}` }), + branchGen(function*() { + // TODO: There is a root takeout folder - p.collect(col).cd('Access Log Activity/Activities - A list of Google services accessed by.csv').read() - p.collect(col).cd('Devices - A list of devices (i.e. Nest, Pixel, iPh.csv').read() + yield pipe(cd('Access Log Activity/Activities - A list of Google services accessed by.csv'), read()) + yield pipe(cd('Devices - A list of devices (i.e. Nest, Pixel, iPh.csv'), read()) - // Assignments - data was empty - // Business messages - GMB messages, there's some but so far outside of what I want - // TODO: Calendar, exports an .ics + // Assignments - data was empty + // Business messages - GMB messages, there's some but so far outside of what I want + // TODO: Calendar, exports an .ics - // a = t.fork().cd(`Chrome`) - // TODO: Assersses and mode.json - // TODO: Bookmarks.csv - // TODO: Device Information.json - // TODO: Dictionary.csv - // TODO: ... - p.collect(col).cd('Chrome/History.json') - .read() - // TODO: Typed Url", no data - // TODO: "session", complex data - // Omitted .ptoken and .client_id for now. I think ptoken is maybe for the history API? client_id is base64 something... - // TODO: time_usec IS WRONG!! Needs to be ms - .cmd(["jq", "-r", `["favicon_url","page_transition","title","url","time_usec"], - ( - ."Browser History"[] - | [.favicon_url, .page_transition, .title, .url, (.time_usec | todateiso8601)] - ) - | @csv`]) + // a = t.fork().cd(`Chrome`) + // TODO: Assersses and mode.json + // TODO: Bookmarks.csv + // TODO: Device Information.json + // TODO: Dictionary.csv + // TODO: ... + yield pipe( + cd('Chrome/History.json'), + read(), + // TODO: Typed Url", no data + // TODO: "session", complex data + // Omitted .ptoken and .client_id for now. I think ptoken is maybe for the history API? client_id is base64 something... + // TODO: time_usec IS WRONG!! Needs to be ms + cmd(["jq", "-r", `["favicon_url","page_transition","title","url","time_usec"], + ( + ."Browser History"[] + | [.favicon_url, .page_transition, .title, .url, (.time_usec | todateiso8601)] + ) + | @csv + `]) + ); - // TODO: Contactss, exports an .vcf - // TODO: ... + // TODO: Contactss, exports an .vcf + // TODO: ... - // a = t.fork().cd(`Google Pay`) - p.collect(col).cd(`Google Pay/Google transactions`).glob(`transactions_*.csv`) - .read() - .csvSink() - // .fork("a").cd(`Money sends and requests`) - // .fork().cd(`Money sends and requests.csv`) - // .read() - // .cmd(t=>["sqlite-utils", "insert", "your.db", t.basename, "-", "--csv", "--detect-types"]) - // TODO: One more folder, and it only has a pdf + // a = t.fork().cd(`Google Pay`) + yield pipe( + cd(`Google Pay/Google transactions`), + glob(`transactions_*.csv`), + read(), + // .fork("a").cd(`Money sends and requests`) + // .fork().cd(`Money sends and requests.csv`) + // .read() + // .cmd(t=>["sqlite-utils", "insert", "your.db", t.basename, "-", "--csv", "--detect-types"]) + // TODO: One more folder, and it only has a pdf + ); - // TODO: Google Play Movies _ TV - no data - // TODO: ... + // TODO: Google Play Movies _ TV - no data + // TODO: ... - p.collect(col).cd("Location History/Location History.json") - .read() - // TODO: This is missing - // "altitude" : 158, - // "verticalAccuracy" : 68 - // and the activity models. I had no idea google tries to determine if I'm "tilting" - .cmd(["jq", "-r", `["timestamp","latitudeE7","longitudeE7","accuracy"], - ( - .locations[] - | [.timestampMs | todateiso8601, .latitudeE7, .longitudeE7, .accuracy] - ) - | @csv`]) - .csvSink() - // There's also the semantic history but that's an entire nother can of worms - // it seems like + yield pipe( + cd("Location History/Location History.json"), + read(), + // TODO: This is missing + // "altitude" : 158, + // "verticalAccuracy" : 68 + // and the activity models. I had no idea google tries to determine if I'm "tilting" + cmd(["jq", "-r", `["timestamp","latitudeE7","longitudeE7","accuracy"], + ( + .locations[] + | [.timestampMs | todateiso8601, .latitudeE7, .longitudeE7, .accuracy] + ) + | @csv + `]) + ); + // There's also the semantic history but that's an entire nother can of worms + // it seems like - // TODO: Needs no-headers! - // a = t.fork().cd(`My Activity`) - // a.fork().glob(`**/MyActivity.html`) - // .setId(t=>`Google - ${t.basenameN(2)}`) - // .read() - // .pipe(()=>{ - // // Parses the MyActivity format, chunking it into pieces of HTML text - // // and then parsing out the text - // const dup = htmlSelectorChunkedDuplex( - // (tag, attrs)=>{ - // // TODO: We also probably want to get and parse each - // // ".content-cell.mdl-typography--caption" as well (it - // // has location for websearches and sometimes a details field) - // // but then we have to get ".mdl-grid" and parse it - // return attrs.class?.includes("content-cell") - // && attrs.class?.includes("mdl-typography--body-1") - // && !attrs.class?.includes("mdl-typography--text-right") - // }, - // (chunk)=>{ - // const text = chunk.innerText; - // const split = text.split("\n"); - // const timestamp = split.pop(); // TODO: need to parse this - // const rest = split.join("\n"); - // // TODO: Escape instead of replace - // const restSafe = rest.replace(/"/g, "'").replace(/\n/g,"\\n"); // escape newlines and quotes - // // Return a CSV - // return `"${restSafe}","${timestamp}"\n`; - // } - // ); - // return dup; - // }) + // TODO: Needs no-headers! + // a = t.fork().cd(`My Activity`) + // a.fork().glob(`**/MyActivity.html`) + // .setId(t=>`Google - ${t.basenameN(2)}`) + // .read() + // .pipe(()=>{ + // // Parses the MyActivity format, chunking it into pieces of HTML text + // // and then parsing out the text + // const dup = htmlSelectorChunkedDuplex( + // (tag, attrs)=>{ + // // TODO: We also probably want to get and parse each + // // ".content-cell.mdl-typography--caption" as well (it + // // has location for websearches and sometimes a details field) + // // but then we have to get ".mdl-grid" and parse it + // return attrs.class?.includes("content-cell") + // && attrs.class?.includes("mdl-typography--body-1") + // && !attrs.class?.includes("mdl-typography--text-right") + // }, + // (chunk)=>{ + // const text = chunk.innerText; + // const split = text.split("\n"); + // const timestamp = split.pop(); // TODO: need to parse this + // const rest = split.join("\n"); + // // TODO: Escape instead of replace + // const restSafe = rest.replace(/"/g, "'").replace(/\n/g,"\\n"); // escape newlines and quotes + // // Return a CSV + // return `"${restSafe}","${timestamp}"\n`; + // } + // ); + // return dup; + // }) - // TODO: News - // TODO: Profile - // TODO: Tasks - No data - - return Array.from(col); + // TODO: News + // TODO: Profile + // TODO: Tasks - No data + }) + ); }; diff --git a/data-export/io.ts b/data-export/io.ts new file mode 100644 index 0000000..208a88d --- /dev/null +++ b/data-export/io.ts @@ -0,0 +1,52 @@ +import fs from 'node:fs/promises'; +import fsSync from 'node:fs'; +import { DatabaseSync } from "node:sqlite"; +import { type ProcessOutputAggregate, type RunOutput, TaskTarget, runAll, type ProcessOutputSimple } from "./task.ts"; +import { ProcessOutput } from 'zx'; + + +async function loadCSVTable( + db: DatabaseSync, + target: TaskTarget, + result: ProcessOutput | ProcessOutputAggregate | ProcessOutputSimple +) { + const id = target.id; + const table = id; + const tmpPath = `/tmp/${id}.csv`; + // console.log(`Writing ${tmpPath}`); + const fd = await fs.open(tmpPath, 'w'); + await fs.writeFile(fd, result.stdout, { encoding: 'utf8' }); + await fd.close(); + // console.log(`Loading ${tmpPath} → table ${table}`); + + db.exec(`CREATE VIRTUAL TABLE temp.intermediate USING csv(filename='${tmpPath}', header);`); + db.exec(`CREATE TABLE "${table}" AS SELECT * FROM intermediate;`); + db.exec(`DROP TABLE IF EXISTS intermediate;`); + return; +} + +// TODO: This should really have the same name throughout the codebase? +export const runPipeline = runAll; + +/** + * @param db Must be a DatabaseSync with the csv.so extension enabled + */ +export async function loadIntoDb(db: DatabaseSync, runOutput: RunOutput[]) { + for (const {result, target} of runOutput) { + await loadCSVTable(db, target, result); + } +} +export function getDefaultDB(): DatabaseSync { + const db = new DatabaseSync(":memory:", { allowExtension: true }); + db.loadExtension("/home/cobertos/sqlite-files/csv.so") + db.enableLoadExtension(false); + return db; +} +export async function dumpDBToDisk(db: DatabaseSync, dumpPath: string) { + if (fsSync.existsSync(dumpPath)) { + await fs.unlink(dumpPath); // unlink the old + } + + // Dump it all to the path specified + db.exec(`VACUUM main INTO '${dumpPath}'`); +} diff --git a/data-export/parallel.ts b/data-export/parallel.ts index 27add5c..4998ac1 100644 --- a/data-export/parallel.ts +++ b/data-export/parallel.ts @@ -1,17 +1,18 @@ -import { $, type ProcessOutput } from 'zx'; import os from 'os'; -import { type TaskTarget, run } from "./task.ts"; -$.verbose = false; - -type ResultMap = Map; - -export async function parallel( - targets: TaskTarget[], +/**Generic parallel runner with optional logging + * Runs `targets` with `runFn` up to a maximum of `maxConcurrency` amount at a time + * Shaped in a way that expects generally something that returns zx.ProcessOutput (or + * something with .duration and .ok built-in to the return) + * @param runFn Should NOT throw. Return { ok: false } instead + */ +export async function parallel( + targets: T[], + runFn: (t: T)=>Promise, quiet: boolean = false, maxConcurrency: number = os.cpus().length -): Promise { - const results = new Map(); +): Promise { + const resultMap = new Map(); const total = targets.length; let completed = 0; @@ -42,14 +43,14 @@ export async function parallel( process.stderr.write(`\r${formatEta()}`.padEnd(80)); } - async function runJob(t: TaskTarget): Promise { + async function runJob(t: T): Promise { running++; printStatus(); - const result = await run(t); + const result = await runFn(t); completionTimes.push(result.duration); - results.set(t.id, result); + resultMap.set(t, result); running--; completed++; @@ -77,10 +78,18 @@ export async function parallel( // Final status line process.stderr.write('\n'); const totalSeconds = ((Date.now() - startTime) / 1000).toFixed(1); - const failed = Array.from(results.values().filter(p => !p.ok)); - process.stderr.write( - `\nCompleted ${total} jobs in ${totalSeconds}s (${failed.length} failed)\n` - ); + const failed = Array.from(resultMap.values().filter(p => !p.ok)); + if (!quiet) { + process.stderr.write( + `\nCompleted ${total} jobs in ${totalSeconds}s (${failed.length} failed)\n` + ); + } - return results; + const output = targets + .map(t => { + const r = resultMap.get(t)!; + return r; + }); + + return output; } diff --git a/data-export/snapchat.ts b/data-export/snapchat.ts new file mode 100644 index 0000000..82b73c9 --- /dev/null +++ b/data-export/snapchat.ts @@ -0,0 +1,260 @@ +import { pipe, cmd, assignMeta, cd, read, branchGen, type PipelineOp } from "./task.ts"; + +/** + * jq helper to normalize Snapchat's "YYYY-MM-DD HH:MM:SS UTC" timestamps + * to ISO 8601 "YYYY-MM-DDTHH:MM:SS+00:00". Passes through empty strings/nulls. + */ +const SNAPISO = `def snapiso: if . == null or . == "" then "" elif endswith(" UTC") then ((.[:-4] | gsub(" "; "T")) + "+00:00") else . end;`; + +/** Login events from account.json */ +function snapchat_login_history(): PipelineOp { + return pipe( + cd(`json/account.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["ip", "country", "created", "status", "device"], + ( + .["Login History"][] + | [.IP, .Country, (.Created | snapiso), .Status, .Device] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Login History", + columnMeta: ["text", "text", "isodatetime", "text", "text"], + perRowDescription: 'Login from {0} ({1}) on {2}', + perRowTags: "snapchat,security", + }) + ); +} + +/** + * Account changes over time from account_history.json. + * Flattens display name changes, email changes, password changes, + * bitmoji links, and data download requests into one table. + */ +function snapchat_account_history(): PipelineOp { + return pipe( + cd(`json/account_history.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["change_type", "date", "detail"], + ( + ( + (.["Display Name Change"][]? | {t: "display_name_change", d: .Date, v: ."Display Name"}), + (.["Email Change"][]? | {t: "email_change", d: .Date, v: ."Email Address"}), + (.["Password Change"][]? | {t: "password_change", d: .Date, v: ""}), + (.["Snapchat Linked to Bitmoji"][]? | {t: "linked_to_bitmoji", d: .Date, v: ""}), + (.["Download My Data Reports"][]? | {t: "data_download", d: .Date, v: (.Status + " / " + ."Email Address")}) + ) + | [.t, (.d | snapiso), .v] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Account History", + columnMeta: ["text", "isodatetime", "text"], + perRowDescription: '{0} on {1}: {2}', + perRowTags: "snapchat,security", + }) + ); +} + +/** + * All friend relationship types from friends.json combined into one table. + * relationship_type column distinguishes Friends, Blocked Users, etc. + */ +function snapchat_friends(): PipelineOp { + return pipe( + cd(`json/friends.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["relationship_type", "username", "display_name", "created_at", "modified_at", "source"], + ( + ["Friends", "Friend Requests Sent", "Blocked Users", "Deleted Friends", "Ignored Snapchatters", "Pending Requests"][] + as $key + | .[$key][]? + | [$key, .Username, ."Display Name", (."Creation Timestamp" | snapiso), (."Last Modified Timestamp" | snapiso), .Source] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Friends", + columnMeta: ["text", "text", "text", "isodatetime", "isodatetime", "text"], + perRowDescription: '{0}: {2} (@{1}) since {3}', + perRowTags: "snapchat", + }) + ); +} + +/** + * All chat messages from chat_history.json. + * Keys in that file are friend usernames; they become the conversation_with column. + */ +function snapchat_chat_history(): PipelineOp { + return pipe( + cd(`json/chat_history.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["conversation_with", "from", "media_type", "created", "content", "is_sender"], + ( + to_entries[] + | .key as $conv + | .value[] + | [$conv, .From, ."Media Type", (.Created | snapiso), (.Content // ""), (.IsSender | tostring)] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Chat History", + columnMeta: ["text", "sender", "text", "isodatetime", "text", "any"], + perRowDescription: '"{4}" from {1} in {0} at {3}', + perRowTags: "snapchat,message", + }) + ); +} + +/** Approximate visited areas from location_history.json */ +function snapchat_location_visits(): PipelineOp { + return pipe( + cd(`json/location_history.json`), read(), + cmd(["jq", "-r", ` + ["time", "city", "region", "postal_code"], + ( + .["Areas you may have visited in the last two years"][] + | [.Time, .City, .Region, ."Postal Code"] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Location Visits", + columnMeta: ["any", "text", "text", "any"], + perRowDescription: 'Visited {1}, {2} ({3}) around {0}', + perRowTags: "snapchat,location", + }) + ); +} + +/** Spotlight/story activity from shared_story.json */ +function snapchat_spotlight(): PipelineOp { + return pipe( + cd(`json/shared_story.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["story_date", "story_url", "action_type", "view_time"], + ( + .["Spotlight History"][] + | [(.["Story Date"] | snapiso), .["Story URL"], .["Action Type"], .["View Time"]] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Spotlight", + columnMeta: ["isodatetime", "url", "text", "any"], + perRowDescription: '{2} on spotlight at {0}', + perRowTags: "snapchat", + }) + ); +} + +/** Terms of service acceptance history from terms_history.json */ +function snapchat_terms_history(): PipelineOp { + return pipe( + cd(`json/terms_history.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["version", "acceptance_date"], + ( + .["Snap Inc. Terms of Service"][] + | [.Version, (.["Acceptance Date"] | snapiso)] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Terms History", + columnMeta: ["text", "isodatetime"], + perRowDescription: 'Accepted terms {0} on {1}', + perRowTags: "snapchat", + }) + ); +} + +/** Third-party app permissions from connected_apps.json */ +function snapchat_connected_apps(): PipelineOp { + return pipe( + cd(`json/connected_apps.json`), read(), + cmd(["jq", "-r", `${SNAPISO} + ["app", "time", "type"], + ( + .Permissions[] + | [.App, (.Time | snapiso), .Type] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Connected App Permissions", + columnMeta: ["text", "isodatetime", "text"], + perRowDescription: '{2} permission for {0} on {1}', + perRowTags: "snapchat", + }) + ); +} + +/** Email campaign subscription preferences from email_campaign_history.json */ +function snapchat_email_campaigns(): PipelineOp { + return pipe( + cd(`json/email_campaign_history.json`), read(), + cmd(["jq", "-r", ` + ["campaign", "opt_out_status"], + ( + .["Email Campaign Subscriptions"][] + | [.["Email Campaign"], .["Opt Out Status"]] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - Email Campaigns", + columnMeta: ["text", "text"], + perRowDescription: 'Email campaign "{0}": {1}', + perRowTags: "snapchat", + }) + ); +} + +/** + * In-app survey responses from in_app_surveys.json. + * Keys are dynamic dates ("Survey YYYY/MM/DD"), flattened via to_entries. + */ +function snapchat_surveys(): PipelineOp { + return pipe( + cd(`json/in_app_surveys.json`), read(), + cmd(["jq", "-r", ` + ["survey", "time", "question", "response"], + ( + to_entries[] + | .key as $survey + | .value[] + | [$survey, .Time, .["Survey Question"], .["Survey Response"]] + ) + | @csv + `]), + assignMeta({ + idValue: "Snapchat - In-App Surveys", + columnMeta: ["text", "any", "text", "text"], + perRowDescription: 'Survey "{2}": {3}', + perRowTags: "snapchat", + }) + ); +} + +export function snapchat(): PipelineOp { + return pipe( + assignMeta({ idValue: t => `Snapchat - ${t.basename}` }), + branchGen(function* () { + yield snapchat_login_history(); + yield snapchat_account_history(); + yield snapchat_friends(); + yield snapchat_chat_history(); + yield snapchat_location_visits(); + yield snapchat_spotlight(); + yield snapchat_terms_history(); + yield snapchat_connected_apps(); + yield snapchat_email_campaigns(); + yield snapchat_surveys(); + }) + ); +} diff --git a/data-export/task.ts b/data-export/task.ts index df884b8..ceb1b00 100644 --- a/data-export/task.ts +++ b/data-export/task.ts @@ -2,8 +2,10 @@ import nodePath from 'node:path'; import fs from 'node:fs'; import { strict as assert } from "node:assert"; import { ZipFS } from "./zipFs.ts"; -import { globSync } from "glob"; -import { $, ProcessPromise, quote } from "zx"; +import { $, ProcessOutput, quote } from "zx"; +import { parallel } from "./parallel.ts"; + +$.verbose = false; type FSImpl = { isZip?: boolean; @@ -11,6 +13,7 @@ type FSImpl = { init?(): Promise; ready?: boolean; + globSync: typeof fs["globSync"]; statSync: typeof fs["statSync"]; existsSync: typeof fs["existsSync"]; @@ -38,19 +41,20 @@ function safe(s: string) { interface TaskTargetOp { type: "read" | "mid"; - toShell(target: TaskTarget): string; + toShell(target: TaskTarget): string | undefined; clone(): TaskTargetOp; } class TaskTargetRead implements TaskTargetOp { get type(){ return "read" as const; } toShell(target: TaskTarget) { if (target.fsImpl.isZip) { + // Read the file to stdout from the target inside the zip file + // This relies on the internals of fsImpl a bit to have the path to + // the root zip so we can create a command against it assert(target.fsImpl.zipPath, "Should have a zipPath"); - // We need to be able to do this return `7z x ${quote(target.fsImpl.zipPath)} -so ${quote(target.path)}`; } - // TODO : Implement when reading from a zip file return `cat ${quote(target.path)}`; } clone() { @@ -93,17 +97,67 @@ class TaskTargetCmd implements TaskTargetOp { } type ValidId = string | ((t: TaskTarget)=>string); +export const COLUMN_TYPES = { + /**A numeric value*/ + "numeric": {}, + /**ISO Datetime*/ + "isodatetime": {}, + /**Urls*/ + "url": {}, + /**Freetext*/ + "text": {}, + /**For anything untyped*/ + "any": {}, + /**The sender/originator of a row (maps to Owner in Timelinize)*/ + "sender": {}, + /**The receiver/recipient of a row (maps to RelSent entity in Timelinize)*/ + "receiver": {}, + /**Latitude coordinate*/ + "lat": {}, + /**Longitude coordinate*/ + "lng": {}, + "TODO": {} +}; + +/**Column metadata. Just a string into the TYPES*/ +type ColumnMeta = (keyof typeof COLUMN_TYPES | undefined); +// Make non-optional version of just the metadata values of TaskTarget +type TaskTargetMeta = Required>; + export class TaskTarget { + /**The current path pointed to by this TaskTarget*/ path: string; + /**The fsImpl used to access the .path*/ fsImpl: FSImpl = defaultFSImpl; + /**The pipeline of things to do to the above path to get an stdout of the output*/ pipeline: TaskTargetOp[]; - idValue: ValidId | undefined; - postFns: ((t: TaskTarget)=>Promise)[]; + + // == Metadata, user configurable, no good defaults == + /**Id of the TaskTarget + * string - Static id + * fn returning string - Function can derive the id from a task target even after a glob() and cd() operation + **/ + idValue?: ValidId; + /**For every output CSV, this defines a description of that CSV per-row + * Use the items {0}, {1} to template + * Example: For a CSV with a row format like ["time", "sender", "sendee", "message"] + * you might do something like '"{3}" sent from {2} to {1}' + * */ + perRowDescription?: string; + /**A CSV of tags that is added to every row of the table (TODO: no template functionality currently)*/ + perRowTags?: string; + /**Metadata about the columns*/ + columnMeta?: ColumnMeta[]; + /**Whether or not to aggregate to a single task (everything with the id value idValue)*/ + aggregate?: boolean; + /**Names of the columns to aggregate with*/ + aggregateColumns?: string[]; + /**A metadata TaskTarget for this TaskTarget, if one exists*/ + metaIdValue?: ValidId; constructor(path: string){ this.path = path; this.pipeline = []; - this.postFns = []; } exists() { @@ -136,6 +190,15 @@ export class TaskTarget { } return safe(this.idValue); } + get metaId() { + if (!this.metaIdValue) { + return undefined; + } + if (typeof this.metaIdValue === "function") { + return safe(this.metaIdValue(this)); + } + return safe(this.metaIdValue); + } /**Changes the current directory of the target*/ cd(path: string): TaskTarget { @@ -154,10 +217,7 @@ export class TaskTarget { /**Get a glob off of the target*/ glob(globPath: string): TaskTarget[] { globPath = this._joinPath(globPath); - const items = globSync(globPath, { - cwd: '/DUMMYCWD', - fs: this.fsImpl - }); + const items = this.fsImpl.globSync(globPath); const ret = items.map(i => new TaskTarget(i)); // TODO: This should probably clone() ret.forEach(t => t.fsImpl = this.fsImpl); // Should all use the same fsImpl @@ -168,10 +228,16 @@ export class TaskTarget { clone(): TaskTarget { const t = new TaskTarget(this.path); t.fsImpl = this.fsImpl; // holds no state, just needs same impl - t.idValue = this.idValue; - t.postFns = this.postFns.slice(); t.pipeline = this.pipeline.slice() .map(p => p.clone()); + // metadata + t.idValue = this.idValue; + t.perRowDescription = this.perRowDescription; + t.perRowTags = this.perRowTags; + t.columnMeta = this.columnMeta?.slice(); + t.metaIdValue = this.metaIdValue; + t.aggregate = this.aggregate; + t.aggregateColumns = this.aggregateColumns?.slice(); return t; } @@ -186,14 +252,11 @@ export class TaskTarget { toShell() { const shell = this.pipeline .map(p => p.toShell(this)) + .filter(p => !!p) // remove empty strings and undefined .join(" | ") return shell; } - pushPostFn(fn: ((t: TaskTarget)=>Promise)) { - this.postFns.push(fn); - } - cmd(cmd: ValidCmd) { this.pushToPipeline(new TaskTargetCmd(cmd)); return this; @@ -202,92 +265,82 @@ export class TaskTarget { this.pushToPipeline(new TaskTargetRead()); return this; } - setId(idValue: ValidId) { - this.idValue = idValue; + assignMeta(meta: Partial) { + Object.assign(this, { + ...meta, + // Clone this deeply so no shared object references + columnMeta: meta.columnMeta?.slice() + }); return this; } - post(fn: any) { - this.pushPostFn(fn); - } - types( - types: string[] - ) { - // TODO: - return this; - } - csvSink( - summarization?: [string, string][] - ) { - // TODO: - return this; - - // Ingest this csv into the database at the given id - // this.cmd(t=>["sqlite-utils", "insert", "your.db", t.id, "-", "--csv", "--detect-types"]); - // Add a post processing function for these targets that prints out the summarization - // stats - // this.post(async (t: TaskTarget)=>{ - // // We only do the first one so far for the summarization - // let queryLine: string; - // let formatFn: (r: any)=>string; - // const [columnName, type] = summarization?.[0] ?? [undefined, undefined]; - // if (type === "numeric") { - // queryLine = `min(${columnName}) as lo, max(${columnName}) as hi, count(*) as n`; - // formatFn = (r: any)=>`${r.n} rows from ${r.lo} to ${r.hi} for ${t.id}`; - // } - // else { - // queryLine = `count(*) as n`; - // formatFn = (r: any)=>`${r.n} rows for ${t.id}`; - // } - - // const cmd = "sqlite-utils"; - // const args = ["query", "your.db", `select ${queryLine} from ${t.id}`] - // const { stdout, stderr } = await execFile(cmd, args); - // const results = JSON.parse(stdout); - // const result = results[0]; // should only be one result in the array for this type of query - // const logLine = formatFn(result); - // (t as any).log = logLine; - // }); - - // return this; - } } -export function each(targets: TaskTarget[], fn: (t: TaskTarget)=>void) { - for (const t of targets) { - fn(t); - } +export interface PipelineOp { + (targets: TaskTarget[]): TaskTarget[] | Promise; } -export function map(targets: TaskTarget[], fn: (t: TaskTarget)=>TaskTarget) { - const newTargets = []; - for (const t of targets) { - newTargets.push(fn(t)); - } - return newTargets; + +export function cd(path: string): PipelineOp { + return (targets: TaskTarget[]) => targets.map(t => t.clone().cd(path)); } -export function cd(targets: TaskTarget[], path: string): TaskTarget[] { - return targets.map(t => t.clone().cd(path)); +export function glob(globPath: string): PipelineOp { + return (targets: TaskTarget[]) => targets.map(t => t.glob(globPath)).flat(); } -export function glob(targets: TaskTarget[], globPath: string): TaskTarget[] { - return targets.map(t => t.glob(globPath)).flat(); +export function unzip(): PipelineOp { + return async (targets: TaskTarget[]) => Promise.all(targets.map(t => t.unzip())); } -export async function unzip(targets: TaskTarget[]): Promise { - return Promise.all(targets.map(t => t.unzip())); +export function read(): PipelineOp { + return (targets: TaskTarget[]) => targets.map(t => t.clone().read()) } -export function read(targets: TaskTarget[]): TaskTarget[] { - return targets.map(t => t.clone().read()) +export function cmd(cmd: ValidCmd): PipelineOp { + return (targets: TaskTarget[]) => targets.map(t => t.clone().cmd(cmd)) } -export function cmd(targets: TaskTarget[], cmd: ValidCmd): TaskTarget[] { - return targets.map(t => t.clone().cmd(cmd)) +export function assignMeta(meta: Partial): PipelineOp { + return (targets: TaskTarget[]) => targets.map(t => t.clone().assignMeta(meta)) } -export function setId(targets: TaskTarget[], id: ValidId): TaskTarget[] { - return targets.map(t => t.clone().setId(id)) + +export function each(fn: (t: TaskTarget)=>TaskTarget): PipelineOp { + return (targets: TaskTarget[])=> targets.map(fn); } +export function pipe(...ops: PipelineOp[]): PipelineOp { + return async (targets: TaskTarget[]) => { + for (const op of ops) { + targets = await op(targets); + } + return targets; + }; +} +export function branch(...ops: PipelineOp[]): PipelineOp { + return async (targets: TaskTarget[]) => { + const targetsArrays = await Promise.all(ops.map(op => op(targets))); + return targetsArrays.flat(); + }; +} +export function branchGen(genFn: ()=>Generator): PipelineOp { + const opsToBranch = Array.from(genFn()); + return (targets: TaskTarget[]) => { + return branch(...opsToBranch)(targets); + }; +} + +export async function execPaths(entries: ({path: string, op: PipelineOp })[]) { + return (await Promise.all( + // Map every entry path into a TaskTarget and run the PipelineOp with + // that TaskTarget + entries + .map(async ({path,op})=>{ + const targets = [new TaskTarget(path)]; + return await op(targets); + }) + )).flat(); +} + /**Verify, anything that fails is skipped and throws an error*/ export async function verify(targets: TaskTarget[]) { const outTargets: TaskTarget[] = []; for (const t of targets) { // Make sure fsImpl is ready + // TODO: DO NOT PUT THIS IN VERIFY, this should go somewhere in the task building stuff... if ("ready" in t.fsImpl && !t.fsImpl.ready && t.fsImpl.init) { await t.fsImpl.init(); } @@ -302,109 +355,133 @@ export async function verify(targets: TaskTarget[]) { outTargets.push(t); } + return outTargets; } -/**Writes a manifest for parallel, a TSV where each record is an id + the shell to run - * @todo Enforce doing a verify before we output? - */ -export function getTSVManifest(targets: TaskTarget[]): string { - let out: string[] = []; - for (const t of targets) { - const shell = t.toShell(); - out.push(`${t.id}\t${shell}`); - } - - return out.join("\n"); +export interface ProcessOutputAggregate { + stdout: string; + stderr: string; + exitCodes: (number | null)[]; + duration: number; + ok: boolean; +} +export interface ProcessOutputSimple { + stdout: string; + stderr: string; + exitCode: number; + duration: number; + ok: boolean; } -export function getTaskManifest(targets: TaskTarget[]): [string, string][] { - let out: [string, string][] = []; - for (const t of targets) { - const shell = t.toShell(); - out.push([t.id, shell] as const); +function combineProcessOutputAggregate(poa: ProcessOutputAggregate | undefined, t: TaskTarget, po: ProcessOutput) { + if (!poa) { + assert(t.aggregateColumns, "aggregate TaskTarget must have aggregateColumns"); + const headers = t.aggregateColumns.join(",") + "\n"; + return { + stdout: headers + po.stdout, + stderr: po.stderr, + exitCodes: [po.exitCode], + duration: po.duration, + ok: po.ok + }; } - return out; + // Comes with a builtin "\n" from jq on stdout and stderr, no need to add + // a trailing one + poa.stdout += po.stdout; + poa.stderr += po.stderr; + poa.exitCodes.push(po.exitCode); + poa.duration += po.duration; + poa.ok &&= po.ok; + return poa; } -function collectionSwap(a: TaskTargetPipelineHelper, b: TaskTargetPipelineHelper) { - if (!a.__collection) { - return; - } - - // Remove a, add b - const collection = a.__collection; - delete a.__collection; - collection.delete(a); - b.__collection = collection; - collection.add(b); +export interface RunOutput { + target: TaskTarget, + result: ProcessOutput | ProcessOutputAggregate | ProcessOutputSimple } -export class TaskTargetPipelineHelper extends Array { - __collection?: Set; - - static pipeline(t: TaskTarget[]): TaskTargetPipelineHelper { - if (Object.getPrototypeOf(t) === TaskTargetPipelineHelper.prototype) { - return t as any; // Already done - } - Object.setPrototypeOf(t, TaskTargetPipelineHelper.prototype); - return t as any; - } - - _fn(fn: (t: TaskTarget[])=>TaskTarget[]): TaskTargetPipelineHelper { - const p = TaskTargetPipelineHelper.pipeline(this); - const t = fn(p); - const p2 = TaskTargetPipelineHelper.pipeline(t); - collectionSwap(p, p2); // Move collection pointer to the new item, ends always end up in the collection - return p2; - } - async _afn(fn: (t: TaskTarget[])=>Promise): Promise { - const p = TaskTargetPipelineHelper.pipeline(this); - const t = await fn(p); - const p2 = TaskTargetPipelineHelper.pipeline(t); - collectionSwap(p, p2); // Move collection pointer to the new item, ends always end up in the collection - return p2; - } - - cd(path: string): TaskTargetPipelineHelper { - return this._fn(t => cd(t, path)); - } - glob(globPath: string): TaskTargetPipelineHelper { - return this._fn(t => glob(t, globPath)); - } - async unzip(): Promise { - return this._afn(unzip); - } - read(): TaskTargetPipelineHelper { - return this._fn(read); - } - cmd(_cmd: ValidCmd): TaskTargetPipelineHelper { - return this._fn(t => cmd(t, _cmd)); - } - setId(id: ValidId): TaskTargetPipelineHelper { - return this._fn(t => setId(t, id)); - } - - types(...args: any[]) { - // TODO: no-op - return this; - } - csvSink(...args: any[]) { - // TODO: no-op - return this; - } - /** - * @todo Nested versions of this don't currently work, but they could if we - * turn __collection into an array of collections - */ - collect(_c: Set) { - this.__collection = _c; - return this; - } -} - -export async function run(target: TaskTarget): Promise { +export async function run(target: TaskTarget): Promise { const command = target.toShell(); return await $({ nothrow: true })`bash -c ${command}`; +} + +export async function runAll(targets: TaskTarget[]): Promise { + const finalTargets = await verify(targets); + const results = await parallel(finalTargets, run, true); + + const nonAggregateTargets: TaskTarget[] = finalTargets.filter(t => !t.aggregate); + const nonAggregateResults: RunOutput[] = []; + const aggregateResultsMap: Record = {}; + + // == Aggregate tables == + // Some TaskTargets have .aggregate: true, which means they should all be combined + // into a single task with the id of the .id property + for (const [idx, r] of results.entries()) { + const t = finalTargets[idx]; + if (!t.aggregate) { + nonAggregateResults.push({ + target: t, + result: r + }); + continue; + } + const aggregateId = t.id; + const prevResult = aggregateResultsMap[aggregateId]?.result; + aggregateResultsMap[aggregateId] = { + target: t, // Use target t for metadata, so it will use the last target + result: combineProcessOutputAggregate(prevResult as (ProcessOutputAggregate | undefined), t, r) + }; + } + + // == Metadata table == + // Each TaskTarget has things like perRowDescription and other things we want to store + // and output. this creates a single TaskTarget for all that perTable metadata + function csvEscape(s: string | undefined) { + if (s === undefined) { + return ""; + } + if (s.includes("\"") || s.includes(",") || s.includes("\n")) { + return `"${s.replace(/\"/g, "\"\"")}"`; + } + return s; + } + let metadataCSV = "id,perRowDescription,perRowTags,columnMeta,metaId\n"; + for (const t of nonAggregateTargets) { + const tableNamePart = t.id; + const perRowDescriptionPart = t.perRowDescription; + const perRowTagsPart = t.perRowTags; + const columnMetaPart = t.columnMeta?.join(",") ?? ""; + const metaIdPart = t.metaId; + metadataCSV += [ + csvEscape(tableNamePart), + csvEscape(perRowDescriptionPart), + csvEscape(perRowTagsPart), + csvEscape(columnMetaPart), + csvEscape(metaIdPart) + ].join(",") + "\n"; + } + // Won't be removed by verify() because we're adding it after that's used + // TODO: Would be nice to bake this into TaskTarget/verify for tasks that dont point + // to a real path + const metadataTarget = new TaskTarget(""); + metadataTarget + // id, perRowDescription, perRowTags, columnMeta, metaId + .assignMeta({ + idValue: "base_data_manager_metadata", + columnMeta: ["any", "any", "any", "any", "any"], + perRowTags: "internal", + }); + const metadataResult= { + stdout: metadataCSV, + stderr: "", + exitCode: 0, + duration: 0, // TODO + ok: true + }; + const metadataRunOutput: RunOutput = { target: metadataTarget, result: metadataResult }; + + const aggregateResults: RunOutput[] = Object.values(aggregateResultsMap); + return aggregateResults.concat(nonAggregateResults).concat(metadataRunOutput); } \ No newline at end of file diff --git a/data-export/zipFs.ts b/data-export/zipFs.ts index c54cb88..355ae7c 100644 --- a/data-export/zipFs.ts +++ b/data-export/zipFs.ts @@ -1,8 +1,7 @@ import { strict as assert } from "node:assert"; -import fs from "node:fs"; -import path from "node:path"; import { Readable } from "node:stream"; import yauzl from "yauzl"; +import { globSync } from "glob"; function removeDummyCwd(path: string) { if (path.startsWith("/DUMMYCWD/")) { @@ -309,6 +308,16 @@ export class ZipFS { } } + globSync(globPath: string) { + const selfImpl = this.getImpl(); + return globSync(globPath, { + fs: selfImpl as any, + // We strip this later, this is so glob() doesn't use the cwd of the current + // process, which matches no files inside the .zip file + cwd: `/DUMMYCWD` + }); + } + getImpl() { // Because glob uses ...xxx notation to unpack ourselves into a _new_ object // we need to make sure that we DONT use a class, otherwise the properties @@ -319,6 +328,7 @@ export class ZipFS { init: this.init.bind(this), ready: this.ready, + globSync: this.globSync.bind(this), statSync: this.statSync.bind(this), createReadStream: this.createReadStream.bind(this), createWriteStream: this.createWriteStream.bind(this), diff --git a/main.ts b/main.ts index a8e234a..914eef9 100644 --- a/main.ts +++ b/main.ts @@ -1,67 +1,90 @@ -import fs from 'node:fs/promises'; -import nodePath from "node:path"; -import { DatabaseSync } from "node:sqlite"; -import "./data-export/facebook.ts"; +import { type DatabaseSync } from "node:sqlite"; +import { fileURLToPath } from "node:url"; import { google } from "./data-export/google.ts"; -import { TaskTargetPipelineHelper } from "./data-export/task.ts"; +import { facebook, facebook_v2 } from "./data-export/facebook.ts"; +import { type TaskTarget, execPaths } from "./data-export/task.ts"; +import * as DataIO from "./data-export/io.ts"; -declare module "./data-export/task.ts" { - interface TaskTargetPipelineHelper { - google: typeof google; - } -} +const __filename = fileURLToPath(import.meta.url); -Object.assign(TaskTargetPipelineHelper.prototype, { - google -}); +export const startTime = Date.now(); +export const elapsed = ()=>`${((Date.now() - startTime) / 1000).toFixed(2)}s`; -function loadIntoSqlite( - paths: string[], - sqlitePath: string -) { - // Open an in-memory db for speed - const db = new DatabaseSync(":memory:", { allowExtension: true }); - db.loadExtension("/home/cobertos/sqlite-files/csv.so") - db.enableLoadExtension(false); - for (const path of paths) { - const table = nodePath.basename(path, ".csv"); - console.log(`Loading ${path} → table ${table}`); +export async function loadTaskInNewDb(targets: TaskTarget[]): Promise { + console.log(`${elapsed()} - Run all targets`); + const out = await DataIO.runPipeline(targets); + console.log(`${elapsed()} - Final targets exported to CSV. Got ${out.length} targets`); - // const headers = lines[0].split(","); - // const columnsSql = headers.map(h => `"${h}" TEXT`).join(", "); - db.exec(`CREATE VIRTUAL TABLE temp.intermediate USING csv(filename='${path}');`); - db.exec(`CREATE TABLE "${table}" AS SELECT * FROM intermediate;`); - db.exec(`DROP TABLE IF EXISTS intermediate;`); - } + // TODO: Add an option to output everything plainly as CSV in a single directory - // Dump it all to the path specified - db.exec(`VACUUM main INTO '${sqlitePath}'`); - db.close(); + console.log(`${elapsed()} - Building combined database table in :memory:`); + const db = DataIO.getDefaultDB(); + await DataIO.loadIntoDb(db, out); + + const tableCount = db.prepare(`SELECT COUNT(*) as count FROM base_data_manager_metadata`).get()!.count; + console.log(`${elapsed()} - Single database built with ${tableCount} tables`); + + return db; } async function main() { - const t = TaskTargetPipelineHelper; - // TODO: - // t.fork().cd("/home/cobertos/Seafile/archive/ExportedServiceData/facebook/formapcast_facebook-DEADNAME-May2021-json") - // .facebook() + // Configurable stuff + const sqlitePath = 'your.db'; - // (await t.fork().cd("/home/cobertos/Seafile/archive/ExportedServiceData/facebook/facebook-x-2025-11-29-x.zip").zip()).facebook_v2(); + console.log(`${elapsed()} - Building targets`); + const targets = await execPaths([ + {path: "/home/cobertos/Seafile/archive/ExportedServiceData/facebook/formapcast_facebook-DEADNAME-May2021-json", op: facebook()} + // {path: "/home/cobertos/Seafile/projects/base-data-manager/test/fixtures/facebook-json-2021-05-01", op: facebook()} + // {path: "/home/cobertos/Seafile/archive/ExportedServiceData/facebook/facebook-x-2025-11-29-x.zip", op: pipe(unzip(), facebook_v2())} + // {path: "/home/cobertos/Seafile/archive/ExportedServiceData/google/2023-NAMEwork-001", op: facebook_v2()} + ]); + console.log(`${elapsed()} - Found ${targets.filter(t => !t.aggregate).length} possible targets`); - // t.fork().cd("/home/cobertos/Seafile/archive/ExportedServiceData/google/2023-NAMEwork-001") - // .google() + const db = await loadTaskInNewDb(targets); + console.log(`${elapsed()} - Writing database to disk at "${sqlitePath}"`); + DataIO.dumpDBToDisk(db, sqlitePath); - // let zipTask = t.fork().zip("/home/cobertos/Seafile/archive/ExportedServiceData/facebook/facebook-DEADNAME-May2021-json.zip"); - // await (zipTask.fsImpl as any).init(); - - // zipTask.facebook(); - // Now take the output and load it all into a single SQLITE file - // const entries = await fs.readdir('OUTTEST', { withFileTypes: true }); - // const csvFiles = entries - // .filter(e => e.isFile() && e.name.endsWith(".csv")) - // .map(e => nodePath.join('OUTTEST', e.name)); - // await fs.unlink('your.db'); - // loadIntoSqlite(csvFiles, 'your.db'); + console.log(`${elapsed()} - Database written to disk`); } -main(); \ No newline at end of file +if (process.argv[1] === __filename) { + main(); +} + +// TODO: Move this into here +// csvSink( +// summarization?: [string, string][] +// ) { +// // TODO: +// return this; + +// // Ingest this csv into the database at the given id +// // this.cmd(t=>["sqlite-utils", "insert", "your.db", t.id, "-", "--csv", "--detect-types"]); +// // Add a post processing function for these targets that prints out the summarization +// // stats +// // this.post(async (t: TaskTarget)=>{ +// // // We only do the first one so far for the summarization +// // let queryLine: string; +// // let formatFn: (r: any)=>string; +// // const [columnName, type] = summarization?.[0] ?? [undefined, undefined]; +// // if (type === "numeric") { +// // queryLine = `min(${columnName}) as lo, max(${columnName}) as hi, count(*) as n`; +// // formatFn = (r: any)=>`${r.n} rows from ${r.lo} to ${r.hi} for ${t.id}`; +// // } +// // else { +// // queryLine = `count(*) as n`; +// // formatFn = (r: any)=>`${r.n} rows for ${t.id}`; +// // } + +// // const cmd = "sqlite-utils"; +// // const args = ["query", "your.db", `select ${queryLine} from ${t.id}`] +// // const { stdout, stderr } = await execFile(cmd, args); +// // const results = JSON.parse(stdout); +// // const result = results[0]; // should only be one result in the array for this type of query +// // const logLine = formatFn(result); +// // (t as any).log = logLine; +// // }); + +// // return this; +// } \ No newline at end of file diff --git a/package.json b/package.json index e0ed113..47edcaf 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "type": "module", "scripts": { "test": "node --enable-source-maps --test --experimental-transform-types --no-warnings ./test/task.ts", - "test2": "node --enable-source-maps --test --experimental-transform-types --no-warnings ./test/facebook.ts", - "test-update-snapshots": "node --enable-source-maps --test --experimental-transform-types --no-warnings --test-update-snapshots ./test/facebook.ts", + "test-scrub": "node --enable-source-maps --test --experimental-transform-types --no-warnings ./test/scrub.ts", + "test-exports": "node --enable-source-maps --test --experimental-transform-types --no-warnings ./test/data-export.ts", + "test-exports-snapshots": "node --enable-source-maps --test --experimental-transform-types --no-warnings --test-update-snapshots ./test/data-export.ts", "dev": "vite --port 2223", "server": "node --experimental-transform-types server/server.ts", "prototype": "node --import ./util/tsx-loader.js --import ./util/ignore-css-loader.js --experimental-transform-types server/prototype.ts" @@ -20,7 +21,6 @@ "@types/duplexify": "^3.6.5", "@types/yauzl": "^2.10.3", "duplexify": "^4.1.3", - "fp-ts": "^2.16.11", "glob": "^13.0.0", "htmlparser2": "^10.0.0", "yauzl": "^3.2.0", @@ -28,6 +28,9 @@ }, "devDependencies": { "@types/node": "^24.1.0", + "csv-parse": "^6.1.0", + "csv-stringify": "^6.6.0", + "diff": "^8.0.3", "typescript": "^5.9.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d5812c..12acd03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,6 @@ importers: duplexify: specifier: ^4.1.3 version: 4.1.3 - fp-ts: - specifier: ^2.16.11 - version: 2.16.11 glob: specifier: ^13.0.0 version: 13.0.0 @@ -36,6 +33,15 @@ importers: '@types/node': specifier: ^24.1.0 version: 24.10.0 + csv-parse: + specifier: ^6.1.0 + version: 6.1.0 + csv-stringify: + specifier: ^6.6.0 + version: 6.6.0 + diff: + specifier: ^8.0.3 + version: 8.0.3 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -62,6 +68,16 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + csv-parse@6.1.0: + resolution: {integrity: sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==} + + csv-stringify@6.6.0: + resolution: {integrity: sha512-YW32lKOmIBgbxtu3g5SaiqWNwa/9ISQt2EcgOq0+RAIFufFp9is6tqNnKahqE5kuKvrnYAzs28r+s6pXJR8Vcw==} + + diff@8.0.3: + resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + engines: {node: '>=0.3.1'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -89,9 +105,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - fp-ts@2.16.11: - resolution: {integrity: sha512-LaI+KaX2NFkfn1ZGHoKCmcfv7yrZsC3b8NtWsTVQeHkq4F27vI5igUuO53sxqDEa2gNQMHFPmpojDw/1zmUK7w==} - glob@13.0.0: resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} engines: {node: 20 || >=22} @@ -182,6 +195,12 @@ snapshots: buffer-crc32@0.2.13: {} + csv-parse@6.1.0: {} + + csv-stringify@6.6.0: {} + + diff@8.0.3: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -215,8 +234,6 @@ snapshots: entities@6.0.1: {} - fp-ts@2.16.11: {} - glob@13.0.0: dependencies: minimatch: 10.1.1 diff --git a/summary.ts b/summary.ts new file mode 100644 index 0000000..d6f2c03 --- /dev/null +++ b/summary.ts @@ -0,0 +1,216 @@ +import { type DatabaseSync, type SQLOutputValue } from "node:sqlite"; +import { fileURLToPath } from "node:url"; +import { stringify } from "csv-stringify/sync"; +import { facebook } from "./data-export/facebook.ts"; +import { execPaths, COLUMN_TYPES } from "./data-export/task.ts"; +import { elapsed, loadTaskInNewDb } from "./main.ts"; + +const __filename = fileURLToPath(import.meta.url); + +type ColumnMetaType = keyof typeof COLUMN_TYPES; + +interface MetadataRow { + id: string; + perRowTags?: string; + columnMeta: ColumnMetaType[]; + columnNames: string[]; +} + +// ── query helpers ───────────────────────────────────────────────────────────── + +function q(name: string) { + return `"${name}"`; +} + +/** Format a number compactly: integer if whole, otherwise 4 sig figs */ +function fmt(v: number): string { + if (!isFinite(v)) return String(v); + if (Number.isInteger(v)) return String(v); + return v.toPrecision(4).replace(/\.?0+$/, ''); +} + +/** Non-null filter: treats SQL NULL, empty string, and the literal "null" as missing */ +function notNull(col: string) { + return `${q(col)} IS NOT NULL AND ${q(col)} != '' AND ${q(col)} != 'null'`; +} + +function rowCount(db: DatabaseSync, table: string): number { + return (db.prepare(`SELECT count(*) as n FROM ${q(table)}`).get() as { n: number }).n; +} + +function datetimeRange(db: DatabaseSync, table: string, col: string): string { + const r = db.prepare( + `SELECT MIN(${q(col)}) as lo, MAX(${q(col)}) as hi FROM ${q(table)} WHERE ${notNull(col)}` + ).get() as { lo: string | null; hi: string | null }; + if (!r.lo) return '(no dates)'; + // Trim to date portion if it looks like a full ISO datetime — keeps the line shorter + const trim = (s: string) => s.length > 10 && s[10] === 'T' ? s.slice(0, 10) : s; + return `${trim(r.lo)}..${trim(r.hi!)}`; +} + +function numericRange(db: DatabaseSync, table: string, col: string): { lo: number; hi: number } | null { + const r = db.prepare( + `SELECT MIN(CAST(${q(col)} AS REAL)) as lo, MAX(CAST(${q(col)} AS REAL)) as hi + FROM ${q(table)} WHERE ${notNull(col)}` + ).get() as { lo: number | null; hi: number | null }; + return r.lo !== null ? { lo: r.lo, hi: r.hi! } : null; +} + +function topValues(db: DatabaseSync, table: string, col: string, n: number): { distinct: number; top: { v: string; c: number }[] } { + const distinct = (db.prepare( + `SELECT count(distinct ${q(col)}) as d FROM ${q(table)} WHERE ${notNull(col)}` + ).get() as { d: number }).d; + const top = db.prepare( + `SELECT ${q(col)} as v, count(*) as c FROM ${q(table)} WHERE ${notNull(col)} + GROUP BY ${q(col)} ORDER BY c DESC LIMIT ${n}` + ).all() as { v: string; c: number }[]; + return { distinct, top }; +} + +// ── metadata parsing (mirrors timelinize.ts) ────────────────────────────────── + +function getColumnNames(db: DatabaseSync, tableName: string): string[] { + return db.prepare(`PRAGMA table_info(${q(tableName)})`).all().map(c => (c as any).name) as string[]; +} + +function parseMetadataRow(db: DatabaseSync, row: Record): MetadataRow | undefined { + const { id, perRowTags, columnMeta: columnMetaCSV } = row; + if (!id || typeof id !== 'string') return undefined; + + const columnNames = getColumnNames(db, id); + + // columnMeta may be absent for tables without type annotations — still useful to show + let columnMeta: ColumnMetaType[] = []; + if (columnMetaCSV && typeof columnMetaCSV === 'string') { + const parsed = columnMetaCSV.split(',') as ColumnMetaType[]; + if (parsed.length === columnNames.length) { + columnMeta = parsed; + } + } + + return { + id, + perRowTags: typeof perRowTags === 'string' ? perRowTags : undefined, + columnMeta, + columnNames, + }; +} + +/** Maps semantic type names → the actual column name in this table (first match wins) */ +function metaToNames(meta: MetadataRow): Partial> { + const out: Partial> = {}; + for (const [idx, colName] of meta.columnNames.entries()) { + const type = meta.columnMeta[idx]; + if (!type || out[type]) continue; // skip untyped or already-seen types + out[type] = colName; + } + return out; +} + +// ── table row builder ───────────────────────────────────────────────────────── + +const TOP_SENDERS = 3; +const MAX_SENDER_LEN = 20; + +interface SummaryRow { + type: string; + id: string; + n: string; + dates: string; + senders: string; + geo: string; + tags: string; +} + +function buildSummaryRow(db: DatabaseSync, meta: MetadataRow): SummaryRow { + const { id, perRowTags } = meta; + const typeMap = metaToNames(meta); + const n = rowCount(db, id); + + // ── shape label ───────────────────────────────────────────────────────────── + let type: string; + if (typeMap.sender && typeMap.isodatetime) type = 'chat'; + else if (typeMap.isodatetime) type = 'time'; + else type = 'static'; + if (typeMap.lat && typeMap.lng) type += '+geo'; + + // ── datetime range ────────────────────────────────────────────────────────── + const dates = typeMap.isodatetime ? datetimeRange(db, id, typeMap.isodatetime) : ''; + + // ── sender info ───────────────────────────────────────────────────────────── + let senders = ''; + if (typeMap.sender) { + const { distinct, top } = topValues(db, id, typeMap.sender, TOP_SENDERS); + const topStr = top + .map(r => { + const name = r.v.length > MAX_SENDER_LEN ? r.v.slice(0, MAX_SENDER_LEN - 1) + '…' : r.v; + return `${name}×${r.c}`; + }) + .join(', '); + senders = `${distinct} [${topStr}]`; + } + + // ── geo ranges ────────────────────────────────────────────────────────────── + let geo = ''; + const latR = typeMap.lat ? numericRange(db, id, typeMap.lat) : null; + const lngR = typeMap.lng ? numericRange(db, id, typeMap.lng) : null; + if (latR && lngR) { + geo = `[${fmt(latR.lo)}..${fmt(latR.hi)}] [${fmt(lngR.lo)}..${fmt(lngR.hi)}]`; + } else if (latR) { + geo = `lat=[${fmt(latR.lo)}..${fmt(latR.hi)}]`; + } + + return { type, id, n: String(n), dates, senders, geo, tags: perRowTags ?? '' }; +} + +// ── CSV output ──────────────────────────────────────────────────────────────── + +const COLUMNS: { key: keyof SummaryRow; header: string }[] = [ + { key: 'type', header: 'type' }, + { key: 'id', header: 'id' }, + { key: 'n', header: 'n' }, + { key: 'dates', header: 'dates' }, + { key: 'senders', header: 'senders' }, + { key: 'geo', header: 'lat / lng' }, + { key: 'tags', header: 'tags' }, +]; + +function printCSV(summaryRows: SummaryRow[]) { + const records = [ + COLUMNS.map(c => c.header), + ...summaryRows.map(row => COLUMNS.map(c => row[c.key])), + ]; + process.stdout.write(stringify(records)); +} + +// ── main ───────────────────────────────────────────────────────────────────── + +async function main() { + process.stderr.write(`${elapsed()} - Building targets\n`); + const targets = await execPaths([ + {path: "/home/cobertos/Seafile/archive/ExportedServiceData/facebook/formapcast_facebook-DEADNAME-May2021-json", op: facebook()} + // {path: "/home/cobertos/Seafile/archive/ExportedServiceData/fitbit/FullHumanName", op: fitbit()} + ]); + process.stderr.write(`${elapsed()} - Found ${targets.filter(t => !t.aggregate).length} possible targets\n`); + + const db = await loadTaskInNewDb(targets); + + const rows = db.prepare( + `SELECT id, perRowTags, columnMeta FROM base_data_manager_metadata ORDER BY id` + ).all() as Record[]; + + const summaryRows: SummaryRow[] = []; + for (const row of rows) { + const meta = parseMetadataRow(db, row); + if (!meta) continue; + summaryRows.push(buildSummaryRow(db, meta)); + } + + printCSV(summaryRows); + + db.close(); +} + +if (process.argv[1] === __filename) { + main(); +} diff --git a/test/data-export.ts b/test/data-export.ts new file mode 100644 index 0000000..281b7df --- /dev/null +++ b/test/data-export.ts @@ -0,0 +1,93 @@ +import test from "node:test"; +import nodePath from "node:path"; +import fs from "node:fs/promises"; +import { strict as assert } from "node:assert"; + +import { unzip, pipe, execPaths, type PipelineOp } from "../data-export/task.ts"; +import * as DataIO from "../data-export/io.ts"; +import { assertCSVWellFormed } from "./utils/csvUtils.ts"; +import { assertStringEq, ptry } from "./utils/general.ts"; + +import { facebook, facebook_v2 } from "../data-export/facebook.ts"; +import { discord } from "../data-export/discord.ts"; +import { snapchat } from "../data-export/snapchat.ts"; +import { discord_chat_exporter } from "../data-export/discord-chat-exporter.ts"; +import { fitbit } from "../data-export/fitbit.ts"; + +const THIS_FILE = import.meta.dirname; +const SNAPSHOT_DIR = nodePath.join(THIS_FILE, 'snapshots'); +const updateSnapshots = process.execArgv.includes("--test-update-snapshots"); + +/**Custom version of t.snapshot + * * We save each csv id to it's own file (regardless of where it came from) + * * Properly handles \r\n which nodejs's t.snapshot gets rid of because of + * how it encodes it into backticks/template literals + */ +async function snapshotCSV(id: string, csv: string) { + const snapshotFilePath = nodePath.join(SNAPSHOT_DIR, id) + "snapshot.csv"; + if (updateSnapshots) { + // Update the snapshots, do no checking, our internal csv is the source of truth + await fs.writeFile(snapshotFilePath, csv, { encoding: "utf8" }); + } + else { + const [err, prevCSV] = await ptry(fs.readFile(snapshotFilePath, { encoding: "utf8" })); + assert(!err, `Snapshot file '${snapshotFilePath}' did not exist. Perhaps you need to update snapshots, "--test-update-snapshots"?`); + assertStringEq(csv, prevCSV, "csv and snapshot csv should be the same"); + } +} + +async function testPipelineOp(path: string, op: PipelineOp, overwriteIdPrefix?: string) { + const targets = await execPaths([{ path, op }]); + const out = await DataIO.runPipeline(targets); + const idAndCSVs: [string, string][] = []; + // Verify and collect all the id + csv tuples + for (const {target, result} of out) { + const id = target.id; + // Check the result for success + assert.ok(!result.stderr, `Task ${id} should have no stderr output`); + assert.ok(result.ok, `Task ${id} should be okay`); + // Check the CSV itself for correctness + const csv = result.stdout; + assertCSVWellFormed(csv, `${csv}\nTask ${id} should have well-formed csv.`); + + idAndCSVs.push([target.id, csv]); + } + + // Everything is verified for cleanliness coming out of the current run, verify + // against the snapshots + save if we're updating snapshots + const idPrefix = overwriteIdPrefix ?? path.split("/").pop(); // Make unique with the last name of the path + await Promise.all(idAndCSVs.map(([id, csv])=>snapshotCSV(`${idPrefix}_${id}`, csv))); +} + +test("facebook: Can load the 2021-01 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2021-05-01'); + await testPipelineOp(path, facebook()); +}); +test("facebook: Can load the 2021-01 export zipped", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2021-05-01.zip'); + await testPipelineOp(path, pipe(unzip(), facebook())); +}); +test("facebook: Can load the 2025-11 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2025-11-29'); + await testPipelineOp(path, facebook_v2()); +}); + +test("discord: Can load the 2021-05 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/discord-json-2021-01'); + await testPipelineOp(path, discord()); +}); + +test("snapchat: Can load the 2023-11 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/snapchat-2023-11'); + await testPipelineOp(path, snapchat()); +}); + +test("discord-chat-exporter: Can load the 2026-02 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/discord-chat-exporter-2026-02'); + await testPipelineOp(path, discord_chat_exporter()); +}); + +test("fitbit: Can load the 2026-02 export", async () => { + const path = nodePath.join(THIS_FILE, 'fixtures/fitbit-2026-02/FullHumanName'); + await testPipelineOp(path, fitbit(), "fitbit-2026-02"); +}); diff --git a/test/facebook.ts b/test/facebook.ts deleted file mode 100644 index 9835c91..0000000 --- a/test/facebook.ts +++ /dev/null @@ -1,73 +0,0 @@ -import test from "node:test"; -import nodePath from "node:path"; -import { strict as assert } from "node:assert"; -import { finished } from "node:stream/promises"; -import { Readable, Writable } from "node:stream"; -import { TaskTargetPipelineHelper, TaskTarget, verify, getTSVManifest, getTaskManifest, run } from "../data-export/task.ts"; -import { parallel } from "../data-export/parallel.ts"; -import "../data-export/facebook.ts"; - -const THIS_FILE = import.meta.dirname; -const FACEBOOK_V1_DIR = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2021-05-01'); -const FACEBOOK_V1_ZIPPED = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2021-05-01.zip'); -const FACEBOOK_V2_DIR = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2025-11-29'); - -test("facebook: Can load the 2021 export", async (t) => { - const targets = TaskTargetPipelineHelper.pipeline([ - new TaskTarget(FACEBOOK_V1_DIR) - ]) - .facebook(); - - const finalTargets = await verify(targets); - const result = await parallel(finalTargets, true); - for (const [id, r] of result.entries()) { - assert.ok(!r.stderr, `Task ${id} should have no stderr output`); - assert.ok(r.ok, `Task ${id} should be okay`); - } - - const allCSV = Array.from(result.entries()) - .sort() // Keep stable ordering for snapshots - .map(([id, r]) => r.stdout); - - t.assert.snapshot(allCSV); -}); -test("facebook: Can load the 2021 export zipped", async (t) => { - const targets = await TaskTargetPipelineHelper.pipeline([ - new TaskTarget(FACEBOOK_V1_ZIPPED) - ]) - .unzip(); - const targets2 = targets - .facebook(); - - const finalTargets = await verify(targets2); - const result = await parallel(finalTargets, true); - for (const [id, r] of result.entries()) { - assert.ok(!r.stderr, `Task ${id} should have no stderr output`); - assert.ok(r.ok, `Task ${id} should be okay`); - } - - const allCSV = Array.from(result.entries()) - .sort() // Keep stable ordering for snapshots - .map(([id, r]) => r.stdout); - - t.assert.snapshot(allCSV); -}); -test("facebook: Can load the 2025 export", async (t) => { - const targets = TaskTargetPipelineHelper.pipeline([ - new TaskTarget(FACEBOOK_V2_DIR) - ]) - .facebook_v2(); - - const finalTargets = await verify(targets); - const result = await parallel(finalTargets, true); - for (const [id, r] of result.entries()) { - assert.ok(!r.stderr, `Task ${id} should have no stderr output`); - assert.ok(r.ok, `Task ${id} should be okay`); - } - - const allCSV = Array.from(result.entries()) - .sort() // Keep stable ordering for snapshots - .map(([id, r]) => r.stdout); - - t.assert.snapshot(allCSV); -}); diff --git a/test/facebook.ts.snapshot b/test/facebook.ts.snapshot deleted file mode 100644 index fb5c5e6..0000000 --- a/test/facebook.ts.snapshot +++ /dev/null @@ -1,116 +0,0 @@ -exports[`facebook: Can load the 2021 export 1`] = ` -[ - "\\"album\\",\\"uri\\",\\"creation_timestamp\\"\\n\\"xxx\\",\\"photos_and_videos/CoverPhotos_yyyyyy/200x200png.png\\",\\"2024-03-07T15:23:20Z\\"\\n\\"xxx\\",\\"photos_and_videos/CoverPhotos_yyyyyy/200x200png.png\\",\\"2024-07-01T07:46:40Z\\"\\n", - "[\\n \\"from\\",\\n \\"to\\",\\n \\"timestamp\\",\\n \\"body\\"\\n]\\n\\"Me\\",\\"xxx\\",\\"2024-01-13T07:13:20Z\\",\\"xxx\\"\\n\\"Me\\",\\"xxx\\",\\"2024-01-13T07:13:20Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"action\\",\\"ip\\",\\"user_agent\\",\\"datr_cookie\\",\\"city\\",\\"region\\",\\"country\\",\\"site_name\\",\\"timestamp\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"status\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-02-13T14:36:40Z\\"\\n", - "\\"service_name\\",\\"native_app_id\\",\\"username\\",\\"email\\",\\"phone_number\\",\\"name\\"\\n\\"xxx\\",69,\\"xxx\\",\\"not_a_real_email@example.com\\",\\"xxx\\",\\"xxx\\"\\n\\"xxx\\",1707005000,\\"xxx\\",\\"not_a_real_email@example.com\\",,\\"xxx\\"\\n", - "\\"event\\",\\"created_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"datr_cookie\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\",,,\\n\\"xxx\\",\\"2024-02-13T14:36:40Z\\",,,\\n", - "\\"name\\",\\"added_timestamp\\"\\n\\"xxx\\",\\"2024-12-29T08:13:20Z\\"\\n\\"xxx\\",\\"2024-09-02T12:26:40Z\\"\\n", - "\\"name\\",\\"created_timestamp\\",\\"updated_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"location\\",\\"app\\",\\"session_type\\",\\"datr_cookie\\"\\n\\"xxx\\",\\"2024-08-22T01:26:40Z\\",\\"2024-05-11T15:06:40Z\\",\\"1.1.1.1\\",\\"some/path\\",\\"\\",\\"\\",\\"\\",\\"xxx\\"\\n", - "\\"timestamp\\",\\"data\\",\\"title\\"\\n\\"2024-02-08T19:20:00Z\\",\\"TODO\\",\\"xxx\\"\\n\\"2024-01-17T14:00:00Z\\",\\"TODO\\",\\"xxx\\"\\n", - "\\"timestamp\\",\\"email\\",\\"contact_type\\"\\n\\"2024-10-18T07:03:20Z\\",\\"not_a_real_email@example.com\\",69\\n\\"2024-01-21T22:10:00Z\\",\\"not_a_real_email@example.com\\",69\\n", - "\\"name\\"\\n\\"xxx\\"\\n\\"xxx\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-13T13:13:20Z\\"\\n\\"xxx\\",\\"2024-10-31T00:36:40Z\\"\\n", - "\\"game\\",\\"added_timestamp\\"\\n\\"xxx\\",\\"2024-11-03T16:06:40Z\\"\\n", - "\\"title\\",\\"price\\",\\"seller\\",\\"created_timestamp\\",\\"latitude\\",\\"longitude\\",\\"description\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-12-18T05:33:20Z\\",69,69,\\"xxx\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-12-18T05:33:20Z\\",69,69,\\"xxx\\"\\n", - "\\"action\\",\\"timestamp\\",\\"site\\",\\"ip_address\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n\\"xxx\\",\\"2024-04-23T17:56:40Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n", - "\\"timestamp\\",\\"unread\\",\\"href\\",\\"text\\"\\n\\"2024-04-30T08:16:40Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n\\"2024-04-30T08:16:40Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"from\\",\\"to\\",\\"amount\\",\\"currency\\",\\"type\\",\\"status\\",\\"payment_method\\",\\"created_timestamp\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-05T21:36:40Z\\"\\n", - "\\"name\\",\\"uri\\",\\"timestamp\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-01-15T12:00:00Z\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-01-12T06:13:20Z\\"\\n", - "\\"from\\",\\"to\\",\\"rank\\",\\"timestamp\\"\\n\\"xxx\\",\\"xxx\\",69,\\"2024-07-22T19:03:20Z\\"\\n", - "\\"title\\",\\"timestamp\\",\\"reaction\\"\\n,\\"2024-01-14T06:50:00Z\\",\\"xxx\\"\\n,\\"2024-01-14T06:50:00Z\\",\\"xxx\\"\\n", - "\\"title\\",\\"timestamp\\"\\n,\\"2024-10-06T08:56:40Z\\"\\n,\\"2024-10-06T08:56:40Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-08T16:33:20Z\\"\\n\\"xxx\\",\\"2024-09-24T19:10:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-09-27T15:13:20Z\\"\\n\\"xxx\\",\\"2024-08-24T00:40:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-06-23T05:20:00Z\\"\\n\\"xxx\\",\\"2024-05-25T08:16:40Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-04-28T20:10:00Z\\"\\n", - "\\"from\\",\\"to\\",\\"subject\\",\\"message\\",\\"timestamp\\"\\n\\"not_a_real_email@example.com\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-10-16T06:26:40Z\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"url://somewhere\\",\\"2024-10-16T06:26:40Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-12-17T08:43:20Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n", - "\\"name\\",\\"id\\",\\"type\\",\\"timestamp\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-11T12:36:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-10T19:56:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-10T11:36:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-07T21:06:40Z\\"\\n", - "\\"name\\",\\"uri\\",\\"timestamp\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-02-27T05:00:00Z\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-05-16T03:26:40Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"TODO: data\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"TODO: data\\",\\"2024-10-31T06:10:00Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-02-08T19:20:00Z\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-02-08T19:20:00Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-11-17T06:30:00Z\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-11-17T06:30:00Z\\"\\n" -] -`; - -exports[`facebook: Can load the 2021 export zipped 1`] = ` -[ - "\\"album\\",\\"uri\\",\\"creation_timestamp\\"\\n\\"xxx\\",\\"photos_and_videos/CoverPhotos_yyyyyy/200x200png.png\\",\\"2024-03-07T15:23:20Z\\"\\n\\"xxx\\",\\"photos_and_videos/CoverPhotos_yyyyyy/200x200png.png\\",\\"2024-07-01T07:46:40Z\\"\\n", - "[\\n \\"from\\",\\n \\"to\\",\\n \\"timestamp\\",\\n \\"body\\"\\n]\\n\\"Me\\",\\"xxx\\",\\"2024-01-13T07:13:20Z\\",\\"xxx\\"\\n\\"Me\\",\\"xxx\\",\\"2024-01-13T07:13:20Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"action\\",\\"ip\\",\\"user_agent\\",\\"datr_cookie\\",\\"city\\",\\"region\\",\\"country\\",\\"site_name\\",\\"timestamp\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"status\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-02-13T14:36:40Z\\"\\n", - "\\"service_name\\",\\"native_app_id\\",\\"username\\",\\"email\\",\\"phone_number\\",\\"name\\"\\n\\"xxx\\",69,\\"xxx\\",\\"not_a_real_email@example.com\\",\\"xxx\\",\\"xxx\\"\\n\\"xxx\\",1707005000,\\"xxx\\",\\"not_a_real_email@example.com\\",,\\"xxx\\"\\n", - "\\"event\\",\\"created_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"datr_cookie\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\",,,\\n\\"xxx\\",\\"2024-02-13T14:36:40Z\\",,,\\n", - "\\"name\\",\\"added_timestamp\\"\\n\\"xxx\\",\\"2024-12-29T08:13:20Z\\"\\n\\"xxx\\",\\"2024-09-02T12:26:40Z\\"\\n", - "\\"name\\",\\"created_timestamp\\",\\"updated_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"location\\",\\"app\\",\\"session_type\\",\\"datr_cookie\\"\\n\\"xxx\\",\\"2024-08-22T01:26:40Z\\",\\"2024-05-11T15:06:40Z\\",\\"1.1.1.1\\",\\"some/path\\",\\"\\",\\"\\",\\"\\",\\"xxx\\"\\n", - "\\"timestamp\\",\\"data\\",\\"title\\"\\n\\"2024-02-08T19:20:00Z\\",\\"TODO\\",\\"xxx\\"\\n\\"2024-01-17T14:00:00Z\\",\\"TODO\\",\\"xxx\\"\\n", - "\\"timestamp\\",\\"email\\",\\"contact_type\\"\\n\\"2024-10-18T07:03:20Z\\",\\"not_a_real_email@example.com\\",69\\n\\"2024-01-21T22:10:00Z\\",\\"not_a_real_email@example.com\\",69\\n", - "\\"name\\"\\n\\"xxx\\"\\n\\"xxx\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-13T13:13:20Z\\"\\n\\"xxx\\",\\"2024-10-31T00:36:40Z\\"\\n", - "\\"game\\",\\"added_timestamp\\"\\n\\"xxx\\",\\"2024-11-03T16:06:40Z\\"\\n", - "\\"title\\",\\"price\\",\\"seller\\",\\"created_timestamp\\",\\"latitude\\",\\"longitude\\",\\"description\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-12-18T05:33:20Z\\",69,69,\\"xxx\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-12-18T05:33:20Z\\",69,69,\\"xxx\\"\\n", - "\\"action\\",\\"timestamp\\",\\"site\\",\\"ip_address\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n\\"xxx\\",\\"2024-04-23T17:56:40Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n", - "\\"timestamp\\",\\"unread\\",\\"href\\",\\"text\\"\\n\\"2024-04-30T08:16:40Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n\\"2024-04-30T08:16:40Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"2024-05-01T07:53:20Z\\"\\n", - "\\"from\\",\\"to\\",\\"amount\\",\\"currency\\",\\"type\\",\\"status\\",\\"payment_method\\",\\"created_timestamp\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-05-05T21:36:40Z\\"\\n", - "\\"name\\",\\"uri\\",\\"timestamp\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-01-15T12:00:00Z\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-01-12T06:13:20Z\\"\\n", - "\\"from\\",\\"to\\",\\"rank\\",\\"timestamp\\"\\n\\"xxx\\",\\"xxx\\",69,\\"2024-07-22T19:03:20Z\\"\\n", - "\\"title\\",\\"timestamp\\",\\"reaction\\"\\n,\\"2024-01-14T06:50:00Z\\",\\"xxx\\"\\n,\\"2024-01-14T06:50:00Z\\",\\"xxx\\"\\n", - "\\"title\\",\\"timestamp\\"\\n,\\"2024-10-06T08:56:40Z\\"\\n,\\"2024-10-06T08:56:40Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-08T16:33:20Z\\"\\n\\"xxx\\",\\"2024-09-24T19:10:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-09-27T15:13:20Z\\"\\n\\"xxx\\",\\"2024-08-24T00:40:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-06-23T05:20:00Z\\"\\n\\"xxx\\",\\"2024-05-25T08:16:40Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-04-28T20:10:00Z\\"\\n", - "\\"from\\",\\"to\\",\\"subject\\",\\"message\\",\\"timestamp\\"\\n\\"not_a_real_email@example.com\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-10-16T06:26:40Z\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"url://somewhere\\",\\"2024-10-16T06:26:40Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-12-17T08:43:20Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n\\"xxx\\",\\"2024-01-14T06:50:00Z\\"\\n", - "\\"name\\",\\"id\\",\\"type\\",\\"timestamp\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-11T12:36:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-10T19:56:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-10T11:36:40Z\\"\\n\\"xxx\\",69,\\"xxx\\",\\"2024-02-07T21:06:40Z\\"\\n", - "\\"name\\",\\"uri\\",\\"timestamp\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-02-27T05:00:00Z\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-05-16T03:26:40Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"TODO: data\\",\\"2024-05-01T07:53:20Z\\"\\n\\"xxx\\",\\"TODO: data\\",\\"2024-10-31T06:10:00Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-02-08T19:20:00Z\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-02-08T19:20:00Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-11-17T06:30:00Z\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-11-17T06:30:00Z\\"\\n" -] -`; - -exports[`facebook: Can load the 2025 export 1`] = ` -[ - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"some/path\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\"xxx\\"\\n", - "\\"from\\",\\"to\\",\\"timestamp\\",\\"content\\"\\n\\"xxx\\",\\"\\",\\"1970-01-01T00:00:00Z\\",\\n", - "\\"action\\",\\"ip\\",\\"user_agent\\",\\"datr_cookie\\",\\"city\\",\\"region\\",\\"country\\",\\"site_name\\",\\"timestamp\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-11-22T10:06:40Z\\"\\n\\"xxx\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-11-21T23:00:00Z\\"\\n", - "\\"timestamp\\",\\"data\\",\\"title\\"\\n\\"2024-02-13T02:06:40Z\\",\\"TODO\\",\\"xxx\\"\\n\\"2024-07-12T02:06:40Z\\",\\"TODO\\",\\"xxx\\"\\n", - "\\"name\\",\\"added_timestamp\\"\\n\\"xxx\\",\\"2024-01-12T00:40:00Z\\"\\n\\"xxx\\",\\"2024-06-21T17:13:20Z\\"\\n", - "\\"timestamp\\",\\"email\\",\\"contact_type\\"\\n\\"2024-02-07T19:43:20Z\\",\\"not_a_real_email@example.com\\",69\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-10-06T06:10:00Z\\"\\n\\"xxx\\",\\"TODO\\",\\"2024-01-22T16:13:20Z\\"\\n", - "\\"title\\",\\"price\\",\\"seller\\",\\"created_timestamp\\",\\"latitude\\",\\"longitude\\",\\"description\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-10-02T23:00:00Z\\",69,69,\\"xxx\\"\\n\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"2024-09-27T01:20:00Z\\",69,69,\\"xxx\\"\\n", - "\\"action\\",\\"timestamp\\",\\"site\\",\\"ip_address\\"\\n\\"xxx\\",\\"2024-08-10T14:26:40Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n\\"xxx\\",\\"2024-08-10T14:26:40Z\\",\\"xxx\\",\\"1.1.1.1\\"\\n", - "\\"timestamp\\",\\"unread\\",\\"href\\",\\"text\\"\\n\\"2024-11-20T12:16:40Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n\\"2024-11-15T00:20:00Z\\",true,\\"url://somewhere\\",\\"xxx\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-21T03:10:00Z\\"\\n", - "\\"name\\",\\"uri\\",\\"timestamp\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-09-11T20:03:20Z\\"\\n\\"xxx\\",\\"url://somewhere\\",\\"2024-01-20T12:50:00Z\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-09-10T10:43:20Z\\"\\n\\"xxx\\",\\"2024-09-02T12:26:40Z\\"\\n", - "\\"event\\",\\"created_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"datr_cookie\\"\\n\\"xxx\\",\\"2024-08-11T01:33:20Z\\",,,\\n\\"xxx\\",\\"2024-08-10T14:26:40Z\\",,,\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-09-01T14:13:20Z\\"\\n\\"xxx\\",\\"2024-08-12T08:06:40Z\\"\\n", - "\\"start\\",\\"end\\"\\n", - "\\"name\\",\\"created_timestamp\\",\\"updated_timestamp\\",\\"ip_address\\",\\"user_agent\\",\\"location\\",\\"app\\",\\"session_type\\",\\"datr_cookie\\"\\n,\\"2024-04-04T19:46:40Z\\",\\"2024-11-23T02:46:40Z\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\"\\n,\\"2024-04-05T06:53:20Z\\",\\"2024-11-22T10:06:40Z\\",\\"1.1.1.1\\",\\"some/path\\",\\"xxx\\",\\"xxx\\",\\"xxx\\",\\"xxx\\"\\n", - "\\"name\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-04-01T16:46:40Z\\"\\n\\"xxx\\",\\"2024-09-07T16:03:20Z\\"\\n", - "\\"title\\",\\"timestamp\\"\\n\\"xxx\\",\\"2024-02-12T17:46:40Z\\"\\n\\"xxx\\",\\"2024-02-12T17:46:40Z\\"\\n", - "\\"title\\",\\"data\\",\\"timestamp\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-12-08T09:26:40Z\\"\\n\\"xxx\\",\\"xxx\\",\\"2024-12-28T00:16:40Z\\"\\n" -] -`; diff --git a/test/fixtures/README.md b/test/fixtures/README.md index 75fd3a9..f0b836d 100644 --- a/test/fixtures/README.md +++ b/test/fixtures/README.md @@ -11,3 +11,6 @@ * `facebook-json-2021-05-01` - Facebook JSON export * `facebook-json-2025-11-29` - Facebook JSON export +* [`discord-chat-exporter-2026-02`](./discord-chat-exporter-2026-02.md) - Discord export with [DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter) sometime around Feb 2026 +* [`discord-json-2021-01`](./discord-json-2021-01.md) - Discord JSON export +* [`snapchat-2023-11`](./snapchat-2023-11.md) - Snapchat JSON + HTML export diff --git a/test/fixtures/discord-chat-exporter-2026-02.md b/test/fixtures/discord-chat-exporter-2026-02.md new file mode 100644 index 0000000..3f8982f --- /dev/null +++ b/test/fixtures/discord-chat-exporter-2026-02.md @@ -0,0 +1,25 @@ +# discord-chat-exporter-2026-02 + +An export from `DiscordChatExporter`, a comprehensive DiscordChatExporter + +## Export methodology + +This uses the version of `DiscordChatExporter` that existed at the top of the releases tab on GitHub around `2026 February`. **TODO: figure out version** + +This export used a command something like the following to try to get _everything_ `dotnet DiscordChatExporter.Cli.dll export -t xxx -o ~/DiscordChatExporter -f json --media --reuse-media --include-threads -c xxx` + +* It uses `export` command and `-c` but it's the same for `exportguild` and `-g` +* `-f json` so only the json export +* `--media` download all media +* `--reuse-media` not quite sure what this does because it puts it in a folder per channel... +* `--include-threads` to get any threads + +## Manual edits +* Lots of image replacing + placeholders +* Had to rename the folders + +## Notes +The export format has files and folders with similar, information-dense names. I tried to preserve that as that's the only way to correlate between the folder and the file name + +* No exif on any media files +* There's embeds, thumbnails in the example chat messages but I have no other specimen \ No newline at end of file diff --git a/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json new file mode 100644 index 0000000..653631d --- /dev/null +++ b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json @@ -0,0 +1,145 @@ +{ + "guild": { + "id": "111111111111111111", + "name": "xxxxxxxx", + "iconUrl": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png" + }, + "channel": { + "id": "111111111111111111", + "type": "xxxxxxxxxxxxx", + "categoryId": "111111111111111111", + "category": "xxxxxxxxxxxxx", + "name": "xxxxxxx", + "topic": null + }, + "dateRange": { + "after": null, + "before": null + }, + "exportedAt": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "messages": [ + { + "id": "111111111111111111", + "type": "xxxxxxxxxxxxxxx", + "timestamp": "2020-04-13T10:09:08.000000+00:00", + "timestampEdited": null, + "callEndedTimestamp": null, + "isPinned": false, + "content": "xxxxxxxxxxxxxxxxxx", + "author": { + "id": "111111111111111111", + "name": "xxxxxxxx", + "discriminator": "1111", + "nickname": "xxxxxxxx", + "color": null, + "isBot": false, + "roles": [], + "avatarUrl": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png" + }, + "attachments": [], + "embeds": [], + "stickers": [], + "reactions": [], + "mentions": [], + "inlineEmojis": [] + }, + { + "id": "111111111111111111", + "type": "xxxxxxx", + "timestamp": "2020-04-13T10:09:08.000000+00:00", + "timestampEdited": null, + "callEndedTimestamp": null, + "isPinned": false, + "content": "xxxxxxxxx", + "author": { + "id": "111111111111111111", + "name": "xxxxxxxx", + "discriminator": "1111", + "nickname": "xxxxxxxx", + "color": null, + "isBot": false, + "roles": [], + "avatarUrl": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png" + }, + "attachments": [], + "embeds": [], + "stickers": [], + "reactions": [], + "mentions": [], + "inlineEmojis": [] + }, + { + "id": "111111111111111111", + "type": "xxxxxxx", + "timestamp": "2020-04-13T10:09:08.000000+00:00", + "timestampEdited": null, + "callEndedTimestamp": null, + "isPinned": false, + "content": "https://example.com/example.png", + "author": { + "id": "111111111111111111", + "name": "xxxxxxxx", + "discriminator": "1111", + "nickname": "xxxxxxxx", + "color": null, + "isBot": false, + "roles": [], + "avatarUrl": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png" + }, + "attachments": [], + "embeds": [ + { + "title": "", + "url": "https://example.com/example.png", + "timestamp": null, + "description": "", + "thumbnail": { + "url": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/example.png", + "width": 111, + "height": 111 + }, + "images": [], + "fields": [], + "inlineEmojis": [] + } + ], + "stickers": [], + "reactions": [], + "mentions": [], + "inlineEmojis": [] + }, + { + "id": "111111111111111111", + "type": "xxxxxxx", + "timestamp": "2020-04-13T10:09:08.000000+00:00", + "timestampEdited": null, + "callEndedTimestamp": null, + "isPinned": false, + "content": "xxx", + "author": { + "id": "111111111111111111", + "name": "xxxxxxxx", + "discriminator": "1111", + "nickname": "xxxxxxxx", + "color": null, + "isBot": false, + "roles": [], + "avatarUrl": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png" + }, + "attachments": [ + { + "id": "111111111111111111", + "url": "GuildName - Text Channels - ChannelName [0000000000000000].json_Files/unknown-SUFFIX.png", + "fileName": "unknown.png", + "fileSizeBytes": 111111 + } + ], + "embeds": [], + "stickers": [], + "reactions": [], + "mentions": [], + "inlineEmojis": [] + } + ], + "messageCount": 111 +} diff --git a/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png new file mode 100644 index 0000000..1655996 Binary files /dev/null and b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/avatar.png differ diff --git a/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/example.png b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/example.png new file mode 100644 index 0000000..abc6b4a Binary files /dev/null and b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/example.png differ diff --git a/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/unknown-SUFFIX.png b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/unknown-SUFFIX.png new file mode 100644 index 0000000..abc6b4a Binary files /dev/null and b/test/fixtures/discord-chat-exporter-2026-02/GuildName - Text Channels - ChannelName [0000000000000000].json_Files/unknown-SUFFIX.png differ diff --git a/test/fixtures/discord-json-2021-01.md b/test/fixtures/discord-json-2021-01.md new file mode 100644 index 0000000..c7fca9e --- /dev/null +++ b/test/fixtures/discord-json-2021-01.md @@ -0,0 +1,41 @@ +# discord-json-2021-01 + +## Manual edits +* images -> placeholders + * `accounts/avatar.png` +* manually scrub folder names + * `account/applications/0000000000000` + +## Notes about files +* `activity/` + * All the .json are NDJSON so some json tools don't like them + * _Massive_ files. They hang scrub.ts for a long long time (had to run these piecemeal) + * These files also have an _incredible_ amount of shapes and variance. + * Instead of outputing all the shapes I made a sort of "super-object" to capture the shape with `jq -n '[inputs] | add' events-2021-00000-of-00001.json.tmp > unique_shape.json` and then scrubbing `unique_shape.json` +* `messages/` + * I hand did these to keep all the ids the same + * There are multiple types of chats. DMs, guild channels, etc + * I hand did the csvs as I have no scrubber for that + * These are only **THE EXPORTING USERS MESSAGES**, no other user, just fyi + * Ids in `messages.csv` are just the id of the message, not of any user + * There is the potential to derive missing info from a channel via `@` tags sent or possibly via attachments. Maybe... + * `11111111111111111` + * This one has a shorter id (it's an older one) + * Has `type: 0` but there's no guild information in `channel.json` + * The user name was `null` in `index.json` + * It's a really odd one + * `222222222222222222` + * This was a dm channel (said `direct message with xxx#7777` in index.json) + * Has `type: 1` and there are two recipients (just the ids) in `channel.json` + * Unfortunately that's all the info in the export + * `333333333333333333` + * This was a normal guild channel + * `type: 0` and there's guild information in `channel.json` + * I kept a good set of messages around from this one to show how attachements and other stuff works + * The last message seemed to be a link not as an attachment. Links just seem to be normal text +* `programs/` + * was empty... +* `servers/`` + * Info about _some_ of the guilds we have ids for + * guild.json didn't really contain anything except the name + * I kept around the only guild I noticed an audit-log.json with info in it \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/README.txt b/test/fixtures/discord-json-2021-01/README.txt new file mode 100644 index 0000000..e13a74b --- /dev/null +++ b/test/fixtures/discord-json-2021-01/README.txt @@ -0,0 +1,26 @@ + __ __ ___ _ _ ___ ___ ___ _____ ___ _ + \ \ / / / _ \ | | | | | _ \ o O O | \ / \ |_ _| / \ | | + \ V / | (_) | | |_| | | / o | |) | | - | | | | - | |_| + _|_|_ \___/ \___/ |_|_\ TS__[O] |___/ |_|_| _|_|_ |_|_| _(_)_ +_| """ |_|"""""|_|"""""|_|"""""| <======|_|"""""|_|"""""|_|"""""|_|"""""|_| """ | +"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-' + ___ ___ _ _ ___ ___ ___ _ _ _ + |_ _| / __| o O O | || | | __| | _ \ | __| | | | | | | + | | \__ \ o | __ | | _| | / | _| |_| |_| |_| + |___| |___/ TS__[O] |_||_| |___| |_|_\ |___| _(_)_ _(_)_ _(_)_ +_|"""""|_|"""""| <======|_|"""""|_|"""""|_|"""""|_|"""""|_| """ |_| """ |_| """ | +"`-0-0-'"`-0-0-'./o--000'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-' + +Welcome to your Discord Data Package! + +Inside, you'll find a few JSON (JavaScript Object Notation) and CSV (Comma Separated Values) files +of the data we use to provide Discord's service to you. We've chosen these formats for ease of +processing. Furthermore, the files have been organized into logical groups to make it easy to +understand and work with (at least, we hope so)! + +For more information, you can view our in-depth help article at the following URL: + +https://support.discord.com/hc/articles/360004957991 + +All the best, +Discord Team diff --git a/test/fixtures/discord-json-2021-01/account/applications/0000000000000000/application.json b/test/fixtures/discord-json-2021-01/account/applications/0000000000000000/application.json new file mode 100644 index 0000000..58dc565 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/account/applications/0000000000000000/application.json @@ -0,0 +1,16 @@ +{ + "id": "111111111111111111", + "name": "xxxxxxx", + "icon": null, + "description": "", + "summary": "", + "hook": false, + "verify_key": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "flags": 1, + "secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "redirect_uris": [], + "rpc_application_state": 1, + "store_application_state": 1, + "verification_state": 1, + "interactions_endpoint_url": null +} diff --git a/test/fixtures/discord-json-2021-01/account/avatar.png b/test/fixtures/discord-json-2021-01/account/avatar.png new file mode 100644 index 0000000..0ada828 Binary files /dev/null and b/test/fixtures/discord-json-2021-01/account/avatar.png differ diff --git a/test/fixtures/discord-json-2021-01/account/user.json b/test/fixtures/discord-json-2021-01/account/user.json new file mode 100644 index 0000000..d26819a --- /dev/null +++ b/test/fixtures/discord-json-2021-01/account/user.json @@ -0,0 +1,399 @@ +{ + "id": "111111111111111111", + "username": "xxxxxxxx", + "discriminator": 1111, + "email": "not_a_real_email@example.com", + "verified": false, + "avatar_hash": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "has_mobile": false, + "needs_email_verification": false, + "premium_until": "2020-04-13T10:09:08.000000+00:00", + "flags": 11111111111111, + "phone": "xxxxxxxxxxxx", + "temp_banned_until": null, + "ip": "1.1.1.1", + "settings": { + "locale": "xxxxx", + "show_current_game": false, + "restricted_guilds": [], + "default_guilds_restricted": false, + "inline_attachment_media": false, + "inline_embed_media": false, + "gif_auto_play": false, + "render_embeds": false, + "render_reactions": false, + "animate_emoji": false, + "enable_tts_command": false, + "message_display_compact": false, + "convert_emoticons": false, + "explicit_content_filter": 1, + "disable_games_tab": false, + "theme": "xxxx", + "developer_mode": false, + "guild_positions": [ + "111111111111111111", + "111111111111111111" + ], + "detect_platform_accounts": false, + "status": "xxxxxx", + "afk_timeout": 111, + "timezone_offset": 111, + "stream_notifications_enabled": false, + "allow_accessibility_detection": false, + "contact_sync_enabled": false, + "native_phone_integration_enabled": false, + "animate_stickers": 1, + "friend_source_flags": { + "all": false + }, + "guild_folders": [ + { + "guild_ids": [ + "111111111111111111" + ], + "id": null, + "name": null, + "color": null + }, + { + "guild_ids": [ + "111111111111111111" + ], + "id": null, + "name": null, + "color": null + } + ], + "custom_status": null + }, + "connections": [ + { + "type": "xxxxxxxxx", + "id": "xxxxxxxxxxx", + "name": "xxxxxxxxxxx", + "revoked": false, + "visibility": 1, + "friend_sync": false, + "show_activity": false, + "verified": false + }, + { + "type": "xxxxxxx", + "id": "xxxxxxxx", + "name": "xxxxxxxx", + "revoked": false, + "visibility": 1, + "friend_sync": false, + "show_activity": false, + "verified": false + } + ], + "external_friends_lists": [ + { + "user_id": "111111111111111111", + "platform_type": "xxxxx", + "name": "xxxxxxxx", + "id_hash": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "friend_id_hashes": [ + "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + ] + }, + { + "user_id": "111111111111111111", + "platform_type": "xxxxxxxxx", + "name": "xxxxxxxxxxx", + "id_hash": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "friend_id_hashes": [ + "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + ] + } + ], + "friend_suggestions": [], + "mfa_sessions": [], + "relationships": [ + { + "id": "11111111111111111", + "type": 1, + "nickname": null, + "user": { + "id": "11111111111111111", + "username": "xxxxxxxxxxxx", + "avatar": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "discriminator": "1111", + "public_flags": 1 + } + }, + { + "id": "11111111111111111", + "type": 1, + "nickname": null, + "user": { + "id": "11111111111111111", + "username": "xxxx", + "avatar": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "discriminator": "1111", + "public_flags": 111 + } + } + ], + "payments": [ + { + "id": "111111111111111111", + "created_at": "2020-04-13T10:09:08.000000+00:00", + "currency": "xxx", + "tax": 111, + "tax_inclusive": false, + "amount": 1111, + "amount_refunded": 1, + "status": 1, + "description": "xxxxxxxxxxxxxxxxxxxx", + "flags": 1, + "subscription": { + "id": "111111111111111111", + "type": 1, + "current_period_start": "2020-04-13T10:09:08.000000+00:00", + "current_period_end": "2020-04-13T10:09:08.000000+00:00", + "payment_gateway": null, + "payment_gateway_plan_id": "xxxxxxxxxxxxxxxxxxx", + "currency": "xxx", + "plan_id": "111111111111111111", + "items": [ + { + "id": "111111111111111111", + "plan_id": "111111111111111111", + "quantity": 1 + } + ] + }, + "payment_source": { + "id": "111111111111111111", + "type": 1, + "invalid": false, + "brand": "xxxx", + "last_4": "1111", + "expires_month": 11, + "expires_year": 1111, + "billing_address": { + "name": "xxxxxxxxxxxxx", + "line_1": "xxxxxxxxxxxxxxxxx", + "line_2": null, + "city": "xxxxxxxx", + "state": "xx", + "country": "xx", + "postal_code": "11111" + }, + "country": "xx" + }, + "sku_id": "111111111111111111", + "sku_price": 1111, + "sku_subscription_plan_id": "111111111111111111" + }, + { + "id": "111111111111111111", + "created_at": "2020-04-13T10:09:08.000000+00:00", + "currency": "xxx", + "tax": 111, + "tax_inclusive": false, + "amount": 1111, + "amount_refunded": 1, + "status": 1, + "description": "xxxxxxxxxxxxxxxxxxxx", + "flags": 1, + "subscription": { + "id": "111111111111111111", + "type": 1, + "current_period_start": "2020-04-13T10:09:08.000000+00:00", + "current_period_end": "2020-04-13T10:09:08.000000+00:00", + "payment_gateway": null, + "payment_gateway_plan_id": "xxxxxxxxxxxxxxxxxxx", + "currency": "xxx", + "plan_id": "111111111111111111", + "items": [ + { + "id": "111111111111111111", + "plan_id": "111111111111111111", + "quantity": 1 + } + ] + }, + "payment_source": { + "id": "111111111111111111", + "type": 1, + "invalid": false, + "brand": "xxxx", + "last_4": "1111", + "expires_month": 11, + "expires_year": 1111, + "billing_address": { + "name": "xxxxxxxxxxxxx", + "line_1": "xxxxxxxxxxxxxxxxxx", + "line_2": null, + "city": "xxxxxxxxxx", + "state": "xx", + "country": "xx", + "postal_code": "11111" + }, + "country": "xx" + }, + "sku_id": "111111111111111111", + "sku_price": 1111, + "sku_subscription_plan_id": "111111111111111111" + } + ], + "payment_sources": [ + { + "id": "111111111111111111", + "type": 1, + "invalid": false, + "brand": "xxxx", + "last_4": "1111", + "expires_month": 11, + "expires_year": 1111, + "billing_address": { + "name": "xxxxxxxxxxxxx", + "line_1": "xxxxxxxxxxxxxxxxx", + "line_2": null, + "city": "xxxxxxxx", + "state": "xx", + "country": "xx", + "postal_code": "11111" + }, + "country": "xx" + } + ], + "guild_settings": [ + { + "guild_id": null, + "suppress_everyone": false, + "suppress_roles": false, + "message_notifications": 1, + "mobile_push": false, + "muted": false, + "mute_config": null, + "channel_overrides": [ + { + "channel_id": "111111111111111111", + "message_notifications": 1, + "muted": false, + "mute_config": null + } + ], + "version": 11 + }, + { + "guild_id": "11111111111111111", + "suppress_everyone": false, + "suppress_roles": false, + "message_notifications": 1, + "mobile_push": false, + "muted": false, + "mute_config": null, + "channel_overrides": [ + { + "channel_id": "111111111111111111", + "message_notifications": 1, + "muted": false, + "mute_config": null + }, + { + "channel_id": "111111111111111111", + "message_notifications": 1, + "muted": false, + "mute_config": null + } + ], + "version": 1 + } + ], + "library_applications": [ + { + "application": { + "id": "111111111111111111", + "name": "xxxxxxxxxxxx", + "icon": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "description": "xxxxxxxxxxxxxxxxxxxxx", + "summary": "xxxxxxxxxxxxxxxxxxxxx", + "primary_sku_id": "111111111111111111", + "hook": false, + "slug": "xxxxxxxxxxxx", + "guild_id": "111111111111111111", + "verify_key": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1", + "publishers": [ + { + "id": "111111111111111111", + "name": "xxxxxxxxxxx" + } + ], + "developers": [ + { + "id": "111111111111111111", + "name": "xxxxxxxxxxx" + }, + { + "id": "111111111111111111", + "name": "xxxxxxxxxxxxxxxxxxxxxxxx" + } + ] + }, + "branch_id": "111111111111111111", + "sku_id": "111111111111111111", + "sku": { + "id": "111111111111111111", + "type": 1, + "premium": false, + "preorder_release_at": null, + "preorder_approximate_release_date": null + }, + "flags": 1, + "created_at": "2020-04-13T10:09:08.000000+00:00", + "entitlements": [ + { + "id": "111111111111111111", + "sku_id": "111111111111111111", + "application_id": "111111111111111111", + "user_id": "111111111111111111", + "type": 1, + "deleted": false, + "gift_code_flags": 1, + "branches": [ + "111111111111111111" + ] + } + ] + } + ], + "entitlements": [ + { + "id": "111111111111111111", + "sku_id": "111111111111111111", + "application_id": "111111111111111111", + "user_id": "111111111111111111", + "type": 1, + "deleted": false, + "gift_code_flags": 1, + "branches": [ + "111111111111111111" + ], + "sku_name": "xxxxxxxxxxxx" + } + ], + "user_activity_application_statistics": [ + { + "application_id": "111111111111111111", + "last_played_at": "2020-04-13T10:09:08.000000+00:00", + "total_duration": 1111, + "total_discord_sku_duration": 1 + }, + { + "application_id": "111111111111111111", + "last_played_at": "2020-04-13T10:09:08.000000+00:00", + "total_duration": 111111, + "total_discord_sku_duration": 1 + } + ], + "notes": { + "111111111111111111": "xxxx" + } +} diff --git a/test/fixtures/discord-json-2021-01/activity/analytics/events-2021-00000-of-00001.json b/test/fixtures/discord-json-2021-01/activity/analytics/events-2021-00000-of-00001.json new file mode 100644 index 0000000..1660835 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/activity/analytics/events-2021-00000-of-00001.json @@ -0,0 +1,2 @@ +{"event_type":"xxxxxxxxxxxxxxxx","event_id":"some/path","user_id":"111111111111111111","domain":"xxxxxxxxx","ip":"1.1.1.1","day":"1111","variant":"a","browser":"xxxxxxxxxxxxxx","os":"xxxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","city":"xxxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","chosen_locale":"xxxxx","detected_locale":"xxxxx","os_version":"xxxxxxxxxx","release_channel":"xxxxxx","client_version":"xxxxxxx","event_source":"xxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","client_build_number":"11111","is_refresh":false,"success":false,"method":"xxxxxxxxxxxxxxxxxxx","user_is_authenticated":false,"isp":"xxxxxxxxxxxxx","message_id":"111111111111111111","channel":"111111111111111111","channel_type":"1","is_friend":false,"private":false,"server":"111111111111111111","num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":false,"emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","device":"xxxxxxxxxxxxxxxxx","browser_user_agent":"some/path","os_sdk_version":"11","accessibility_support_enabled":false,"accessibility_features":"111","os_arch":"xxx","system_locale":"xxxxx","payment_source_id":"111111111111111111","payment_source_type":"1","payment_gateway":"1","is_default":false,"search_engine":"xxxxxx","location":"xxxxxxxxxxxxxxxxxxxxxxxx","channel_id":"111111111111111111","guild_id":"111111111111111111","nonce":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","voice_state_count":"1","video_stream_count":"1","video_enabled":false,"rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","connection_type":"xxxxxxx","game_name":"xxxxxxxxx","game_platform":"xxxxxxx","game_id":"111111111111111111","client_performance_cpu":8.08,"client_performance_memory":"111111","custom_status_count":"1","effective_connection_speed":"xx","browser_version":"xxxx","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxxx","guild_size_total":"1111","guild_member_num_roles":"1","guild_member_perms":"11111111","guild_num_channels":"11","guild_num_text_channels":"11","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","channel_size_total":"1","channel_member_perms":"111111111","channel_hidden":false,"num_users_visible":"1","num_users_visible_with_mobile_indicator":"1","num_users_visible_with_game_activity":"1","num_users_visible_with_activity":"1","search_engine_current":"xxxxxxxxxx","type":"xxxxxxxxxx","action":"xxxxxxx","previous_action":"xxxx","channel_size_online":"11","command":"xxx","source":"xxxxxxxxxxxx","action_type":"1","mime_type":"some/path","total_attachments":"1","platform_type":"xxxxxxxxxxxxxxx","display_type":"xxxxx","payment_id":"111111111111111111","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","payment_type":"xxxxxxxxxxxx","price":"1111","currency":"xxx","amount":"1111","amount_refunded":"1","tax":"111","tax_inclusive":false,"sku_id":"111111111111111111","sku_type":"1","sku_subscription_plan_id":"111111111111111111","subscription_id":"111111111111111111","subscription_type":"1","subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_plan_id":"111111111111111111","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","failure_message":"some/path","is_gift":false,"average_ping":"11","duration":"1111","maximum_ping":"11","minimum_ping":"11","previous_tier":"xx","quality":8.08,"session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","speaker":"111111111111111111","tier":"1","guild_affinity_score":8.08,"guild_affinity_index":"11","is_pending":false,"preview_enabled":false,"location_page":"xxxxxxxxxxxxx","location_section":"xxxxxxxxxxxxx","location_object":"xxxxxxxxxx","location_object_type":"xxx","subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","regular_price":"1111","to_step":"xxxxxxx","from_step":"xxxxxx","load_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","step_duration_ms":"1111","flow_duration_ms":"1111","eligible_for_trial":false,"session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"marketing_variant":"a","platform":"xxxxxxx","ptb":false,"referring_location":"xxxx","released":false,"num_failed":"1","num_delta_installed":"1","num_full_installed":"1","foreground_bytes_total":"11111111","background_bytes_total":"1","foreground_download_ms_total":"111","background_download_ms_total":"1","foreground_install_ms_total":"1111","background_install_ms_total":"1","foreground_install_ms_discord_voice":"1111","min_version_discord_voice":"1","max_version_discord_voice":"1","foreground_install_ms_discord_rpc":"111","min_version_discord_rpc":"1","max_version_discord_rpc":"1","foreground_install_ms_discord_desktop_core":"111","min_version_discord_desktop_core":"1","max_version_discord_desktop_core":"1","background_bytes_discord_rpc":"11111","background_download_ms_discord_rpc":"111","background_bytes_discord_cloudsync":"1111111","background_bytes_discord_dispatch":"1111111","background_bytes_discord_game_utils":"111111","background_bytes_discord_media":"111111","background_bytes_discord_modules":"1111111","background_bytes_discord_overlay2":"111111","background_download_ms_discord_cloudsync":"111","background_download_ms_discord_dispatch":"111","background_download_ms_discord_game_utils":"111","background_download_ms_discord_media":"111","background_download_ms_discord_modules":"111","background_download_ms_discord_overlay2":"111","background_install_ms_discord_cloudsync":"111","background_install_ms_discord_dispatch":"111","background_install_ms_discord_game_utils":"111","background_install_ms_discord_media":"11","background_install_ms_discord_modules":"111","background_install_ms_discord_overlay2":"111","background_install_ms_discord_rpc":"111","max_version_discord_cloudsync":"1","max_version_discord_dispatch":"1","max_version_discord_game_utils":"1","max_version_discord_media":"1","max_version_discord_modules":"1","max_version_discord_overlay2":"1","background_bytes_discord_krisp":"11111111","background_download_ms_discord_krisp":"1111","background_install_ms_discord_krisp":"1111","max_version_discord_krisp":"1","background_bytes_discord_voice":"1111111","background_download_ms_discord_voice":"111","background_bytes_discord_desktop_core":"1111111","background_download_ms_discord_desktop_core":"111","background_bytes_discord_hook":"1111111","background_download_ms_discord_hook":"111","background_install_ms_discord_hook":"111","max_version_discord_hook":"1","foreground_bytes_discord_game_utils":"111111","foreground_download_ms_discord_game_utils":"111","foreground_install_ms_discord_dispatch":"111","foreground_install_ms_discord_game_utils":"11","min_version_discord_dispatch":"1","min_version_discord_game_utils":"1","foreground_bytes_discord_voice":"1111111","foreground_download_ms_discord_voice":"111","foreground_install_ms_discord_overlay2":"111","min_version_discord_overlay2":"1","foreground_bytes_discord_desktop_core":"1111111","foreground_bytes_discord_erlpack":"111111","foreground_bytes_discord_spellcheck":"1111111","foreground_bytes_discord_utils":"1111111","foreground_download_ms_discord_desktop_core":"111","foreground_download_ms_discord_erlpack":"11","foreground_download_ms_discord_spellcheck":"111","foreground_download_ms_discord_utils":"111","foreground_install_ms_discord_erlpack":"11","foreground_install_ms_discord_spellcheck":"1111","foreground_install_ms_discord_utils":"1111","min_version_discord_erlpack":"1","min_version_discord_spellcheck":"1","min_version_discord_utils":"1","max_version_discord_erlpack":"1","max_version_discord_spellcheck":"1","max_version_discord_utils":"1","foreground_bytes_discord_krisp":"11111111","foreground_bytes_discord_rpc":"11111","foreground_download_ms_discord_krisp":"1111","foreground_download_ms_discord_rpc":"111","foreground_install_ms_discord_krisp":"1111","min_version_discord_krisp":"1","foreground_bytes_discord_dispatch":"1111111","foreground_download_ms_discord_dispatch":"1111","setup_type":"xxxxxxxxxxxxxxxxxxx","temporary":false,"max_uses":"1","max_age":"11111","regenerate":false,"unique":false,"code":"xxxxxxxx","notice_type":"xxxxxxxxxxxxxxxx","survey_id":"xxxxxxxxxxxxxxxxxxxxxxxxxx","dismissed":false,"mode":"xxxxxxxxxxxxxx","mute":false,"anyone_priority":false,"game_exe_name":"xxxxxxxxxxxxx","media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","source_page":"xxxxxxxxxxxx","source_section":"xxxxxxxxxx","source_object":"xxxxxxxx","change_log_id":"xxxxxxxxxxxx","duration_ms":"11111","num_applications_total":"1","num_applications_battlenet":"1","num_applications_discord":"1","num_applications_steam":"1","num_applications_twitch":"1","num_applications_uplay":"1","num_applications_origin":"1","num_applications_gog":"1","num_applications_epic":"1","cpu":"xxxxxxx","gpu":"xxxxxxx","name":"xxxxxxxxxxxxxxxxxxxxx","bucket":"1","revision":"1","game_ids":["111111111111111111","111111111111111111"],"num_cards_game_news":"1","num_users_subscribed":"11","window_width":"1111","window_height":"1111","feed_layout":"xxxxxxxxx","subscribed_games":[],"num_items_now_playing":"1","num_items_recently_played":"11","news_ids_viewed":[],"guild_ids_viewed":[],"num_cards":"11","num_cards_visible":"1","num_cards_game_playable":"1","num_game_parties":"1","num_game_parties_voice":"1","num_game_parties_solo":"1","num_game_parties_recently_played":"11","num_game_parties_rich_presence":"1","num_game_parties_collapsed":"1","num_launcher_applications":"1","is_premium":false,"application_id":"111111111111111111","branch_id":"111111111111111111","manifest_ids":[],"target_build_id":"111111111111111111","target_manifest_ids":[],"patch_type":"xxxxxxx","num_guilds":"111","num_guilds_recommended":"1","num_guilds_popular":"111","recommended_guild_ids":["11111111111111111","111111111111111111"],"notif_type":"xxxxxxxxxxxxxx","notif_in_app":false,"rel_type":"1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","channel_is_nsfw":false,"context":"xxxxxx","parent_media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","identity_type":"xxxxx","stacktrace":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","notifications_in_app_enabled":false,"emoji_id":"111111111111111111","emoji_name":"x","full":false,"instant_invite":false,"has_images":false,"party_platform":"xxxxxxx","track_id":"xxxxxxxxxxxxxxxxxxxxxx","has_match_secret":false,"has_spectate_secret":false,"has_join_secret":false,"party_max":"1"} +{"event_type":"xxxxxxxxxxxxxxxx","event_id":"some/path","user_id":"111111111111111111","domain":"xxxxxxxxx","ip":"1.1.1.1","day":"1111","variant":"a","browser":"xxxxxxxxxxxxxx","os":"xxxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","city":"xxxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","chosen_locale":"xxxxx","detected_locale":"xxxxx","os_version":"xxxxxxxxxx","release_channel":"xxxxxx","client_version":"xxxxxxx","event_source":"xxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","client_build_number":"11111","is_refresh":false,"success":false,"method":"xxxxxxxxxxxxxxxxxxx","user_is_authenticated":false,"isp":"xxxxxxxxxxxxx","message_id":"111111111111111111","channel":"111111111111111111","channel_type":"1","is_friend":false,"private":false,"server":"111111111111111111","num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":false,"emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","device":"xxxxxxxxxxxxxxxxx","browser_user_agent":"some/path","os_sdk_version":"11","accessibility_support_enabled":false,"accessibility_features":"111","os_arch":"xxx","system_locale":"xxxxx","payment_source_id":"111111111111111111","payment_source_type":"1","payment_gateway":"1","is_default":false,"search_engine":"xxxxxx","location":"xxxxxxxxxxxxxxxxxxxxxxxx","channel_id":"111111111111111111","guild_id":"111111111111111111","nonce":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","voice_state_count":"1","video_stream_count":"1","video_enabled":false,"rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","connection_type":"xxxxxxx","game_name":"xxxxxxxxx","game_platform":"xxxxxxx","game_id":"111111111111111111","client_performance_cpu":8.08,"client_performance_memory":"111111","custom_status_count":"1","effective_connection_speed":"xx","browser_version":"xxxx","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxxx","guild_size_total":"1111","guild_member_num_roles":"1","guild_member_perms":"11111111","guild_num_channels":"11","guild_num_text_channels":"11","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","channel_size_total":"1","channel_member_perms":"111111111","channel_hidden":false,"num_users_visible":"1","num_users_visible_with_mobile_indicator":"1","num_users_visible_with_game_activity":"1","num_users_visible_with_activity":"1","search_engine_current":"xxxxxxxxxx","type":"xxxxxxxxxx","action":"xxxxxxx","previous_action":"xxxx","channel_size_online":"11","command":"xxx","source":"xxxxxxxxxxxx","action_type":"1","mime_type":"some/path","total_attachments":"1","platform_type":"xxxxxxxxxxxxxxx","display_type":"xxxxx","payment_id":"111111111111111111","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","payment_type":"xxxxxxxxxxxx","price":"1111","currency":"xxx","amount":"1111","amount_refunded":"1","tax":"111","tax_inclusive":false,"sku_id":"111111111111111111","sku_type":"1","sku_subscription_plan_id":"111111111111111111","subscription_id":"111111111111111111","subscription_type":"1","subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_plan_id":"111111111111111111","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","failure_message":"some/path","is_gift":false,"average_ping":"11","duration":"1111","maximum_ping":"11","minimum_ping":"11","previous_tier":"xx","quality":8.08,"session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","speaker":"111111111111111111","tier":"1","guild_affinity_score":8.08,"guild_affinity_index":"11","is_pending":false,"preview_enabled":false,"location_page":"xxxxxxxxxxxxx","location_section":"xxxxxxxxxxxxx","location_object":"xxxxxxxxxx","location_object_type":"xxx","subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","regular_price":"1111","to_step":"xxxxxxx","from_step":"xxxxxx","load_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","step_duration_ms":"1111","flow_duration_ms":"1111","eligible_for_trial":false,"session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"marketing_variant":"a","platform":"xxxxxxx","ptb":false,"referring_location":"xxxx","released":false,"num_failed":"1","num_delta_installed":"1","num_full_installed":"1","foreground_bytes_total":"11111111","background_bytes_total":"1","foreground_download_ms_total":"111","background_download_ms_total":"1","foreground_install_ms_total":"1111","background_install_ms_total":"1","foreground_install_ms_discord_voice":"1111","min_version_discord_voice":"1","max_version_discord_voice":"1","foreground_install_ms_discord_rpc":"111","min_version_discord_rpc":"1","max_version_discord_rpc":"1","foreground_install_ms_discord_desktop_core":"111","min_version_discord_desktop_core":"1","max_version_discord_desktop_core":"1","background_bytes_discord_rpc":"11111","background_download_ms_discord_rpc":"111","background_bytes_discord_cloudsync":"1111111","background_bytes_discord_dispatch":"1111111","background_bytes_discord_game_utils":"111111","background_bytes_discord_media":"111111","background_bytes_discord_modules":"1111111","background_bytes_discord_overlay2":"111111","background_download_ms_discord_cloudsync":"111","background_download_ms_discord_dispatch":"111","background_download_ms_discord_game_utils":"111","background_download_ms_discord_media":"111","background_download_ms_discord_modules":"111","background_download_ms_discord_overlay2":"111","background_install_ms_discord_cloudsync":"111","background_install_ms_discord_dispatch":"111","background_install_ms_discord_game_utils":"111","background_install_ms_discord_media":"11","background_install_ms_discord_modules":"111","background_install_ms_discord_overlay2":"111","background_install_ms_discord_rpc":"111","max_version_discord_cloudsync":"1","max_version_discord_dispatch":"1","max_version_discord_game_utils":"1","max_version_discord_media":"1","max_version_discord_modules":"1","max_version_discord_overlay2":"1","background_bytes_discord_krisp":"11111111","background_download_ms_discord_krisp":"1111","background_install_ms_discord_krisp":"1111","max_version_discord_krisp":"1","background_bytes_discord_voice":"1111111","background_download_ms_discord_voice":"111","background_bytes_discord_desktop_core":"1111111","background_download_ms_discord_desktop_core":"111","background_bytes_discord_hook":"1111111","background_download_ms_discord_hook":"111","background_install_ms_discord_hook":"111","max_version_discord_hook":"1","foreground_bytes_discord_game_utils":"111111","foreground_download_ms_discord_game_utils":"111","foreground_install_ms_discord_dispatch":"111","foreground_install_ms_discord_game_utils":"11","min_version_discord_dispatch":"1","min_version_discord_game_utils":"1","foreground_bytes_discord_voice":"1111111","foreground_download_ms_discord_voice":"111","foreground_install_ms_discord_overlay2":"111","min_version_discord_overlay2":"1","foreground_bytes_discord_desktop_core":"1111111","foreground_bytes_discord_erlpack":"111111","foreground_bytes_discord_spellcheck":"1111111","foreground_bytes_discord_utils":"1111111","foreground_download_ms_discord_desktop_core":"111","foreground_download_ms_discord_erlpack":"11","foreground_download_ms_discord_spellcheck":"111","foreground_download_ms_discord_utils":"111","foreground_install_ms_discord_erlpack":"11","foreground_install_ms_discord_spellcheck":"1111","foreground_install_ms_discord_utils":"1111","min_version_discord_erlpack":"1","min_version_discord_spellcheck":"1","min_version_discord_utils":"1","max_version_discord_erlpack":"1","max_version_discord_spellcheck":"1","max_version_discord_utils":"1","foreground_bytes_discord_krisp":"11111111","foreground_bytes_discord_rpc":"11111","foreground_download_ms_discord_krisp":"1111","foreground_download_ms_discord_rpc":"111","foreground_install_ms_discord_krisp":"1111","min_version_discord_krisp":"1","foreground_bytes_discord_dispatch":"1111111","foreground_download_ms_discord_dispatch":"1111","setup_type":"xxxxxxxxxxxxxxxxxxx","temporary":false,"max_uses":"1","max_age":"11111","regenerate":false,"unique":false,"code":"xxxxxxxx","notice_type":"xxxxxxxxxxxxxxxx","survey_id":"xxxxxxxxxxxxxxxxxxxxxxxxxx","dismissed":false,"mode":"xxxxxxxxxxxxxx","mute":false,"anyone_priority":false,"game_exe_name":"xxxxxxxxxxxxx","media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","source_page":"xxxxxxxxxxxx","source_section":"xxxxxxxxxx","source_object":"xxxxxxxx","change_log_id":"xxxxxxxxxxxx","duration_ms":"11111","num_applications_total":"1","num_applications_battlenet":"1","num_applications_discord":"1","num_applications_steam":"1","num_applications_twitch":"1","num_applications_uplay":"1","num_applications_origin":"1","num_applications_gog":"1","num_applications_epic":"1","cpu":"xxxxxxx","gpu":"xxxxxxx","name":"xxxxxxxxxxxxxxxxxxxxx","bucket":"1","revision":"1","game_ids":["111111111111111111","111111111111111111"],"num_cards_game_news":"1","num_users_subscribed":"11","window_width":"1111","window_height":"1111","feed_layout":"xxxxxxxxx","subscribed_games":[],"num_items_now_playing":"1","num_items_recently_played":"11","news_ids_viewed":[],"guild_ids_viewed":[],"num_cards":"11","num_cards_visible":"1","num_cards_game_playable":"1","num_game_parties":"1","num_game_parties_voice":"1","num_game_parties_solo":"1","num_game_parties_recently_played":"11","num_game_parties_rich_presence":"1","num_game_parties_collapsed":"1","num_launcher_applications":"1","is_premium":false,"application_id":"111111111111111111","branch_id":"111111111111111111","manifest_ids":[],"target_build_id":"111111111111111111","target_manifest_ids":[],"patch_type":"xxxxxxx","num_guilds":"111","num_guilds_recommended":"1","num_guilds_popular":"111","recommended_guild_ids":["11111111111111111","111111111111111111"],"notif_type":"xxxxxxxxxxxxxx","notif_in_app":false,"rel_type":"1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","channel_is_nsfw":false,"context":"xxxxxx","parent_media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","identity_type":"xxxxx","stacktrace":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","notifications_in_app_enabled":false,"emoji_id":"111111111111111111","emoji_name":"x","full":false,"instant_invite":false,"has_images":false,"party_platform":"xxxxxxx","track_id":"xxxxxxxxxxxxxxxxxxxxxx","has_match_secret":false,"has_spectate_secret":false,"has_join_secret":false,"party_max":"1"} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/activity/modeling/events-2021-00000-of-00001.json b/test/fixtures/discord-json-2021-01/activity/modeling/events-2021-00000-of-00001.json new file mode 100644 index 0000000..48f3f5a --- /dev/null +++ b/test/fixtures/discord-json-2021-01/activity/modeling/events-2021-00000-of-00001.json @@ -0,0 +1,2 @@ +{"event_type":"xxxxxxxxxxxx","event_id":"some/path","event_source":"xxx","user_id":"111111111111111111","domain":"xxxxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","user_is_authenticated":false,"accessibility_support_enabled":false,"browser_user_agent":"some/path","browser":"xxxxxxxxxxxxxx","browser_version":"xxxxxxxxxxxxx","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","os":"xxxxxxx","os_version":"xxxxxxxxxx","client_build_number":"11111","release_channel":"xxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","search_engine":"xxxxxx","location":"xxxxxxxxxxxxxxxxxxxxxxxx","city":"xxxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","isp":"xxxxxxxxxxxx","location_page":"xxxxxxxxxxxxx","location_section":"xxxxxxx","location_object":"xxxxxxxxxx","location_object_type":"xxx","subscription_type":"1","subscription_plan_id":"111111111111111111","payment_type":"xxxxxxxxxxxx","price":"1111","regular_price":"1111","currency":"xxx","is_gift":false,"to_step":"xxxxxxx","from_step":"xxxxxx","load_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","step_duration_ms":"1111","flow_duration_ms":"1111","accessibility_features":"111","eligible_for_trial":false,"client_send_timestamp":"xxxx","client_track_timestamp":"xxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","device":"xxxxxxxxxxxxxxx","os_sdk_version":"11","client_version":"xxxxxxx","channel_type":"1","channel":"111111111111111111","mode":"xxxxxxxxxxxxxx","server":"111111111111111111","system_locale":"xxxxx","game_platform":"xxxxxxx","game_name":"xxxxxx","variant":"a","mute":false,"os_arch":"xxx","client_performance_cpu":8.08,"client_performance_memory":"111111","channel_id":"111111111111111111","channel_size_total":"1","channel_member_perms":"1111111111","channel_hidden":false,"anyone_priority":false,"game_exe_name":"xxxxxxxxxxxxx","game_id":"111111111111111111","guild_id":"111111111111111111","guild_size_total":"1","guild_member_num_roles":"1","guild_member_perms":"1111111111","guild_num_channels":"1","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","voice_state_count":"1","channel_size_online":"1","message_id":"111111111111111111","is_friend":false,"private":false,"num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":"1","emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","subscription_id":"111111111111111111","payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","removal_type":"xxxxxxxxxxxxxx","plan_id":"111111111111111111","is_initiator":false,"mutual_guilds":"1","type":"xxxxxxxxxx","user_guilds":"11","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","is_pending":false,"preview_enabled":false,"guild_affinity_score":8.08,"guild_affinity_index":"11","emoji_name":"xxxx","emoji_id":"111111111111111111","sku_id":"111111111111111111","quantity":"1","hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","port":"11111","connect_time":"111","connect_count":"1","audio_subsystem":"xxxxxxxx","audio_layer":"xxxxxxxxxxxxxxxx","cloudflare_best_region":"xxxxxxxxxx","context":"xxxxxxx","application_id":"111111111111111111","activity_duration_s":"111","total_duration_s":"1111","total_discord_sku_duration_s":"1","distributor":"xxxxx","priority":false,"packets_sent":"11","packets_sent_lost":"1","packets_received":"11","packets_received_lost":"1","channel_is_nsfw":false,"payment_id":"111111111111111111","transaction_id":"111111111111111111","transaction_type":"1","exchange_rate":1,"presentment_currency":"xxx","settlement_currency":"xxx","net_presentment_amount":"111","net_presentment_fees":"11","net_presentment_tax":"1","net_settlement_amount":"111","net_settlement_fees":"11","net_settlement_tax":"1","payment_gateway":"1","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","presentment_amount":"1111","presentment_fees":"111","presentment_tax":"1","settlement_amount":"1111","settlement_fees":"111","settlement_tax":"1","invite_code":"xxxxxx","invite_guild_id":"111111111111111111","invite_channel_id":"111111111111111111","invite_channel_type":"1","invite_type":"xxxxxxx","is_suggested":false,"row_num":"1","num_total":"111","is_filtered":false,"num_affinity_connections":"111","amount":"1111","amount_refunded":"1","tax":"1","tax_inclusive":false,"sku_type":"1","sku_subscription_plan_id":"111111111111111111","subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","payment_source_id":"111111111111111111","failure_message":"some/path","nonce":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","video_stream_count":"1","video_enabled":false,"connection_type":"xxxxxxx","custom_status_count":"1","effective_connection_speed":"xx","store_title":"xxxxx","gift_code":"xxxxxxxxxxxxxxxx","gift_code_max_uses":"1","resolved":false,"subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","scheduled_start_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","scheduled_end_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","access_type":"xxxx","expected_to_autorenew":false,"duration":"11111","full":false,"instant_invite":false,"has_images":false,"profile_user_status":"xxxxxxx","is_streaming":false,"has_custom_status":false,"application_name":"xxxxxxxxxxxxx","source":"xxxxxxxxxxxx","party_max":"1","protocol":"xxx","reconnect":false,"reason":"xxxxxxxxxxx","channel_bitrate":"111111","ping_average":"11","ping_bad_count":"1","input_detected":false,"no_input_detected_notice":false,"audio_jitter_buffer_mean":"1","audio_jitter_buffer_p75":"1","audio_jitter_buffer_p95":"1","audio_jitter_buffer_p99":"1","audio_jitter_buffer_max":"1","audio_jitter_delay_mean":"1","audio_jitter_delay_p75":"1","audio_jitter_delay_p95":"1","audio_jitter_delay_p99":"1","audio_jitter_delay_max":"1","audio_jitter_target_mean":"1","audio_jitter_target_p75":"1","audio_jitter_target_p95":"1","audio_jitter_target_p99":"1","audio_jitter_target_max":"1","relative_reception_delay_mean":"1","relative_reception_delay_p75":"1","relative_reception_delay_p95":"1","relative_reception_delay_p99":"1","relative_reception_delay_max":"1","relative_playout_delay_mean":"1","relative_playout_delay_p75":"1","relative_playout_delay_p95":"1","relative_playout_delay_p99":"1","relative_playout_delay_max":"1","mos_mean":1,"mos_1":"1","mos_2":"1","mos_3":"1","mos_4":"1","duration_connection_type_wifi":"1","duration_connection_type_cellular":"1","duration_connection_type_ethernet":"1","duration_connection_type_bluetooth":"1","duration_connection_type_other":"1","duration_connection_type_unknown":"111","duration_connection_type_none":"1","duration_effective_connection_speed_2g":"1","duration_effective_connection_speed_3g":"1","duration_effective_connection_speed_4g":"111","duration_effective_connection_speed_unknown":"1","ping_timeout":"1","audio_input_mode":"xxxxxxxxxxxx","automatic_audio_input_sensitivity_enabled":false,"audio_input_sensitivity":111,"echo_cancellation_enabled":false,"noise_suppression_enabled":false,"automatic_gain_control_enabled":false,"voice_output_volume":111,"frame_op_silent":"1","frame_op_normal":"1","frame_op_merged":"1","frame_op_expanded":"1","frame_op_accelerated":"1","frame_op_preemptive_expanded":"1","frame_op_cng":"1","max_voice_state_count":"1","duration_listening":"1","duration_speaking":"1","duration_participation":"1","duration_connected":"1","noise_cancellation_enabled":false,"decryption_failures":"1","encryption_mode":"xxxxxxxxxxxxxxx","audio_decoded_normal":"11","audio_decoded_plc":"11","audio_decoded_plccng":"11111","audio_decoded_cng":"1","audio_decoded_muted_output":"11111","search_type":"xxxxx","search_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","is_error":false,"limit":"11","offset":"1","page":"1","total_results":"1111","page_results":"11","is_indexing":false,"page_num_messages":"11","page_num_links":"11","page_num_embeds":"11","page_num_attach":"1","guild_ids":[],"search_engine_current":"xxxxxxxxxx","load_duration_ms":"111","num_modifiers":"1","custom":false,"guild":"111111111111111111","invite":"xxxxxx","user_day":"1111","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","size_total":"11","size_online":"11","guild_verification_level":"1","location_message_id":"111111111111111111","list_sort":"xxxxxxxxxxxxxxx","list_index":"1","game":"xxxxxxxxxxxxx","verified":false,"elevated":false,"duration_ms":"11111","party_platform":"xxxxxxx","activity_action":"1","activity_party_platform":"xxxxxxx","old_nsfw":false,"new_nsfw":false,"settings_type":"xxxxxxx","destination_pane":"xxxxxxxx","origin_pane":"xxxxxxxxxxxxxxx"} +{"event_type":"xxxxxxxxxxxx","event_id":"some/path","event_source":"xxx","user_id":"111111111111111111","domain":"xxxxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","user_is_authenticated":false,"accessibility_support_enabled":false,"browser_user_agent":"some/path","browser":"xxxxxxxxxxxxxx","browser_version":"xxxxxxxxxxxxx","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","os":"xxxxxxx","os_version":"xxxxxxxxxx","client_build_number":"11111","release_channel":"xxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","search_engine":"xxxxxx","location":"xxxxxxxxxxxxxxxxxxxxxxxx","city":"xxxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","isp":"xxxxxxxxxxxx","location_page":"xxxxxxxxxxxxx","location_section":"xxxxxxx","location_object":"xxxxxxxxxx","location_object_type":"xxx","subscription_type":"1","subscription_plan_id":"111111111111111111","payment_type":"xxxxxxxxxxxx","price":"1111","regular_price":"1111","currency":"xxx","is_gift":false,"to_step":"xxxxxxx","from_step":"xxxxxx","load_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","step_duration_ms":"1111","flow_duration_ms":"1111","accessibility_features":"111","eligible_for_trial":false,"client_send_timestamp":"xxxx","client_track_timestamp":"xxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","device":"xxxxxxxxxxxxxxx","os_sdk_version":"11","client_version":"xxxxxxx","channel_type":"1","channel":"111111111111111111","mode":"xxxxxxxxxxxxxx","server":"111111111111111111","system_locale":"xxxxx","game_platform":"xxxxxxx","game_name":"xxxxxx","variant":"a","mute":false,"os_arch":"xxx","client_performance_cpu":8.08,"client_performance_memory":"111111","channel_id":"111111111111111111","channel_size_total":"1","channel_member_perms":"1111111111","channel_hidden":false,"anyone_priority":false,"game_exe_name":"xxxxxxxxxxxxx","game_id":"111111111111111111","guild_id":"111111111111111111","guild_size_total":"1","guild_member_num_roles":"1","guild_member_perms":"1111111111","guild_num_channels":"1","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","voice_state_count":"1","channel_size_online":"1","message_id":"111111111111111111","is_friend":false,"private":false,"num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":"1","emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","subscription_id":"111111111111111111","payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","removal_type":"xxxxxxxxxxxxxx","plan_id":"111111111111111111","is_initiator":false,"mutual_guilds":"1","type":"xxxxxxxxxx","user_guilds":"11","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","is_pending":false,"preview_enabled":false,"guild_affinity_score":8.08,"guild_affinity_index":"11","emoji_name":"xxxx","emoji_id":"111111111111111111","sku_id":"111111111111111111","quantity":"1","hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","port":"11111","connect_time":"111","connect_count":"1","audio_subsystem":"xxxxxxxx","audio_layer":"xxxxxxxxxxxxxxxx","cloudflare_best_region":"xxxxxxxxxx","context":"xxxxxxx","application_id":"111111111111111111","activity_duration_s":"111","total_duration_s":"1111","total_discord_sku_duration_s":"1","distributor":"xxxxx","priority":false,"packets_sent":"11","packets_sent_lost":"1","packets_received":"11","packets_received_lost":"1","channel_is_nsfw":false,"payment_id":"111111111111111111","transaction_id":"111111111111111111","transaction_type":"1","exchange_rate":1,"presentment_currency":"xxx","settlement_currency":"xxx","net_presentment_amount":"111","net_presentment_fees":"11","net_presentment_tax":"1","net_settlement_amount":"111","net_settlement_fees":"11","net_settlement_tax":"1","payment_gateway":"1","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","presentment_amount":"1111","presentment_fees":"111","presentment_tax":"1","settlement_amount":"1111","settlement_fees":"111","settlement_tax":"1","invite_code":"xxxxxx","invite_guild_id":"111111111111111111","invite_channel_id":"111111111111111111","invite_channel_type":"1","invite_type":"xxxxxxx","is_suggested":false,"row_num":"1","num_total":"111","is_filtered":false,"num_affinity_connections":"111","amount":"1111","amount_refunded":"1","tax":"1","tax_inclusive":false,"sku_type":"1","sku_subscription_plan_id":"111111111111111111","subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","payment_source_id":"111111111111111111","failure_message":"some/path","nonce":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","video_stream_count":"1","video_enabled":false,"connection_type":"xxxxxxx","custom_status_count":"1","effective_connection_speed":"xx","store_title":"xxxxx","gift_code":"xxxxxxxxxxxxxxxx","gift_code_max_uses":"1","resolved":false,"subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","scheduled_start_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","scheduled_end_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","access_type":"xxxx","expected_to_autorenew":false,"duration":"11111","full":false,"instant_invite":false,"has_images":false,"profile_user_status":"xxxxxxx","is_streaming":false,"has_custom_status":false,"application_name":"xxxxxxxxxxxxx","source":"xxxxxxxxxxxx","party_max":"1","protocol":"xxx","reconnect":false,"reason":"xxxxxxxxxxx","channel_bitrate":"111111","ping_average":"11","ping_bad_count":"1","input_detected":false,"no_input_detected_notice":false,"audio_jitter_buffer_mean":"1","audio_jitter_buffer_p75":"1","audio_jitter_buffer_p95":"1","audio_jitter_buffer_p99":"1","audio_jitter_buffer_max":"1","audio_jitter_delay_mean":"1","audio_jitter_delay_p75":"1","audio_jitter_delay_p95":"1","audio_jitter_delay_p99":"1","audio_jitter_delay_max":"1","audio_jitter_target_mean":"1","audio_jitter_target_p75":"1","audio_jitter_target_p95":"1","audio_jitter_target_p99":"1","audio_jitter_target_max":"1","relative_reception_delay_mean":"1","relative_reception_delay_p75":"1","relative_reception_delay_p95":"1","relative_reception_delay_p99":"1","relative_reception_delay_max":"1","relative_playout_delay_mean":"1","relative_playout_delay_p75":"1","relative_playout_delay_p95":"1","relative_playout_delay_p99":"1","relative_playout_delay_max":"1","mos_mean":1,"mos_1":"1","mos_2":"1","mos_3":"1","mos_4":"1","duration_connection_type_wifi":"1","duration_connection_type_cellular":"1","duration_connection_type_ethernet":"1","duration_connection_type_bluetooth":"1","duration_connection_type_other":"1","duration_connection_type_unknown":"111","duration_connection_type_none":"1","duration_effective_connection_speed_2g":"1","duration_effective_connection_speed_3g":"1","duration_effective_connection_speed_4g":"111","duration_effective_connection_speed_unknown":"1","ping_timeout":"1","audio_input_mode":"xxxxxxxxxxxx","automatic_audio_input_sensitivity_enabled":false,"audio_input_sensitivity":111,"echo_cancellation_enabled":false,"noise_suppression_enabled":false,"automatic_gain_control_enabled":false,"voice_output_volume":111,"frame_op_silent":"1","frame_op_normal":"1","frame_op_merged":"1","frame_op_expanded":"1","frame_op_accelerated":"1","frame_op_preemptive_expanded":"1","frame_op_cng":"1","max_voice_state_count":"1","duration_listening":"1","duration_speaking":"1","duration_participation":"1","duration_connected":"1","noise_cancellation_enabled":false,"decryption_failures":"1","encryption_mode":"xxxxxxxxxxxxxxx","audio_decoded_normal":"11","audio_decoded_plc":"11","audio_decoded_plccng":"11111","audio_decoded_cng":"1","audio_decoded_muted_output":"11111","search_type":"xxxxx","search_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","is_error":false,"limit":"11","offset":"1","page":"1","total_results":"1111","page_results":"11","is_indexing":false,"page_num_messages":"11","page_num_links":"11","page_num_embeds":"11","page_num_attach":"1","guild_ids":[],"search_engine_current":"xxxxxxxxxx","load_duration_ms":"111","num_modifiers":"1","custom":false,"guild":"111111111111111111","invite":"xxxxxx","user_day":"1111","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","size_total":"11","size_online":"11","guild_verification_level":"1","location_message_id":"111111111111111111","list_sort":"xxxxxxxxxxxxxxx","list_index":"1","game":"xxxxxxxxxxxxx","verified":false,"elevated":false,"duration_ms":"11111","party_platform":"xxxxxxx","activity_action":"1","activity_party_platform":"xxxxxxx","old_nsfw":false,"new_nsfw":false,"settings_type":"xxxxxxx","destination_pane":"xxxxxxxx","origin_pane":"xxxxxxxxxxxxxxx"} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/activity/reporting/events-2021-00000-of-00001.json b/test/fixtures/discord-json-2021-01/activity/reporting/events-2021-00000-of-00001.json new file mode 100644 index 0000000..a289f7c --- /dev/null +++ b/test/fixtures/discord-json-2021-01/activity/reporting/events-2021-00000-of-00001.json @@ -0,0 +1,2 @@ +{"event_type":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","event_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_id":"111111111111111111","domain":"xxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","variant":"a","browser":"xxxxxxxxxxxxxx","device":"xxxxxxx","os":"xxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","guild_id":"111111111111111111","guild_size_total":"1","guild_member_num_roles":"1","guild_member_perms":"111111111","guild_num_channels":"1","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"channel_id":"111111111111111111","channel_type":"1","channel_size_total":"1","channel_size_online":"1","channel_member_perms":"111111111","channel_hidden":false,"client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","search_engine":"xxxxxx","city":"xxxxxxx","event_source":"xxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","browser_user_agent":"some/path","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","os_version":"xxxxxxxxxx","os_sdk_version":"11","client_build_number":"11111","client_version":"xxxxxxx","user_is_authenticated":false,"isp":"xxxxxxxxxxxx","accessibility_support_enabled":false,"accessibility_features":"111","preview_enabled":false,"browser_version":"xxxxxxxxxxxxx","release_channel":"xxxxxx","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","os_arch":"xxx","is_member":false,"client_performance_cpu":8.08,"client_performance_memory":"111111","num_voice_channels_active":"1","location_section":"xxxxxxx","system_locale":"xxxxx","type":"xxxxxxxxxx","game_name":"xxxxxxxxxxxxxxxxx","game_platform":"xxxxxxx","location_object":"xxxxxxxxxx","has_custom_status":false,"is_friend":false,"has_images":false,"profile_user_status":"xxxxxxx","is_streaming":false,"application_name":"xxx","application_id":"111111111111111111","party_max":"1","source":"xxxxxxxx","payment_source_id":"111111111111111111","payment_source_type":"1","payment_gateway":"xxxxxx","is_default":false,"text_len":"11","clear_after":"xxxxx","message_id":"111111111111111111","channel":"111111111111111111","private":false,"server":"111111111111111111","num_attachments":"1","max_attachment_size":"1","length":"1","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":"1","emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","location":"xxxxxxxxxxxxxxxxxxxx","old_nsfw":false,"new_nsfw":false,"captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"source_page":"xxxxxxxxxxxx","source_section":"xxxxxxxxxx","source_object":"xxxxxxxx","sku_id":"111111111111111111","sku_type":"1","store_title":"xxxxxxx","distribution_type":"xxxxxxxxxxxx","price":"1111","regular_price":"1111","currency":"xxx","load_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","has_description":false,"has_staff_review":false,"carousel_image_count":"1","carousel_video_count":"1","has_single_player":false,"has_online_multiplayer":false,"has_local_multiplayer":false,"has_pvp_features":false,"has_local_coop":false,"has_online_coop":false,"has_cross_platform":false,"has_rich_presence":false,"has_game_invites":false,"has_spectator_mode":false,"has_controller_support":false,"has_cloud_saves":false,"has_secure_networking":false,"payment_id":"111111111111111111","transaction_id":"111111111111111111","transaction_type":"1","exchange_rate":1,"presentment_currency":"xxx","settlement_currency":"xxx","net_presentment_amount":"111","net_presentment_fees":"11","net_presentment_tax":"1","net_settlement_amount":"111","net_settlement_fees":"11","net_settlement_tax":"1","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","presentment_amount":"1111","presentment_fees":"111","presentment_tax":"1","settlement_amount":"1111","settlement_fees":"111","settlement_tax":"1","subscription_id":"111111111111111111","subscription_type":"1","payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","removal_type":"xxxxxxxxxxxxxx","plan_id":"111111111111111111","connected":false,"platform_type":"xxxxxxx","visibility":"1","friend_sync":false,"partner":false,"link_method":"xxxxx","verified":false,"distributor":"xxxxxxx","elevated":false,"mode":"xxxxxxxxxxxxxx","priority":false,"game_ids_viewed":[],"store_application_ids_viewed":[],"store_sku_ids_viewed":[],"num_games_viewed":"11","num_cards_viewed":"11","seconds_spent":"1","subscribed_games":["111111111111111111","111111111111111111"],"num_cards_total":"11","news_ids_viewed":[],"feed_layout":"xxxxxxxxxxxx","window_width":"111","window_height":"111","plan":"xxxxxxxxxxxxx","has_auth_token":false,"invite_code":"xxxxxxx","is_backgrounded":false,"mute":false,"anyone_priority":false,"party_platform":"xxxxxxx","emoji_id":"111111111111111111","join_method":"xxxxxxxxxxxxxxxxx","user_guilds":"11","join_type":"xxxx","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","location_message_id":"111111111111111111","location_page":"xxxxxxxxxxxxx","location_object_type":"xxx","payment_type":"xxxxxxxxxxxx","is_gift":false,"eligible_for_trial":false,"subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_plan_id":"111111111111111111","quantity":"1","device_name":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","num_guild_permissions":"1","source_object_type":"xxx","error_message":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","request_status":"111","price_shown":"1111","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","is_premium":false,"amount":"1111","amount_refunded":"1","tax":"111","tax_inclusive":false,"subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","sku_subscription_plan_id":"111111111111111111","failure_message":"some/path","search_engine_current":"xxxxxxxxxx","card_index":"1","card_type":"xxxxxxxxxxx","opened_from":"xxxxxxxxxxxx","uri_host":"xxx","uri_scheme":"xxxxxxx","theme":"xxxx","application_ids_viewed":[],"sku_ids_viewed":[],"duration_ms":"1111","game_id":"111111111111111111","is_new_user":false,"scheduled_start_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","scheduled_end_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","access_type":"xxxx","expected_to_autorenew":false,"adjust_tracker_token":"xxxxxx","adjust_tracker_name":"xxxxxxx","attribution_network":"xxxxxxx","num_applications_total":"1","num_applications_battlenet":"1","num_applications_discord":"1","num_applications_steam":"1","num_applications_twitch":"1","num_applications_uplay":"1","num_applications_origin":"1","num_applications_gog":"1","num_applications_epic":"1","gift_code":"xxxxxxxxxxxxxxxx","gift_code_max_uses":"1","resolved":false,"harvest_id":"111111111111111111","full":false,"instant_invite":false,"activity_duration_s":"111","total_duration_s":"1111","total_discord_sku_duration_s":"1","promotion_id":"1111111","list_index":"1","promotion_type":"xxxxxxxxxxxxxxx","promotion_url":"url://somewhere","custom":false,"guild":"111111111111111111","invite":"xxxxxxx","user_day":"1111","size_total":"111","size_online":"11","invite_type":"xxxxxxx","guild_verification_level":"1","guild_size_online":"11","list_sort":"xxxxxxxxxxxxxxx","game_ids":[],"num_cards":"1","num_cards_visible":"1","num_cards_game_news":"1","num_cards_game_playable":"1","num_game_parties":"1","num_game_parties_voice":"1","num_game_parties_solo":"1","num_game_parties_recently_played":"1","num_game_parties_rich_presence":"1","num_game_parties_collapsed":"1","num_users_subscribed":"11","num_launcher_applications":"1","guild_ids_viewed":[],"num_items_now_playing":"1","num_items_recently_played":"1","name":"xxxxxxx","has_bot":false,"has_redirect_uri":false,"activity_action":"1","activity_party_platform":"xxxxxxx","success":false} +{"event_type":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","event_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_id":"111111111111111111","domain":"xxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","variant":"a","browser":"xxxxxxxxxxxxxx","device":"xxxxxxx","os":"xxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","guild_id":"111111111111111111","guild_size_total":"1","guild_member_num_roles":"1","guild_member_perms":"111111111","guild_num_channels":"1","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"1","guild_is_vip":false,"channel_id":"111111111111111111","channel_type":"1","channel_size_total":"1","channel_size_online":"1","channel_member_perms":"111111111","channel_hidden":false,"client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","search_engine":"xxxxxx","city":"xxxxxxx","event_source":"xxxxxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","browser_user_agent":"some/path","cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","os_version":"xxxxxxxxxx","os_sdk_version":"11","client_build_number":"11111","client_version":"xxxxxxx","user_is_authenticated":false,"isp":"xxxxxxxxxxxx","accessibility_support_enabled":false,"accessibility_features":"111","preview_enabled":false,"browser_version":"xxxxxxxxxxxxx","release_channel":"xxxxxx","referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","os_arch":"xxx","is_member":false,"client_performance_cpu":8.08,"client_performance_memory":"111111","num_voice_channels_active":"1","location_section":"xxxxxxx","system_locale":"xxxxx","type":"xxxxxxxxxx","game_name":"xxxxxxxxxxxxxxxxx","game_platform":"xxxxxxx","location_object":"xxxxxxxxxx","has_custom_status":false,"is_friend":false,"has_images":false,"profile_user_status":"xxxxxxx","is_streaming":false,"application_name":"xxx","application_id":"111111111111111111","party_max":"1","source":"xxxxxxxx","payment_source_id":"111111111111111111","payment_source_type":"1","payment_gateway":"xxxxxx","is_default":false,"text_len":"11","clear_after":"xxxxx","message_id":"111111111111111111","channel":"111111111111111111","private":false,"server":"111111111111111111","num_attachments":"1","max_attachment_size":"1","length":"1","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":"1","emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","location":"xxxxxxxxxxxxxxxxxxxx","old_nsfw":false,"new_nsfw":false,"captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"source_page":"xxxxxxxxxxxx","source_section":"xxxxxxxxxx","source_object":"xxxxxxxx","sku_id":"111111111111111111","sku_type":"1","store_title":"xxxxxxx","distribution_type":"xxxxxxxxxxxx","price":"1111","regular_price":"1111","currency":"xxx","load_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","has_description":false,"has_staff_review":false,"carousel_image_count":"1","carousel_video_count":"1","has_single_player":false,"has_online_multiplayer":false,"has_local_multiplayer":false,"has_pvp_features":false,"has_local_coop":false,"has_online_coop":false,"has_cross_platform":false,"has_rich_presence":false,"has_game_invites":false,"has_spectator_mode":false,"has_controller_support":false,"has_cloud_saves":false,"has_secure_networking":false,"payment_id":"111111111111111111","transaction_id":"111111111111111111","transaction_type":"1","exchange_rate":1,"presentment_currency":"xxx","settlement_currency":"xxx","net_presentment_amount":"111","net_presentment_fees":"11","net_presentment_tax":"1","net_settlement_amount":"111","net_settlement_fees":"11","net_settlement_tax":"1","created_at":"xxxxxxxxxxxxxxxxxxxxxxx","presentment_amount":"1111","presentment_fees":"111","presentment_tax":"1","settlement_amount":"1111","settlement_fees":"111","settlement_tax":"1","subscription_id":"111111111111111111","subscription_type":"1","payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","removal_type":"xxxxxxxxxxxxxx","plan_id":"111111111111111111","connected":false,"platform_type":"xxxxxxx","visibility":"1","friend_sync":false,"partner":false,"link_method":"xxxxx","verified":false,"distributor":"xxxxxxx","elevated":false,"mode":"xxxxxxxxxxxxxx","priority":false,"game_ids_viewed":[],"store_application_ids_viewed":[],"store_sku_ids_viewed":[],"num_games_viewed":"11","num_cards_viewed":"11","seconds_spent":"1","subscribed_games":["111111111111111111","111111111111111111"],"num_cards_total":"11","news_ids_viewed":[],"feed_layout":"xxxxxxxxxxxx","window_width":"111","window_height":"111","plan":"xxxxxxxxxxxxx","has_auth_token":false,"invite_code":"xxxxxxx","is_backgrounded":false,"mute":false,"anyone_priority":false,"party_platform":"xxxxxxx","emoji_id":"111111111111111111","join_method":"xxxxxxxxxxxxxxxxx","user_guilds":"11","join_type":"xxxx","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","location_message_id":"111111111111111111","location_page":"xxxxxxxxxxxxx","location_object_type":"xxx","payment_type":"xxxxxxxxxxxx","is_gift":false,"eligible_for_trial":false,"subscription_plan_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_plan_id":"111111111111111111","quantity":"1","device_name":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","num_guild_permissions":"1","source_object_type":"xxx","error_message":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","request_status":"111","price_shown":"1111","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","utm_medium":"xxxxxxx","utm_source":"xxxxxxxxxxx","is_premium":false,"amount":"1111","amount_refunded":"1","tax":"111","tax_inclusive":false,"subscription_payment_gateway_plan_id":"xxxxxxxxxxxxxxxxxxx","subscription_current_period_start":"xxxxxxxxxxxxxxxxxxxxxxxxxx","subscription_current_period_end":"xxxxxxxxxxxxxxxxxxxxxxxxxx","sku_subscription_plan_id":"111111111111111111","failure_message":"some/path","search_engine_current":"xxxxxxxxxx","card_index":"1","card_type":"xxxxxxxxxxx","opened_from":"xxxxxxxxxxxx","uri_host":"xxx","uri_scheme":"xxxxxxx","theme":"xxxx","application_ids_viewed":[],"sku_ids_viewed":[],"duration_ms":"1111","game_id":"111111111111111111","is_new_user":false,"scheduled_start_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","scheduled_end_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","access_type":"xxxx","expected_to_autorenew":false,"adjust_tracker_token":"xxxxxx","adjust_tracker_name":"xxxxxxx","attribution_network":"xxxxxxx","num_applications_total":"1","num_applications_battlenet":"1","num_applications_discord":"1","num_applications_steam":"1","num_applications_twitch":"1","num_applications_uplay":"1","num_applications_origin":"1","num_applications_gog":"1","num_applications_epic":"1","gift_code":"xxxxxxxxxxxxxxxx","gift_code_max_uses":"1","resolved":false,"harvest_id":"111111111111111111","full":false,"instant_invite":false,"activity_duration_s":"111","total_duration_s":"1111","total_discord_sku_duration_s":"1","promotion_id":"1111111","list_index":"1","promotion_type":"xxxxxxxxxxxxxxx","promotion_url":"url://somewhere","custom":false,"guild":"111111111111111111","invite":"xxxxxxx","user_day":"1111","size_total":"111","size_online":"11","invite_type":"xxxxxxx","guild_verification_level":"1","guild_size_online":"11","list_sort":"xxxxxxxxxxxxxxx","game_ids":[],"num_cards":"1","num_cards_visible":"1","num_cards_game_news":"1","num_cards_game_playable":"1","num_game_parties":"1","num_game_parties_voice":"1","num_game_parties_solo":"1","num_game_parties_recently_played":"1","num_game_parties_rich_presence":"1","num_game_parties_collapsed":"1","num_users_subscribed":"11","num_launcher_applications":"1","guild_ids_viewed":[],"num_items_now_playing":"1","num_items_recently_played":"1","name":"xxxxxxx","has_bot":false,"has_redirect_uri":false,"activity_action":"1","activity_party_platform":"xxxxxxx","success":false} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/activity/tns/events-2021-00000-of-00001.json b/test/fixtures/discord-json-2021-01/activity/tns/events-2021-00000-of-00001.json new file mode 100644 index 0000000..dc5db5f --- /dev/null +++ b/test/fixtures/discord-json-2021-01/activity/tns/events-2021-00000-of-00001.json @@ -0,0 +1,2 @@ +{"event_type":"xxxxxxxxxxxxxx","event_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","event_source":"xxxxxx","user_id":"111111111111111111","domain":"xxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","freight_id":"xxxxxxxxxxxxxxxxxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","user_is_authenticated":false,"cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","city":"xxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","isp":"xxxxxxxxxxxx","message_id":"111111111111111111","channel":"111111111111111111","channel_type":"1","is_friend":false,"private":false,"num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":false,"emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","browser":"xxxxxxxxxxxxxx","os":"xxxxxxx","os_version":"xxxxxxxxxx","os_arch":"xxx","client_build_number":"11111","release_channel":"xxxxxx","client_version":"xxxxxxx","server":"111111111111111111","browser_user_agent":"some/path","device":"xxxxxxxxxxxxxxxxx","device_advertiser_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","os_sdk_version":"11","accessibility_support_enabled":false,"accessibility_features":"111","system_locale":"xxxxx","browser_version":"xxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","is_new_user":false,"referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","search_engine_current":"xxxxxxxxxx","client_uuid":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","client_performance_cpu":8.08,"client_performance_memory":"111111","guild_id":"111111111111111111","guild_size_total":"111","guild_member_num_roles":"1","guild_member_perms":"111111111","guild_num_channels":"11","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"11","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","channel_id":"111111111111111111","channel_size_total":"1","channel_member_perms":"111111111","channel_hidden":false,"mode":"xxxxxxxxxxxxxx","priority":false,"media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","packets_sent":"111","packets_sent_lost":"1","packets_received":"1111","packets_received_lost":"1","voice_state_count":"1","game_name":"xxxxxxxxxxxxxxxxx","game_exe_name":"xxxxxxxxx","is_initiator":false,"mutual_guilds":"1","type":"xxxxxxxxxx","user_guilds":"11","location":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","activity_action":"1","activity_party_platform":"xxxxxxx","invite_type":"xxxxxxx","invite_code":"xxxxxx","invite_channel_id":"111111111111111111","invite_channel_type":"1","is_suggested":false,"row_num":"1","num_total":"111","is_filtered":false,"num_affinity_connections":"111","invite_guild_id":"111111111111111111","app_id":"111111111111111111","transport":"xxx","resolved":false,"code":"xxxxxx","authenticated":false,"user_banned":false,"error_code":"11111","error_message":"xxxxxxxxxxxxxx","size_total":"1","size_online":"1","preview_enabled":false,"location_section":"xxxxxxx","channel_is_nsfw":false,"hostname":"xxxxxxxxxxxxxxxxxxxxxxxx","port":"11111","protocol":"xxx","reconnect":false,"reason":"xxxxxxxxxxx","duration":"1111111","channel_bitrate":"11111","connect_count":"1","ping_average":"11","ping_bad_count":"1","audio_jitter_buffer_mean":"1","audio_jitter_buffer_p75":"1","audio_jitter_buffer_p95":"1","audio_jitter_buffer_p99":"1","audio_jitter_buffer_max":"1","audio_jitter_delay_mean":"1","audio_jitter_delay_p75":"1","audio_jitter_delay_p95":"1","audio_jitter_delay_p99":"1","audio_jitter_delay_max":"1","audio_jitter_target_mean":"1","audio_jitter_target_p75":"1","audio_jitter_target_p95":"1","audio_jitter_target_p99":"1","audio_jitter_target_max":"1","relative_reception_delay_mean":"1","relative_reception_delay_p75":"1","relative_reception_delay_p95":"1","relative_reception_delay_p99":"1","relative_reception_delay_max":"1","relative_playout_delay_mean":"1","relative_playout_delay_p75":"1","relative_playout_delay_p95":"1","relative_playout_delay_p99":"1","relative_playout_delay_max":"1","mos_mean":8.08,"mos_1":"1","mos_2":"1","mos_3":"11","mos_4":"111","audio_input_mode":"xxxxxxxxxxxxxx","frame_op_silent":"1111111","frame_op_normal":"1111","frame_op_merged":"11","frame_op_expanded":"111","frame_op_accelerated":"111","frame_op_preemptive_expanded":"1111","frame_op_cng":"1","automatic_audio_input_sensitivity_enabled":false,"audio_input_sensitivity":8.08,"echo_cancellation_enabled":false,"noise_suppression_enabled":false,"automatic_gain_control_enabled":false,"voice_output_volume":111,"duration_listening":"111","duration_speaking":"11","duration_participation":"111","duration_connected":"1111","noise_cancellation_enabled":false,"duration_connection_type_wifi":"1","duration_connection_type_cellular":"1","duration_connection_type_ethernet":"1","duration_connection_type_bluetooth":"1","duration_connection_type_other":"1","duration_connection_type_unknown":"1111","duration_connection_type_none":"1","duration_effective_connection_speed_2g":"1","duration_effective_connection_speed_3g":"1","duration_effective_connection_speed_4g":"1111","duration_effective_connection_speed_unknown":"1","context":"xxxxxxx","ping_timeout":"1","input_detected":false,"no_input_detected_notice":false,"max_voice_state_count":"1","cloudflare_best_region":"xxxxxxxxxx","decryption_failures":"1","encryption_mode":"xxxxxxxxxxxxxxx","mute":false,"anyone_priority":false,"convert_emoticons":false,"developer_mode":false,"enable_tts_command":false,"friend_source_flags":"11","guild_positions":["111111111111111111","111111111111111111"],"inline_attachment_media":false,"inline_embed_media":false,"gif_auto_play":false,"locale":"xxxxx","message_display_compact":false,"render_embeds":false,"render_reactions":false,"animate_emoji":false,"restricted_guilds":[],"show_current_game":false,"theme":"xxxx","detect_platform_accounts":false,"status":"xxxxxx","default_guilds_restricted":false,"explicit_content_filter":"1","afk_timeout":"111","timezone_offset":"111","disable_games_tab":false,"num_server_folders":"1","stream_notifications_enabled":false,"has_custom_status":false,"allow_accessibility_detection":false,"contact_sync_enabled":false,"native_phone_integration_enabled":false,"animate_stickers":"1","guild_name":"xxxxxxxxxxx","captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"animated":false,"application_name":"xxxxxxxxxxxx","is_streaming":false,"has_images":false,"profile_user_status":"xxxxxxxxxxxxxx","location_object":"xxxxxx","game_platform":"xxxxxxx","custom":false,"guild":"111111111111111111","invite":"xxxxxx","user_day":"1111","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","guild_verification_level":"1","search_engine":"xxxxxx","location_message_id":"111111111111111111","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","emoji_id":"111111111111111111","application_id":"111111111111111111","name":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","bucket":"1","revision":"1","population":"xx","client_event_source":"xxxxxxx","join_method":"xxxxxxxxxxxxxxxxx","join_type":"xxxx","account_id":"xxxxxxxx","account_name":"xxxxxxxx","connected":false,"platform_type":"xxxxxxx","visibility":"1","friend_sync":false,"partner":false,"link_method":"xxxxx","temporary":false,"max_uses":"1","max_age":"11111","regenerate":false,"unique":false,"video_stream_count":"1","video_enabled":false,"video_input_type":"xxxx","enabled_inputs":[],"source":"xxxxxxxxxxxx","owner":false,"harvest_id":"111111111111111111","channel_name":"xxxxxxxxxxxxxxxxx","is_nsfw":false,"custom_status_count":"1","game_id":"111111111111111111","connection_type":"xxxxxxxx","emoji_name":"xxxx","party_id":"xxxxxxxxxxxxxxxxxxxxxxxxxx","party_platform":"xxxxxxx","sku_id":"111111111111111111","identity_type":"xxxxx"} +{"event_type":"xxxxxxxxxxxxxx","event_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","event_source":"xxxxxx","user_id":"111111111111111111","domain":"xxx","freight_hostname":"xxxxxxxxxxxxxxxxxxxxxxxxx","freight_id":"xxxxxxxxxxxxxxxxxxxxxxxx","ip":"1.1.1.1","day":"1111","chosen_locale":"xxxxx","detected_locale":"xxxxx","user_is_authenticated":false,"cfduid":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a","city":"xxxxxxxxx","country_code":"xx","region_code":"xx","time_zone":"some/path","isp":"xxxxxxxxxxxx","message_id":"111111111111111111","channel":"111111111111111111","channel_type":"1","is_friend":false,"private":false,"num_attachments":"1","max_attachment_size":"1","length":"11","word_count":"1","mention_everyone":false,"emoji_unicode":"1","emoji_custom":"1","emoji_custom_external":"1","emoji_managed":"1","emoji_managed_external":"1","emoji_animated":false,"emoji_only":false,"num_embeds":"1","attachment_ids":[],"has_spoiler":false,"probably_has_markdown":false,"user_is_bot":false,"sticker_ids":[],"message_type":"1","client_send_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","client_track_timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","timestamp":"xxxxxxxxxxxxxxxxxxxxxxxxxx","browser":"xxxxxxxxxxxxxx","os":"xxxxxxx","os_version":"xxxxxxxxxx","os_arch":"xxx","client_build_number":"11111","release_channel":"xxxxxx","client_version":"xxxxxxx","server":"111111111111111111","browser_user_agent":"some/path","device":"xxxxxxxxxxxxxxxxx","device_advertiser_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","os_sdk_version":"11","accessibility_support_enabled":false,"accessibility_features":"111","system_locale":"xxxxx","browser_version":"xxxx","referrer":"url://somewhere","referring_domain":"xxxxxxxxxxxxxx","is_new_user":false,"referrer_current":"url://somewhere","referring_domain_current":"xxxxxxxxxxxxxx","search_engine_current":"xxxxxxxxxx","client_uuid":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","client_performance_cpu":8.08,"client_performance_memory":"111111","guild_id":"111111111111111111","guild_size_total":"111","guild_member_num_roles":"1","guild_member_perms":"111111111","guild_num_channels":"11","guild_num_text_channels":"1","guild_num_voice_channels":"1","guild_num_roles":"11","guild_is_vip":false,"is_member":false,"num_voice_channels_active":"1","channel_id":"111111111111111111","channel_size_total":"1","channel_member_perms":"111111111","channel_hidden":false,"mode":"xxxxxxxxxxxxxx","priority":false,"media_session_id":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","rtc_connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","packets_sent":"111","packets_sent_lost":"1","packets_received":"1111","packets_received_lost":"1","voice_state_count":"1","game_name":"xxxxxxxxxxxxxxxxx","game_exe_name":"xxxxxxxxx","is_initiator":false,"mutual_guilds":"1","type":"xxxxxxxxxx","user_guilds":"11","location":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","activity_action":"1","activity_party_platform":"xxxxxxx","invite_type":"xxxxxxx","invite_code":"xxxxxx","invite_channel_id":"111111111111111111","invite_channel_type":"1","is_suggested":false,"row_num":"1","num_total":"111","is_filtered":false,"num_affinity_connections":"111","invite_guild_id":"111111111111111111","app_id":"111111111111111111","transport":"xxx","resolved":false,"code":"xxxxxx","authenticated":false,"user_banned":false,"error_code":"11111","error_message":"xxxxxxxxxxxxxx","size_total":"1","size_online":"1","preview_enabled":false,"location_section":"xxxxxxx","channel_is_nsfw":false,"hostname":"xxxxxxxxxxxxxxxxxxxxxxxx","port":"11111","protocol":"xxx","reconnect":false,"reason":"xxxxxxxxxxx","duration":"1111111","channel_bitrate":"11111","connect_count":"1","ping_average":"11","ping_bad_count":"1","audio_jitter_buffer_mean":"1","audio_jitter_buffer_p75":"1","audio_jitter_buffer_p95":"1","audio_jitter_buffer_p99":"1","audio_jitter_buffer_max":"1","audio_jitter_delay_mean":"1","audio_jitter_delay_p75":"1","audio_jitter_delay_p95":"1","audio_jitter_delay_p99":"1","audio_jitter_delay_max":"1","audio_jitter_target_mean":"1","audio_jitter_target_p75":"1","audio_jitter_target_p95":"1","audio_jitter_target_p99":"1","audio_jitter_target_max":"1","relative_reception_delay_mean":"1","relative_reception_delay_p75":"1","relative_reception_delay_p95":"1","relative_reception_delay_p99":"1","relative_reception_delay_max":"1","relative_playout_delay_mean":"1","relative_playout_delay_p75":"1","relative_playout_delay_p95":"1","relative_playout_delay_p99":"1","relative_playout_delay_max":"1","mos_mean":8.08,"mos_1":"1","mos_2":"1","mos_3":"11","mos_4":"111","audio_input_mode":"xxxxxxxxxxxxxx","frame_op_silent":"1111111","frame_op_normal":"1111","frame_op_merged":"11","frame_op_expanded":"111","frame_op_accelerated":"111","frame_op_preemptive_expanded":"1111","frame_op_cng":"1","automatic_audio_input_sensitivity_enabled":false,"audio_input_sensitivity":8.08,"echo_cancellation_enabled":false,"noise_suppression_enabled":false,"automatic_gain_control_enabled":false,"voice_output_volume":111,"duration_listening":"111","duration_speaking":"11","duration_participation":"111","duration_connected":"1111","noise_cancellation_enabled":false,"duration_connection_type_wifi":"1","duration_connection_type_cellular":"1","duration_connection_type_ethernet":"1","duration_connection_type_bluetooth":"1","duration_connection_type_other":"1","duration_connection_type_unknown":"1111","duration_connection_type_none":"1","duration_effective_connection_speed_2g":"1","duration_effective_connection_speed_3g":"1","duration_effective_connection_speed_4g":"1111","duration_effective_connection_speed_unknown":"1","context":"xxxxxxx","ping_timeout":"1","input_detected":false,"no_input_detected_notice":false,"max_voice_state_count":"1","cloudflare_best_region":"xxxxxxxxxx","decryption_failures":"1","encryption_mode":"xxxxxxxxxxxxxxx","mute":false,"anyone_priority":false,"convert_emoticons":false,"developer_mode":false,"enable_tts_command":false,"friend_source_flags":"11","guild_positions":["111111111111111111","111111111111111111"],"inline_attachment_media":false,"inline_embed_media":false,"gif_auto_play":false,"locale":"xxxxx","message_display_compact":false,"render_embeds":false,"render_reactions":false,"animate_emoji":false,"restricted_guilds":[],"show_current_game":false,"theme":"xxxx","detect_platform_accounts":false,"status":"xxxxxx","default_guilds_restricted":false,"explicit_content_filter":"1","afk_timeout":"111","timezone_offset":"111","disable_games_tab":false,"num_server_folders":"1","stream_notifications_enabled":false,"has_custom_status":false,"allow_accessibility_detection":false,"contact_sync_enabled":false,"native_phone_integration_enabled":false,"animate_stickers":"1","guild_name":"xxxxxxxxxxx","captcha_service":"xxxxxxxx","sitekey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_flow":"xxxxxxxxx","force_bad":false,"animated":false,"application_name":"xxxxxxxxxxxx","is_streaming":false,"has_images":false,"profile_user_status":"xxxxxxxxxxxxxx","location_object":"xxxxxx","game_platform":"xxxxxxx","custom":false,"guild":"111111111111111111","invite":"xxxxxx","user_day":"1111","location_guild_id":"111111111111111111","location_channel_id":"111111111111111111","location_channel_type":"1","guild_verification_level":"1","search_engine":"xxxxxx","location_message_id":"111111111111111111","session":"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1","emoji_id":"111111111111111111","application_id":"111111111111111111","name":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","bucket":"1","revision":"1","population":"xx","client_event_source":"xxxxxxx","join_method":"xxxxxxxxxxxxxxxxx","join_type":"xxxx","account_id":"xxxxxxxx","account_name":"xxxxxxxx","connected":false,"platform_type":"xxxxxxx","visibility":"1","friend_sync":false,"partner":false,"link_method":"xxxxx","temporary":false,"max_uses":"1","max_age":"11111","regenerate":false,"unique":false,"video_stream_count":"1","video_enabled":false,"video_input_type":"xxxx","enabled_inputs":[],"source":"xxxxxxxxxxxx","owner":false,"harvest_id":"111111111111111111","channel_name":"xxxxxxxxxxxxxxxxx","is_nsfw":false,"custom_status_count":"1","game_id":"111111111111111111","connection_type":"xxxxxxxx","emoji_name":"xxxx","party_id":"xxxxxxxxxxxxxxxxxxxxxxxxxx","party_platform":"xxxxxxx","sku_id":"111111111111111111","identity_type":"xxxxx"} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/messages/11111111111111111/channel.json b/test/fixtures/discord-json-2021-01/messages/11111111111111111/channel.json new file mode 100644 index 0000000..685450c --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/11111111111111111/channel.json @@ -0,0 +1 @@ +{"id": "11111111111111111", "type": 0} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/messages/11111111111111111/messages.csv b/test/fixtures/discord-json-2021-01/messages/11111111111111111/messages.csv new file mode 100644 index 0000000..e5a1674 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/11111111111111111/messages.csv @@ -0,0 +1,2 @@ +ID,Timestamp,Contents,Attachments +8888888888,2022-02-22 22:22:22.222222+00:00,Heyo, diff --git a/test/fixtures/discord-json-2021-01/messages/222222222222222222/channel.json b/test/fixtures/discord-json-2021-01/messages/222222222222222222/channel.json new file mode 100644 index 0000000..d461056 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/222222222222222222/channel.json @@ -0,0 +1 @@ +{"id": "222222222222222222", "type": 1, "recipients": ["00000000000000000", "1111111111111111"]} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/messages/222222222222222222/messages.csv b/test/fixtures/discord-json-2021-01/messages/222222222222222222/messages.csv new file mode 100644 index 0000000..9c1324a --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/222222222222222222/messages.csv @@ -0,0 +1,2 @@ +ID,Timestamp,Contents,Attachments +2222222222222,2022-22-22 22:22:22.22222+00:00,Heyo, diff --git a/test/fixtures/discord-json-2021-01/messages/333333333333333333/channel.json b/test/fixtures/discord-json-2021-01/messages/333333333333333333/channel.json new file mode 100644 index 0000000..5892587 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/333333333333333333/channel.json @@ -0,0 +1 @@ +{"id": "333333333333333333", "type": 0, "name": "generalchat", "guild": {"id": "333333333333333332", "name": "xxx"}} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/messages/333333333333333333/messages.csv b/test/fixtures/discord-json-2021-01/messages/333333333333333333/messages.csv new file mode 100644 index 0000000..5603929 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/333333333333333333/messages.csv @@ -0,0 +1,6 @@ +ID,Timestamp,Contents,Attachments +000000000000000005,2011-02-02 02:05:02.000000+00:00,Huh what the heck is this message, +000000000000000004,2011-02-02 02:04:02.000000+00:00,<:thonk:000000000000000000><:thonk:000000000000000000><:thonk:000000000000000000>, +000000000000000003,2011-02-02 02:03:02.000000+00:00,"(so <@00000000000000000> who are you)", +000000000000000002,2011-02-02 02:02:02.000000+00:00,,https://cdn.discordapp.com/attachments/000000000000000000/000000000000000000/image.png +000000000000000001,2011-02-02 02:01:02.000000+00:00,https://google.com/whatever, diff --git a/test/fixtures/discord-json-2021-01/messages/index.json b/test/fixtures/discord-json-2021-01/messages/index.json new file mode 100644 index 0000000..10c1f13 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/messages/index.json @@ -0,0 +1,5 @@ +{ + "11111111111111111": null, + "222222222222222222": "Direct Message with xxx#7777", + "333333333333333333": "generalchat" +} \ No newline at end of file diff --git a/test/fixtures/discord-json-2021-01/servers/444444444444444444/audit-log.json b/test/fixtures/discord-json-2021-01/servers/444444444444444444/audit-log.json new file mode 100644 index 0000000..2ed58ec --- /dev/null +++ b/test/fixtures/discord-json-2021-01/servers/444444444444444444/audit-log.json @@ -0,0 +1,18 @@ +[ + { + "id": "111111111111111111", + "user_id": "111111111111111111", + "action_type": 11, + "changes": [ + { + "key": "xxxx", + "new_value": [ + { + "name": "xxxxxxxxxx", + "id": "111111111111111111" + } + ] + } + ] + } +] diff --git a/test/fixtures/discord-json-2021-01/servers/444444444444444444/guild.json b/test/fixtures/discord-json-2021-01/servers/444444444444444444/guild.json new file mode 100644 index 0000000..fd56ec8 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/servers/444444444444444444/guild.json @@ -0,0 +1,4 @@ +{ + "id": "444444444444444444", + "name": "xxx" +} diff --git a/test/fixtures/discord-json-2021-01/servers/index.json b/test/fixtures/discord-json-2021-01/servers/index.json new file mode 100644 index 0000000..8b6c150 --- /dev/null +++ b/test/fixtures/discord-json-2021-01/servers/index.json @@ -0,0 +1,3 @@ +{ + "444444444444444444": "xxx" +} \ No newline at end of file diff --git a/test/fixtures/facebook-json.md b/test/fixtures/facebook-json.md new file mode 100644 index 0000000..3acaf76 --- /dev/null +++ b/test/fixtures/facebook-json.md @@ -0,0 +1,9 @@ +# facebook-json exports + +## `facebook-json-2021-05-01` + * Manual edits of images -> placeholders, folder names, key names (in support cases specficially) + * This was one of the first few datasets I scrubbed so a lot of manual work was done. Should be easier now + * I went poking around this one and there was no exif on any of the images I looked at, only in the json was there exif +## `facebook-json-2025-11-29` + * Manual edits of images -> placeholders, folder names, key names + * This was one of the first few datasets I scrubbed so a lot of manual work was done. Should be easier now \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02.md b/test/fixtures/fitbit-2026-02.md new file mode 100644 index 0000000..2d282bc --- /dev/null +++ b/test/fixtures/fitbit-2026-02.md @@ -0,0 +1,31 @@ +# fitbit-2026-02 + +## Manual edits / notes +* Some many of these files are `category-2020-04-13.json` or `.csv` and then there's like 100 files. I had to manually delete all the extras and just keep one or two around. Im not keeping 2 for all of them because it's too much manual editing right now + +* `Social` + * `badge.json` kept some of the type names +* `Sleep` + * `sleep_score.csv` Manually kept magnitude of last index as scrubber was not good at preserving that + * `sleep-xxx.json` Pretty sure there are multiple shapes on this based on the type. In the UI it presents them differently (one showing just asleep/not asleep, the other showing like rem and such) + * **TODO** - I only have one of the types in the export data in the fixture im pretty sure. Need to add the other one +* `Physical Activity` + * `time_in_heart_rate_zones-xxx.json` WHY DOES THIS USE MM/DD/YY DATES, ughhhhh + * `swim_lengths_data-xxx.json` I don't really swim so I dont know why there's so much data in here + * `sedentary_minutes-xxx.json` Hmm, this one has a lot of 1440, even though I did not wear my fitbit for a lot of those days or it failed to sync, so probably just defaults... I see that in a lot of this data + * `resting_heart_rate-xxx.json` Yeah... This one has a bunch of null objects in it. So if you want to parse any of this data, you're going to have to filter out all the days you weren't wearing your device manually + * I also added an extra entry to this file for the nulls to show up in the export with + * `exercise-100.json` These are weird, seems to be a suffix of like `-\d+`. Not really any specific pattern either, I only see 0 and 100 in here + * `distance-xxx.json` This one seems to be minute-by-minute, but only for some minutes. It's kinda weird. Idk if these are supposed to be like number since last message, or number since last minute (but the last minute was missed), or what... + * `calories-xxx.json` Why does the default value here seem to be 0.95 for everything, ugh + * `Active Zone Minutes - xxx.csv` Added stuff the types back to this + * UGH this uses `2020-04-13T10:10` for the times, wtf why +* `Personal & Account` + * `weight-xxx.json` this uses `MM/DD/YY` and `HH:MM:ss` seperately. Manually had to fix +* `Heart` + * `afib_ppg_enrollment.csv` Another off the wall date format `Fri Dec 19 06:32:30 UTC 2025`. Going to manually edit this +* `Biometrics` + * `Glucose xxx.csv` no data in all of these. they're not even proper csvs when there's no data... ugh. The only info is that there's year and month inside the filename +* `Google Data` + * There's so much overlap here with the other stuff, but some of it looks better handled, others don't + * `Physical Activity/daily_heart_rate_zones.csv` - Fuck this file, JSON embedded in CSV why, they did not need to do this... \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Access_Events_1.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Access_Events_1.csv new file mode 100644 index 0000000..bb49f1e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Access_Events_1.csv @@ -0,0 +1,10 @@ +timestamp,event_name,email,location,ip,outcome,reason,application,device_info +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxxxxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Management_Events_1.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Management_Events_1.csv new file mode 100644 index 0000000..af34107 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Account_Management_Events_1.csv @@ -0,0 +1,4 @@ +timestamp,event_name,email,location,ip,outcome,reason +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Application/Coach README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Coach README.txt new file mode 100644 index 0000000..d6078d6 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Application/Coach README.txt @@ -0,0 +1,42 @@ +Coach Data Export + +The Coach category of your data export includes the pieces of content that you favorited in the Coach view, as well as +content recommendations that were generated for you based on watch history, wellbeing and physical activity. + +Files included: +---------- + +Coach Favorites.csv + +This includes the items that you marked as favorite in the Coach view. + timestamp - Datetime of the moment the item was favorited + id - Unique identifier of the item + title - Name of the item + bundle_id - Category of content in the Coach view that the item belongs to + content_type - Content type of the item + +---------- + +Coach Content Recommendations.csv + +This is the list of videos/audios that were recently recommended to you based on your and other users' viewing history. + date - Date when the recommendation was generated + id - Unique identifier of the item + title - Name of the item + bundle_id - Category of content in the Coach view that the item belongs to + content_type - Content type of the item + rating - Rating representing how good the recommendation was computed to be + +---------- + +Coach Dynamic Recommendations.csv + +This file contains the list of content rows that were personalized for you and that were embedded in pages in the app +other than the main Coach view. + timestamp - Datetime of when the content row was determined + component_id - Unique identifier of the content row + bundle_id - Category of content in the Coach view that the item belongs to + id - Unique identifier of the item + title - Name of the item + content_type - Content type of the item + associated_tags - Tags describing the items in the content row and which can be filtered upon diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Email_Audit_Entry.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Email_Audit_Entry.csv new file mode 100644 index 0000000..0ac579c --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Email_Audit_Entry.csv @@ -0,0 +1,2 @@ +previous_email,change_time,request_id +not_a_real_email@example.com,2020-04-13T10:09:08.000000Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Retired_Password.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Retired_Password.csv new file mode 100644 index 0000000..8bcaf99 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Application/User_Retired_Password.csv @@ -0,0 +1,2 @@ +date_changed,reason +2020-04-13T10:09:08.000000Z,some/path diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Biometrics Readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Biometrics Readme.txt new file mode 100644 index 0000000..1359053 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Biometrics Readme.txt @@ -0,0 +1,39 @@ +Biometrics Data Export + +Description: For users who have access and started to use biometrics features (such as Blood Glucose) in the Fitbit app this category of the data export includes all of the content added via those features. This includes your biometrics and other associated data, including annotations, time and type of measurement, your personal ranges and reminders. + +Files Included: +---------- + +Glucose Reminders.csv + +The list of reminders that you created in the Fitbit app. + + time - Time + days - Days of week + enabled - Whether this remainder enabled + +---------- + +Glucose Target Ranges.csv + +Your blood glucose personal target range. + + min - Target range (min) + max - Target range (max) + +---------- + +Glucose YYYYMM.csv + +Each file holds the list of blood glucose values and associated data for the specific month (defined by YYYY-MM). + + time - Entry date and time + value - Value + unit - Unit (MMOL_L / MG_DL) + data_source - Description of data source (UNKNOWN / MANUAL / APP) + measurement_type - Entry type (UNSPECIFIED / SMBG / CGM / LAB_TEST) + medical_codes - List of medical codes (if available) (LOINC and/or SNOMED) + tags - List of associated annotations + + diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Glucose 202004.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Glucose 202004.csv new file mode 100644 index 0000000..9b6da8c --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Biometrics/Glucose 202004.csv @@ -0,0 +1 @@ +no data \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Commerce/README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Commerce/README.txt new file mode 100644 index 0000000..c187570 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Commerce/README.txt @@ -0,0 +1 @@ +To access your order history and related information for orders placed between 2008 and 2024, please contact Google customer support at https://support.google.com/fitbit/gethelp. \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad README.txt new file mode 100644 index 0000000..0a33984 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad README.txt @@ -0,0 +1,15 @@ +Calibration Status for Readiness and Load + +The CalibrationStatusForReadinessAndLoad file contains the calibration status for the Readiness and Load features. +When the remaining number of days is 0, the user is considered calibrated. + +---------- + +CalibrationStatusForReadinessAndLoad.csv + + user_id - unique id for the user + feature - name of the feature + remaining_days - the remaining number of days required to complete the calibration + calibration_start_date - the date when calibration was started, local date + latest_completion_date - the date when calibration was completed, local date + latest_update_date - the date when the calibration status was last updated, local date \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad.csv new file mode 100644 index 0000000..ab6a223 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/CalibrationStatusForReadinessAndLoad.csv @@ -0,0 +1,3 @@ +feature,remaining_days,calibration_start_date,latest_completion_date,latest_update_date +xxxxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 +xxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory README.txt new file mode 100644 index 0000000..8af27cd --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory README.txt @@ -0,0 +1,14 @@ +Goal Settings History + +The GoalSettingsHistory file contains the settings history for the user goals. + +Files Included: +---------- + +GoalSettingsHistory.csv + + name - name of the goal + objectives - objectives of the goal containing the target value and the metric to measure + schedule - schedule of the goal, weekly or fixed datetime range + status - status of the goal, enabled or disabled + update_time - time when the goal was updated with these settings diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory.csv new file mode 100644 index 0000000..1ec3636 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/GoalSettingsHistory.csv @@ -0,0 +1,21 @@ +name,objectives,schedule,status,update_time,meta,title,subtitle,rationale,domain,progress_start_time,progress_end_time,progress +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState README.txt new file mode 100644 index 0000000..e756953 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState README.txt @@ -0,0 +1,65 @@ +Irregular Rhythm Notifications + +The below section is dedicated to the exported data of the Irregular Rhythm +Notifications (IRN) data domain. The IRN feature analyzes your heart rhythm for +signs of atrial fibrillation (AFib). + +Files Included: +---------- + +IrnUserState.csv + +The data for the user's state with respect to the Irregular Rhythm Notifications feature. + + EnrollmentState - The user's enrollment status in the IRN feature + (e.g., ENROLLED). + LastProcessedTime - The timestamp of the last time the user's heart + rhythm data was processed. + LastConclusiveWindow - The timestamp of the end of the last window of + data that was considered conclusive (either positive or negative for AFib). + LastProcessedTimestamps - A JSON array detailing the last processed + timestamp for each data source (e.g., each device). + LastNotifiedTime - The timestamp of the last time a notification was + sent to the user. + + +IrnAfibAlertWindows.csv + +The data for individual AFib analysis windows. An alert is generated from one or more of these windows. + + DeviceId - The identifier of the device that recorded the + data. + DeviceFitbitDeviceType - The model of the Fitbit device (e.g., ANTARES). + AlgorithmVersion - The version of the AFib detection algorithm used. + ServiceVersion - The version of the backend service that processed + the data. + StartTime - The start time of the analysis window. + Positive - A boolean indicating if the window was positive + for signs of AFib. + HeartBeats - A JSON array of heartbeats recorded during the + window, including the timestamp and beats per + minute for each. + + +IrnAfibAlerts.csv + +The data for AFib alerts sent to the user. + + DeviceId - The identifier of the device that recorded the + data. + DeviceFitbitDeviceType - The model of the Fitbit device. + AlgorithmVersion - The version of the AFib detection algorithm used. + ServiceVersion - The version of the backend service that processed + the data. + StartTime - The start time of the first analysis window in + the alert. + EndTime - The end time of the last analysis window in the + alert. + DetectedTime - The timestamp when the alert was officially + generated. + AlertWindows - A JSON array of the individual analysis windows + that constitute the alert. Each window includes + its start and end times, a positive flag, and the + associated heartbeats. + IsRead - A boolean indicating if the user has viewed the + notification. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState.csv new file mode 100644 index 0000000..24f272f --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/TakeoutIrnUserState.csv @@ -0,0 +1,2 @@ +enrollment_state,last_processed_time,last_conclusive_window,last_processed_timestamps,last_notified_time +xxxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData README.txt new file mode 100644 index 0000000..ded0245 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData README.txt @@ -0,0 +1,20 @@ +UserAppSettingData Export + +The UserAppSetting data file contains user prerferences such as measurment units, height and weight system etc. + +Files included: +---------- + +UserAppSettingData.csv + +The data for User AppSettings + + preferred_workout_intensity_level - the preferred workout intensity level of the user + height_system - the height system of the user + weight_system - the weight system of the user + water_measurement_unit - the water measurement unit of the user + glucose_measurement_unit - the glucose measurement unit of the user + body_temperature_measurement_unit - the body temperature measurement unit of the user + pool_length - the pool length of the user (e.g. 16 units) + pool_length_measurement_unit - the pool length measurement unit of the user + swim_unit - the swim unit of the user diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData.csv new file mode 100644 index 0000000..72f34b5 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserAppSettingData.csv @@ -0,0 +1,8 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx +2020-04-13 10:09:08+0000,xxxxxxxxxxx,11 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData README.txt new file mode 100644 index 0000000..7c91eb1 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData README.txt @@ -0,0 +1,19 @@ +UserDemographicData Export + +The User Demographic data file contains information commonly used for creating statistics / automatic update emails, including country, state, is_child etc. + +Files included: +---------- + +UserDemographicData.csv + +The data for User Demographic: + + country - the country of the user + state - the state of the user + sex - the gender of the user + timezone - the timezone of the user + locale - the locale of the user + is_child - whether the user is a child + +Note that it is expected for migrated Google accounts to contain entries with default Fitbit values for date_of_birth ("1970-01-01"). \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData.csv new file mode 100644 index 0000000..b30edf0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserDemographicData.csv @@ -0,0 +1,4 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxx,xxxxxx +2020-04-13 10:09:08+0000,xxxxxxxx,some/path diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises README.txt new file mode 100644 index 0000000..6a51427 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises README.txt @@ -0,0 +1,85 @@ +Exercises + +The below section is dedicated to the exported data of the Exercise data domain. +Exercises are sent by the wearables device to the Backend and contain data about +the exercises performed by the user. + +Files Included: +---------- + +UserExercises.csv + +The data for exercises + exercise_id - the unique identifier of the exercise + exercise_start - the exercise start time at UTC + exercise_end - the exercise end time at UTC + utc_offset - the timezone offset relative to UTC for the exercise + exercise_created - the time when the exercise was created at UTC + exercise_last_updated - the time when the exercise was last updated at UTC + activity_name - the type of activity performed during the exercise + log_type - where the exercise was logged from (mobile, tracker, etc.) + + pool_length - user's preferred pool length in the unit specified by PoolLengthUnit + pool_length_unit - pool length unit + + intervals data about the intervals of the exercise (if the exercise was an interval workout). + it is listed as blocks of the following - + - type: the type of the interval (REST or MOVE) + - interval_num: the interval number + - total_intervals: the total number of intervals in the workout + - num_repeats: the number of times the interval was repeated + - duration_millis: the interval duration in milliseconds + + distance_units - the units of the distance (imperial or metric) + tracker_total_calories - the total calories burned during the exercise (registered by the tracker) + tracker_total_steps - the total steps taken during the exercise (registered by the tracker) + tracker_total_distance_mm - the total distance in millimeters covered during the exercise (registered by the tracker) + tracker_total_altitude_mm - the total altitude in millimeters covered during the exercise (registered by the tracker) + tracker_avg_heart_rate - the average heart rate during the exercise (registered by the tracker) + tracker_peak_heart_rate - the peak heart rate during the exercise (registered by the tracker) + tracker_avg_pace_mm_per_second - the average pace in millimeters per second during the exercise (registered by the tracker) + tracker_avg_speed_mm_per_second - the average speed in millimeters per second during the exercise (registered by the tracker) + tracker_peak_speed_mm_per_second - the peak speed in millimeters per second during the exercise (registered by the tracker) + tracker_auto_stride_run_mm - the stride length when running in millimeters during the exercise (registered by the tracker) + tracker_auto_stride_walk_mm - the stride length when walking in millimeters during the exercise (registered by the tracker) + tracker_swim_lengths - the number of lengths swam during a swim exercise (registered by the tracker) + tracker_pool_length - the pool length in the unit specified by TrackerPoolLengthUnit (calculated by the tracker) + tracker_pool_length_unit - the pool length unit + tracker_cardio_load - the cardio load of the exercise (registered by the tracker) + + manually_logged_total_calories - total calories burned during the exercise (manually logged by the user) + manually_logged_total_steps - total steps taken during the exercise (manually logged by the user) + manually_logged_total_distance_mm - total distance in millimeters covered during the exercise (manually logged by the user) + manually_logged_pool_length - the pool length in the unit specified by ManuallyLoggedPoolLengthUnit (manually logged by the user) + manually_logged_pool_length_unit - the pool length unit + + exercise_events - data about the events that happen throughout the exercise such as start, stop, pause, split + - for SPLIT, AUTO_SPLIT and INTERVAL events, all the metrics are relative to the previous event + - for PAUSE, AUTO_PAUSE and STOP events, all the metrics are relative to the start of the exercise + it is listed as blocks of the following - + - exercise_event_id: the unique identifier of the event + - timestamp: the time when the event occurred at UTC + - type: the type of the event (START, STOP, PAUSE, RESUME etc.) + - auto_cue_type: the type of the auto cue (MANUAL, DISTANCE, TIME, CALORIES etc.) + - elapsed_time_millis: the elapsed time in milliseconds + - traveled_distance_mm: the distance traveled in millimeters + - calories_burned: the calories burned + - steps: the steps taken + - average_heart_rate: average heart rate + - elevation_gain_mm: elevation gain in millimeters + - swim_lengths: number of lengths swam + - average_speed_mm_per_sec: average speed in millimeters per second + - interval_type: the type of the interval (REST or MOVE) + + activity_type_probabilities - a list of activities that the user might have performed during the exercise, with the probability of each activity + autodetected_confirmed - whether the user confirmed the autodetected exercise + autodetected_start_timestamp - the start time of the autodetected exercise at UTC + autodetected_end_timestamp - the end time of the autodetected exercise at UTC + autodetected_utc_offset - the timezone offset relative to UTC for the autodetected exercise + autodetected_activity_name - the name of the autodetected activity + autodetected_sensor_based_activity_name - the name of the sensor based autodetected activity + deletion_reason - the reason why the exercise was deleted + activity_label - the label of the activity + suggested_start_timestamp - the suggested start time of the exercise at UTC + suggested_end_timestamp - the suggested end time of the exercise at UTC + reconciliation_status - the status of the reconciliation diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises_2020-04-13 b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises_2020-04-13 new file mode 100644 index 0000000..0bc11e0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserExercises_2020-04-13 @@ -0,0 +1,21 @@ +exercise_id,exercise_start,exercise_end,utc_offset,exercise_created,exercise_last_updated,activity_name,log_type,pool_length,pool_length_unit,intervals,distance_units,tracker_total_calories,tracker_total_steps,tracker_total_distance_mm,tracker_total_altitude_mm,tracker_avg_heart_rate,tracker_peak_heart_rate,tracker_avg_pace_mm_per_second,tracker_avg_speed_mm_per_second,tracker_peak_speed_mm_per_second,tracker_auto_stride_run_mm,tracker_auto_stride_walk_mm,tracker_swim_lengths,tracker_pool_length,tracker_pool_length_unit,tracker_cardio_load,manually_logged_total_calories,manually_logged_total_steps,manually_logged_total_distance_mm,manually_logged_pool_length,manually_logged_pool_length_unit,events,activity_type_probabilities,autodetected_confirmed,autodetected_start_timestamp,autodetected_end_timestamp,autodetected_utc_offset,autodetected_activity_name,autodetected_sensor_based_activity_name,deletion_reason,activity_label,suggested_start_timestamp,suggested_end_timestamp,reconciliation_status +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData README.txt new file mode 100644 index 0000000..7afcfd3 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData README.txt @@ -0,0 +1,22 @@ +UserLegacySettingData Export + +The User Legacy Setting Data file contains legacy settings for the user, including + +Files included: +---------- + +UserLegacySettingData.csv + +The data for User Legacy Setting Data: + + clock12 - whether the user has opted for a 12-hour clock display (e.g., AM/PM) instead of a 24-hour clock. + start_day_of_week - the user's start day of week (e.g., Monday, Sunday). This affects how weekly data is displayed in the app. + food_budget - whether the user has enabled a food budget feature, and the intensity level selected for it (e.g. maintenance, strict). + food_plan_estimation_enabled - whether the user has enabled food plan estimation feature. + legal_terms - the version of the legal terms the user has accepted. + sdk_developer_enabled - whether the user has enabled the SDK developer mode. + sdk_legal_terms_version - the version of the SDK legal terms the user has accepted. + weight_objective - the user's weight objective (e.g., lose, maintain, gain). + weight_track_start_date - the date the user started tracking their weight. + weight_goal_target_date - the user's weight goal target date. + food_database - the user's food database. \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData.csv new file mode 100644 index 0000000..e61031f --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserLegacySettingData.csv @@ -0,0 +1,5 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxx,some/path +2020-04-13 10:09:08+0000,xxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData README.txt new file mode 100644 index 0000000..0526ea2 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData README.txt @@ -0,0 +1,27 @@ +UserMBDData Export + +The User MBD data file contains measured body data including sensitive fields such as is_nursing, pregnant_state, body_constitution etc. + +Files included: +---------- + +UserMBDData.csv + +The data for User MBD: + + is_nursing - whether the user is nursing + pregnant_state - the pregnancy state of the user (not_pregnant, first_trimester etc.) + body_constitution - the body constitution of the user (unspecified,regular, lean) + hr_scaling_sleep_rest - the user's HR scaling sleep rest + stride_length_walking - the user's stride length walking (mm) + stride_length_running - the user's stride length running (mm) + auto_stride_length_walking - the user's auto stride length walking (mm) + auto_stride_length_running - the user's auto stride length running (mm) + auto_stride_enabled - whether the user's auto stride is enabled + auto_run_enabled - whether the user's auto run is enabled + activity_state - the user's activity state (sedentary, low_active, active etc.) + inactivity_alerts_days - the user's inactivity alerts days + sedentary_alert_times - the user's sedentary alert times + sedentary_prune_time - the user's sedentary prune time (UTC, no timezone offset) + stia_update_time - the user's STIA update time (UTC, no timezone offset) + sleep_proc_algorithm - the user's sleep process algorithm (unspecified, composite, sensitive) diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData.csv new file mode 100644 index 0000000..812762e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserMBDData.csv @@ -0,0 +1,12 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1111 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,2020-04-13T10:09:08.000000Z +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData README.txt new file mode 100644 index 0000000..490666b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData README.txt @@ -0,0 +1,15 @@ +UserProfileData Export + +The User Profile data file contains identifying information for a user's Fitbit account, including biography, username, first name etc. + +Files included: +---------- + +UserProfileData.csv + +The data for User Profile: + + about_me - user biography, free text + display_name_preference - preference for display name (name, username, or full name) + +Note that it is expected for migrated Google accounts to contain entries with default Fitbit values for first_name ("firstName") and last_name ("lastName"). \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData.csv new file mode 100644 index 0000000..626c2b8 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserProfileData.csv @@ -0,0 +1,2 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxx,xxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores README.txt new file mode 100644 index 0000000..a0999cb --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores README.txt @@ -0,0 +1,39 @@ +Sleep Scores + +The below section is dedicated to the exported data of the Sleep Score data domain. +Sleep scores merge multiple sleep metrics (duration, composition, heart rate data) +into a summarized scoring system. + +Files Included: +---------- +UserSleepScores.csv + +The data for sleep scores + user_id - the unique identifier of the user + sleep_id - the unique identifier of the sleep session + sleep_score_id - the unique identifier for the sleep score record + + data_source - the method used to record this stage data (MANUAL, DERIVED, ACTIVELY_MEASURED, PASSIVELY_MEASURED) + + score_utc_offset - timezone offset relative to UTC when the score was generated + score_time - the timestamp (UTC) when the score was generated + + overall_score - the calculated overall sleep score + + duration_score - sub-score reflecting duration alignment with sleep goals + composition_score - sub-score reflecting the ratio/balance of different sleep stages + revitalization_score - sub-score reflecting how restorative the sleep was (based on e.g. restlessness, HR, etc.) + + sleep_time_minutes - total time spent asleep in minutes + deep_sleep_minutes - number of minutes spent in deep sleep + rem_sleep_percent - percentage of total sleep time in the REM stage + resting_heart_rate - measured resting heart rate during sleep + sleep_goal_minutes - the user's sleep goal, in minutes + + waso_count_long_wakes - count of longer awakenings + waso_count_all_wake_time - total wake time after initially falling asleep + restlessness_normalized - a normalized measure of restlessness + hr_below_resting_hr - fraction of HR measurements that were below the previous day's resting HR + + sleep_score_created - the creation timestamp of the sleep score record in UTC + sleep_score_last_updated - the last update timestamp of the sleep score record in UTC diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores_2020-04-13 b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores_2020-04-13 new file mode 100644 index 0000000..8a7935a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepScores_2020-04-13 @@ -0,0 +1,21 @@ +sleep_id,sleep_score_id,data_source,score_utc_offset,score_time,overall_score,duration_score,composition_score,revitalization_score,sleep_time_minutes,deep_sleep_minutes,rem_sleep_percent,resting_heart_rate,sleep_goal_minutes,waso_count_long_wakes,waso_count_all_wake_time,restlessness_normalized,hr_below_resting_hr,sleep_score_created,sleep_score_last_updated +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.11111111111111,-1,-1,-1,111,11,11.11111111111111,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,1.1111111111111111,11,111,11.1,11,1.111111111111111111,1.111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,11111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.11111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,111,11.11,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.1111111111111111,11,1.1111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,11.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,11.1,11,1.111111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.1111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages README.txt new file mode 100644 index 0000000..330e36f --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages README.txt @@ -0,0 +1,26 @@ +Sleep Stages + +The below section is dedicated to the exported data of the Sleep Stage data domain. +Sleep stage data gives more detailed insight into how a user's sleep is distributed +across different stages. + +Files Included: +---------- +UserSleepStages.csv + +The data for sleep stages + user_id - the unique identifier of the user + sleep_id - the unique identifier of the sleep session + sleep_stage_id - the unique identifier of the sleep stage entry + sleep_stage_type - the type of sleep stage (AWAKE, LIGHT, DEEP, REM) + + start_utc_offset - timezone offset relative to UTC at the start of this sleep stage + sleep_stage_start - the start time of this sleep stage in UTC + + end_utc_offset - timezone offset relative to UTC at the end of this sleep stage + sleep_stage_end - the end time of this sleep stage in UTC + + data_source - the method used to record this stage data (MANUAL, DERIVED, ACTIVELY_MEASURED, PASSIVELY_MEASURED) + + sleep_stage_created - the creation timestamp of the sleep stage record in UTC + sleep_stage_last_updated - the last update timestamp of the sleep stage record in UTC diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages_2020-04-13 b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages_2020-04-13 new file mode 100644 index 0000000..6537327 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleepStages_2020-04-13 @@ -0,0 +1,21 @@ +sleep_id,sleep_stage_id,sleep_stage_type,start_utc_offset,sleep_stage_start,end_utc_offset,sleep_stage_end,data_source,sleep_stage_created,sleep_stage_last_updated +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps README.txt new file mode 100644 index 0000000..460afd7 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps README.txt @@ -0,0 +1,33 @@ +Sleeps + +The below section is dedicated to the exported data of the Sleep data domain. +Sleeps are sent by the wearable device (or manually entered) and contain data about +the user's sleep sessions. + +Files Included: +---------- +UserSleeps.csv + +The data for sleeps + user_id - the unique identifier of the user + sleep_id - the unique identifier of the sleep session + sleep_type - the type of the sleep session (CLASSIC, STAGES) + + minutes_in_sleep_period - the total number of minutes between going to bed and final wake-up + minutes_after_wake_up - total minutes after the user wakes up until they leave bed or stop tracking + minutes_to_fall_asleep - number of minutes it took the user to fall asleep + minutes_asleep - the total number of minutes the user was actually asleep + minutes_awake - the total number of minutes awake during the sleep session + minutes_longest_awakening - duration (in minutes) of the single longest awakening + minutes_to_persistent_sleep - number of minutes between going to bed and the onset of sustained sleep + + start_utc_offset - timezone offset relative to UTC at the start of the sleep + sleep_start - the start time of the sleep session in UTC + + end_utc_offset - timezone offset relative to UTC at the end of the sleep + sleep_end - the end time of the sleep session in UTC + + data_source - the method used to record this sleep (MANUAL, DERIVED, ACTIVELY_MEASURED, PASSIVELY_MEASURED) + + sleep_created - the creation timestamp of the sleep record in UTC + sleep_last_updated - the last update timestamp of the sleep record in UTC diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps_2020-04-13 b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps_2020-04-13 new file mode 100644 index 0000000..96ad74e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Health Fitness Data/UserSleeps_2020-04-13 @@ -0,0 +1,21 @@ +sleep_id,sleep_type,minutes_in_sleep_period,minutes_after_wake_up,minutes_to_fall_asleep,minutes_asleep,minutes_awake,minutes_longest_awakening,minutes_to_persistent_sleep,start_utc_offset,sleep_start,end_utc_offset,sleep_end,data_source,sleep_created,sleep_last_updated +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,111,1,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,11,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_2020-04-13.csv new file mode 100644 index 0000000..4087a1b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,light,moderate,very,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_readme.txt new file mode 100644 index 0000000..16b21b0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_minutes_readme.txt @@ -0,0 +1,17 @@ +Time Series Data Export + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +xxxxxxxxxxxxxxx +xxxxxxxxxx + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_2020-04-13.csv new file mode 100644 index 0000000..ab4b025 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone,total minutes,data source +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_readme.txt new file mode 100644 index 0000000..72bf21d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/active_zone_minutes_readme.txt @@ -0,0 +1,15 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +active_zone_minutes_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + heart rate zone - Heart rate zone: fat burn, cardio or peak. + total minutes - Total minutes equals to 1 for low intensity (fat burn) zones or 2 for high intensity zones (cardio, peak). + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_2020-04-13.csv new file mode 100644 index 0000000..1235454 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,level,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_readme.txt new file mode 100644 index 0000000..5f7f1d0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/activity_level_readme.txt @@ -0,0 +1,15 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +activity_level_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Activity level categorizes how active one is during a certain time interval. +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + level - Label that categorizes the activity level. Values can be SEDENTARY, LIGHTLY_ACTIVE, MODERATELY_ACTIVE, VERY_ACTIVE. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_2020-04-13.csv new file mode 100644 index 0000000..5ac3ef0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,temperature celsius,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_readme.txt new file mode 100644 index 0000000..21cb9da --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/body_temperature_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +body_temperature_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + temperature celsius - The body temperature in Celsius. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_2020-04-13.csv new file mode 100644 index 0000000..af53ec5 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,calories,data source +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_2020-04-13.csv new file mode 100644 index 0000000..e3b7f03 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,kcal,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_readme.txt new file mode 100644 index 0000000..47cc6a2 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_in_heart_rate_zone_readme.txt @@ -0,0 +1,15 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +calories_in_heart_rate_zone_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + heart rate zone type - The heart rate zone type the calories were burned in. + kcal - Amount of calories burned in kilocalories. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_readme.txt new file mode 100644 index 0000000..c3ab08d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/calories_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +calories_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + calories - Number of calories burned. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio.csv new file mode 100644 index 0000000..86d142e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio.csv @@ -0,0 +1,21 @@ +timestamp,ratio,label,data source +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio_readme.txt new file mode 100644 index 0000000..c4e4288 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_acute_chronic_workload_ratio_readme.txt @@ -0,0 +1,16 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +cardio_acute_chronic_workload_ratio.csv - Contains all data for this type. + +Cardio acute chronic workload ratio represents how recent load compares with longer term regular load. Used to determine if the user is over or under-training. +Each entry has the following values: + + timestamp - Date at which the entry was logged in UTC. + ratio - Cardio acute chronic workload ratio value. + label - Label interpreting the ratio value. Values can be UNDER_TRAINING, OPTIMAL_TRAINING or OVER_TRAINING. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_2020-04-13.csv new file mode 100644 index 0000000..6cadf11 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,workout,background,total,data source +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.111111111111111,1.111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval.csv new file mode 100644 index 0000000..7e38e65 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval.csv @@ -0,0 +1,21 @@ +timestamp,min observed load,max observed load,data source +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.11111111111111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval_readme.txt new file mode 100644 index 0000000..1fa23a7 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_observed_interval_readme.txt @@ -0,0 +1,16 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +cardio_load_observed_interval.csv - Contains all data for this type. + +Cardio load observed interval measures the personalized cardio load interval for a user. +Each entry has the following values: + + timestamp - Date at which the entry was logged in UTC. + min observed load - Average of top 3 lowest cardio load values over a period of 4 weeks. + max observed load - average of top 3 highest cardio load values over a period of 4 weeks. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_readme.txt new file mode 100644 index 0000000..5144760 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/cardio_load_readme.txt @@ -0,0 +1,17 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +cardio_load_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Cardio load also known as cardio exertion. Indicates the load on the cardiovascular system. +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + workout - Cardio load accrued during workouts. + background - Cardio load accrued outside of workouts. + total - Total cardio load, sum of workout and background cardio load. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability.csv new file mode 100644 index 0000000..1d8de24 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability.csv @@ -0,0 +1,21 @@ +timestamp,average heart rate variability milliseconds,non rem heart rate beats per minute,entropy,deep sleep root mean square of successive differences milliseconds,data source +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.11,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability_readme.txt new file mode 100644 index 0000000..fbee7fc --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_variability_readme.txt @@ -0,0 +1,17 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_heart_rate_variability.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + average heart rate variability milliseconds - The average of a user's heart rate variability during sleep. Heart rate variability is calculated as the root mean square of successive differences (RMSSD) of heartbeat intervals. + non rem heart rate beats per minute - Non-REM heart rate + entropy - The Shanon entropy of heartbeat intervals. + deep sleep root mean square of successive differences milliseconds - The root mean square of successive differences (RMSSD) of heartbeat intervals during deep sleep. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones.csv new file mode 100644 index 0000000..6b9bf17 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones.csv @@ -0,0 +1,21 @@ +timestamp,heart_rate_zone,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones_readme.txt new file mode 100644 index 0000000..59a0fcf --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_heart_rate_zones_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_heart_rate_zones.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + heart_rate_zone - Heart rate zone information. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_2020-04-13.csv new file mode 100644 index 0000000..15ed566 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_2020-04-13.csv @@ -0,0 +1,19 @@ +timestamp,average percentage,lower bound percentage,upper bound percentage,baseline percentage,standard deviation percentage,data source +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_readme.txt new file mode 100644 index 0000000..f018261 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_oxygen_saturation_readme.txt @@ -0,0 +1,18 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_oxygen_saturation_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + average percentage - Average percentage of the day's absolute spo2. + lower bound percentage - Lower bound percentage of the day's absolute spo2. + upper bound percentage - Upper bound percentage of the day's absolute spo2. + baseline percentage - Baseline percentage of the day's absolute spo2. + standard deviation percentage - Standard deviation percentage of the day's absolute spo2. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness.csv new file mode 100644 index 0000000..5613c74 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness.csv @@ -0,0 +1,19 @@ +timestamp,score,type,readiness level,sleep readiness,heart rate variability readiness,resting heart rate readiness,data source +2020-04-13,11,xxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,111,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness_readme.txt new file mode 100644 index 0000000..dbff3f0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_readiness_readme.txt @@ -0,0 +1,20 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_readiness.csv - Contains all data for this type. + +Daily Readiness represents how ready the user is for activity, based on sleep, heart rate variability and resting heart rate. +Each entry has the following values: + + timestamp - Date at which the entry was logged in UTC. + score - Overall score based on the other metrics. Values can range from 1 to 100. + type - Overall readiness rating type. Values can be LOW, MEDIUM or HIGH. + readiness level - Readiness rating level. Values can be VERY LOW, LOW, BALANCED, HIGH, PEAK. + sleep readiness - Readiness rating based on sleep. Values can be VERY LOW, LOW, MEDIUM, HIGH, VERY HIGH, IGNORED or NOT AVAILABLE. + heart rate variability readiness - Readiness rating based on heart rate variability. Values can be VERY LOW, LOW, MEDIUM, HIGH, VERY HIGH. + resting heart rate readiness - Readiness rating based on resting heart rate. Values can be VERY LOW, LOW, MEDIUM, HIGH, VERY HIGH or IGNORED. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate.csv new file mode 100644 index 0000000..18459a7 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate.csv @@ -0,0 +1,21 @@ +timestamp,breaths per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate_readme.txt new file mode 100644 index 0000000..3239c5a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_respiratory_rate_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_respiratory_rate.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + breaths per minute - The number of breaths per minute. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate.csv new file mode 100644 index 0000000..6308621 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate_readme.txt new file mode 100644 index 0000000..dbe1c04 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/daily_resting_heart_rate_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +daily_resting_heart_rate.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + beats per minute - The resting heart rate for the day. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max.csv new file mode 100644 index 0000000..e94ce5c --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max.csv @@ -0,0 +1,21 @@ +timestamp,demographic vo2max,data source +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max_readme.txt new file mode 100644 index 0000000..948ad82 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/demographic_vo2max_readme.txt @@ -0,0 +1,15 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +demographic_vo2max.csv - Contains all data for this type. + +Demographic VO2Max contains information about a user's VO2 max score for a specific day, representing the maximum rate of oxygen the body is able to use during exercises. +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + demographic vo2max - VO2 Max value in ml/kg/min + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_2020-04-13.csv new file mode 100644 index 0000000..411ae14 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,distance,data source +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_readme.txt new file mode 100644 index 0000000..27603f9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/distance_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +distance_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + distance - Distance covered in meters. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_2020-04-13.csv new file mode 100644 index 0000000..ed5bfe9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_readme.txt new file mode 100644 index 0000000..b279c06 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +heart_rate_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + beats per minute - Number of heart's beats per minute. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_2020-04-13.csv new file mode 100644 index 0000000..5c11870 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,root mean square of successive differences milliseconds,standard deviation milliseconds,data source +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_readme.txt new file mode 100644 index 0000000..c07bd21 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/heart_rate_variability_readme.txt @@ -0,0 +1,15 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +heart_rate_variability_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + root mean square of successive differences milliseconds - The root mean square of successive differences between normal heartbeats. + standard deviation milliseconds - The standard deviation of the heart rate variability measurement. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height.csv new file mode 100644 index 0000000..83c4312 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height.csv @@ -0,0 +1,9 @@ +timestamp,height millimeters,data source +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height_readme.txt new file mode 100644 index 0000000..81cfc8e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/height_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +height.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + height millimeters - Height of the user in milimeters. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_2020-04-13.csv new file mode 100644 index 0000000..24f88b0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,steps,distance millimeters,altitude gain millimeters,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_readme.txt new file mode 100644 index 0000000..6b90f91 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/live_pace_readme.txt @@ -0,0 +1,16 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +live_pace_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + steps - Number of steps taken during the activity. + distance millimeters - Distance covered during the activity in millimeters. + altitude gain millimeters - Gain in altitude during the activity in millimeters. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_2020-04-13.csv new file mode 100644 index 0000000..b4c0b2a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,oxygen saturation percentage,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_readme.txt new file mode 100644 index 0000000..3f269a5 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/oxygen_saturation_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +oxygen_saturation_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + oxygen saturation percentage - The oxygen saturation percentage. Valid values are from 0 to 100. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_2020-04-13.csv new file mode 100644 index 0000000..4df7fb0 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,deep sleep stats - milli breaths per minute,deep sleep stats - standard deviation milli breaths per minute,deep sleep stats - signal to noise,light sleep stats - milli breaths per minute,light sleep stats - standard deviation milli breaths per minute,light sleep stats - signal to noise,rem sleep stats - milli breaths per minute,rem sleep stats - standard deviation milli breaths per minute,rem sleep stats - signal to noise,full sleep stats - milli breaths per minute,full sleep stats - standard deviation milli breaths per minute,full sleep stats - signal to noise,data source +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,11.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_readme.txt new file mode 100644 index 0000000..e032f4b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/respiratory_rate_sleep_summary_readme.txt @@ -0,0 +1,25 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +respiratory_rate_sleep_summary_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + deep sleep stats - milli breaths per minute - Deep sleep milli breaths per minute. + deep sleep stats - standard deviation milli breaths per minute - Deep sleep standard deviation milli breaths per minute. + deep sleep stats - signal to noise - Deep sleep signal to noise. + light sleep stats - milli breaths per minute - Light sleep milli breaths per minute. + light sleep stats - standard deviation milli breaths per minute - Light sleep standard deviation milli breaths per minute. + light sleep stats - signal to noise - Light sleep signal to noise. + rem sleep stats - milli breaths per minute - REM sleep milli breaths per minute. + rem sleep stats - standard deviation milli breaths per minute - REM sleep standard deviation milli breaths per minute. + rem sleep stats - signal to noise - REM sleep signal to noise. + full sleep stats - milli breaths per minute - Full sleep milli breaths per minute. + full sleep stats - standard deviation milli breaths per minute - Full sleep standard deviation milli breaths per minute. + full sleep stats - signal to noise - Full sleep signal to noise. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_2020-04-13.csv new file mode 100644 index 0000000..b0f8b9b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_2020-04-13.csv @@ -0,0 +1,3 @@ +start time,end time,data source +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_readme.txt new file mode 100644 index 0000000..6ada93c --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/sedentary_period_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +sedentary_period_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + start time - Start date and time of the sedentary period. + end time - End date and time of the sedentary period. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_2020-04-13.csv new file mode 100644 index 0000000..6b681d5 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,steps,data source +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_readme.txt new file mode 100644 index 0000000..01f219e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/steps_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +steps_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + steps - Number of recorded steps. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_2020-04-13.csv new file mode 100644 index 0000000..6a6760f --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,lap time,stroke count,stroke type,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_readme.txt new file mode 100644 index 0000000..58eb63b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/swim_lengths_data_readme.txt @@ -0,0 +1,16 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +swim_lengths_data_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + lap time - Time to complete a lap. + stroke count - Number of strokes. + stroke type - Stroke type. Values can be FREESTYLE, BACKSTROKE, BREASTSTROKE or BUTTERFLY. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_2020-04-13.csv new file mode 100644 index 0000000..d535eab --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,data source +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_readme.txt new file mode 100644 index 0000000..f5d0f0d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/time_in_heart_rate_zone_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +time_in_heart_rate_zone_YYYY-MM-DD.csv - Where YYYY-MM-DD is the starting date for the entries in the file. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + heart rate zone type - The heart rate zone in which the user spent time. The possible values are: LIGHT, MODERATE, VIGOROUS, PEAK. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight.csv new file mode 100644 index 0000000..538b3f9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight.csv @@ -0,0 +1,2 @@ +timestamp,weight grams,data source +2020-04-13T10:09:08Z,11111,xxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight_readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight_readme.txt new file mode 100644 index 0000000..ec2eaab --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Google Data/Physical Activity/weight_readme.txt @@ -0,0 +1,14 @@ +Time Series Data Export + +The Time Series export provides a detailed timeline of your tracked activity. + +Files Included: +---------- + +weight.csv - Contains all data for this type. + +Each entry has the following values: + + timestamp - Date and time at which the entry was logged. + weight grams - The weight in grams. + data source - The origin or source of this data. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Alerts.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Alerts.csv new file mode 100644 index 0000000..196ed56 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Alerts.csv @@ -0,0 +1 @@ +id,start_timestamp,end_timestamp,type,threshold,value \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Profile.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Profile.csv new file mode 100644 index 0000000..070b26e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications Profile.csv @@ -0,0 +1 @@ +threshold_high_custom,threshold_low_custom,use_custom_threshold_high,use_custom_threshold_low,alert_high_on,alert_low_on \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications README.txt new file mode 100644 index 0000000..fbe39ec --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/Heart Rate Notifications README.txt @@ -0,0 +1,30 @@ +Heart Rate Notifications Data Export + +The Heart Rate Notifications (HRNs) category of your export includes all the data related to your HRNs, including your profile settings and alert history. Some Fitbit devices such as Fitbit Charge 5, Fitbit Sense, and Fitbit Versa 3 notify you when we detect that your heart rate is outside your high or low thresholds while you appear to be inactive for at least 10 minutes. + +Files Included: +---------- + +Heart Rate Notifications Profile.csv + +This is the data related to a user's Heart Rate Notifications settings. + +threshold_high_custom - Custom upper threshold for HRN set by user +threshold_low_custom - Custom lower threshold for HRN set by user +use_custom_threshold_high - Use the custom high threshold +use_custom_threshold_low - Use the custom low threshold +alert_high_on - Are alerts on for heart rate exceeding high threshold +alert_low_on - Are alerts on for heart rate subceeding the lower threshold + +---------- + +Heart Rate Notifications Alerts.csv + +This is the data that was collected for each Heart Rate Notification alert. + +id - Unique numeric id for the alert +start_timestamp - Timestamp for the start of the HRN +end_timestamp - Timestamp for the end of the HRN +type - HIGH or LOW indicating whether HRN was triggered by crossing the high or low threshold +threshold - Threshold value crossed +value - Heart rate that triggered the notification \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_README.txt new file mode 100644 index 0000000..826fc70 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_README.txt @@ -0,0 +1,41 @@ +Afib Ppg Data Export + +The Afib PPG category of your data export includes all of the content you have added to Afib PPG in the app. This includes alerts you received. + +Files Included: +---------- + +afib_ppg_enrollment.csv + +This is the data related to Afib PPG enrollment status. + + consented - If user has consented to Afib PPG program agreement + onboarded - If user has finished the onboard process for the Afib PPG program + enrolled - If user has completed enrollment into the Afib PPG program + last_updated - What was the last time when the user's status was updated + last_notified - What was the last time when the user was notified about a positive observation + +---------- + +afib_ppg_alerts.csv + +This is the data that represents all the received alerts. + + alert_time - Current alert time + alert_detected_time - Time of Afib PPG positive detection + alert_wire_id - The wire ID of the device on which the data was taken + alert_is_read - Was this Afib PPG alert read already or not + window_bpm_time - Afib PPG analysed window bpm time + window_bpm_value - Afib PPG analysed window bpm value + +---------- + +afib_ppg_windows.csv + +This is the data that represents all the analyzed Afib PPG windows. + + start_time - The analysed window start time + is_positive - Does the analysed window have positive detections or not + wire_id - The wire ID of the device on which the data was taken + bpm_time - Afib PPG analysed window bpm time + bpm_value - Afib PPG analysed window bpm value diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_enrollment.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_enrollment.csv new file mode 100644 index 0000000..3df8f22 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Heart/afib_ppg_enrollment.csv @@ -0,0 +1,2 @@ +consented,onboarded,enrolled,last_updated,last_notified +false,false,false,Fri Apr 13 10:09:08 UTC 2020,null \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/Menstrual Health README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/Menstrual Health README.txt new file mode 100644 index 0000000..ca6af53 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/Menstrual Health README.txt @@ -0,0 +1,59 @@ +Menstrual Health Data Export + +The Menstrual Health category of your data export includes all of the content you have added to the Fitbit menstrual health tracker. This includes your settings, symptoms, menstrual cycles and birth control. + +Files Included: +---------- + +menstrual_health_birth_control.csv + +This is the birth control data that you enter into the app. + + birth_control_type - The type of birth control you use + event_date - Date at which you started birth control + has_started - Identifies if it has started or not + +---------- + +menstrual_health_settings.csv + +These are the settings that you enter while using menstrual health tracking + + pregnancy_history - Your pregnancy history + birth_control_history - Your birth control history + avg_period_days - Average number of days your period lasts (entered by user) + avg_cycle_days - Average number of days your cycle lasts (entered by user) + +---------- + +menstrual_health_symptoms.csv + +These are symptoms that you can log on a daily basis. + + timestamp - The date at which you recorded your symptom + fluids - The type of fluid you logged for the date + flow - The type of flow you experienced and you logged + conditions - Logged conditions like acne, headache, etc (multivalued) + sex - Protected/unprotected sex + ovulation_test - Result of an OPK test that you may have logged + cycle_altering_event - Cycle altering event such as morning after pill or pregnancy + mood - Moods you experienced on that day. (multivalued) + +----------- + +menstrual_health_cycles.csv + +All your data about menstrual cycles. + + id - Cycle id + cycle_start_date - Cycle start date + cycle_end_date - Cycle end date + ovulation_start_date - Start date for ovulation phase for this cycle + ovulation_end_date - End date for ovulation phase for this cycle + ovulation_source - Computed (internally computed prediction) / Manual (entered by you) + period_start_date - Start date of your period for this cycle + period_end_date - End date of your period for this cycle + period_source - Computed (internally computed prediction) / Manual (entered by you) + fertile_start_date - Start date of your fertile window for this cycle + fertile_end_date - End date of your fertile window for this cycle + fertile_source - Computed (internally computed prediction) / Manual(entered by you) diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_birth_control.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_birth_control.csv new file mode 100644 index 0000000..06ada91 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_birth_control.csv @@ -0,0 +1 @@ +birth_control_type,event_date,has_started diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_cycles.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_cycles.csv new file mode 100644 index 0000000..75655a4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_cycles.csv @@ -0,0 +1 @@ +id,cycle_start_date,cycle_end_date,ovulation_start_date,ovulation_end_date,ovulation_source,period_start_date,period_end_date,period_source,fertile_start_date,fertile_end_date,fertile_source diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_settings.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_settings.csv new file mode 100644 index 0000000..4067b1e --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_settings.csv @@ -0,0 +1 @@ +pregnancy_history,birth_control_history,avg_period_days,avg_cycle_days diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_symptoms.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_symptoms.csv new file mode 100644 index 0000000..d084e09 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Menstrual Health/menstrual_health_symptoms.csv @@ -0,0 +1 @@ +timestamp,fluids,flow,conditions,sex,ovulation_test,cycle_altering_event,mood diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Other/estimated_oxygen_variation-2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Other/estimated_oxygen_variation-2020-04-13.csv new file mode 100644 index 0000000..3370911 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Other/estimated_oxygen_variation-2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,Infrared to Red Signal Ratio +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices Readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices Readme.txt new file mode 100644 index 0000000..753f931 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices Readme.txt @@ -0,0 +1,100 @@ +Device Data Export + +The Devices category of your data export includes all of the Fitbit devices you have paired. This includes Devices.csv, Trackers.csv, Tracker Optional Configuration.csv, Scales.csv, and iOS App Notification Settings.csv + +Files Included: +---------- + +Devices.csv + +This is the list of all Devices you have paired. + + wire_id + device_type + serial_number + enabled + fw_version + +---------- + +Trackers.csv + +This is the configuration of all Trackers you have paired, which are the data tightly coupled with the physical device. + + tracker_id + date_added + last_sync_date_time + batt_level + hardware_rev + is_display_distance + is_display_calories + is_display_clock + is_display_flower + is_display_elevation + is_display_chatter + is_right_handed + tracker_name + device_type + on_dominant_hand + is_display_active_minutes + clock_face + enable_ancs + is_bonded + is_display_steps + alarm_update_time + is_display_heart_rate + heart_rate_tracking + heart_rate_tracking_update_time + tap_enabled + tap_screen + flick_enabled + flick_screen + +---------- + +Tracker Optional Configuration.csv + +This is the additional configuration of all Trackers you have paired with various number of columns, which are optional and can be different between users. + + tracker_id + enabled_notification_types + on_right_hand + clock_face + enable_inactivity_alerts + last_updated_last_ia_time + last_reboot_time + payments_enabled + last_successful_wifi_connection_time + last_successful_wifi_connectionipv4address + last_successful_wifi_connectionipv6address + last_successful_wifi_connectionssid + live_data_disabled + +---------- + +Scales.csv + +This is the list of all Scales you have paired. + + scale_id + short_name + display_bf + display_bf_mass_unit + display_bmi + user_icon_id + + +---------- + +iOS App Notification Settings.csv + +This is the list of iOS applications that your Fitbit device can display notifications from. Notifications from apps with is_app_enabled “true” are displayed on the device, and apps with is_app_enabled “false” are not. + + user_id + tracker_id + mobile_app_name + is_app_enabled + show_partial_message + is_default_message_app + created_on + modified_on diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices.csv new file mode 100644 index 0000000..91dfbeb --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Devices.csv @@ -0,0 +1,2 @@ +wire_id,device_type,serial_number,enabled,fw_version +a1a1a1a1a1a1,xxxxxxxxx,,false,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Media/Avatar Photo.png b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Media/Avatar Photo.png new file mode 100644 index 0000000..2c3cacd Binary files /dev/null and b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Media/Avatar Photo.png differ diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Products Export Readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Products Export Readme.txt new file mode 100644 index 0000000..b6356aa --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Products Export Readme.txt @@ -0,0 +1,23 @@ +Products Data Export + +This portion of your data export includes data on your Premium subscriptions + +User Premium Subscriptions.csv: +--------- + provider_id - subscription provider, apple or google + sku - Fitbit name of premium product + product_description - premium product short description + product_reccurence - premium product recurrence + start_date - the date premium subscription started + valid_until - the date premium subscription is valid until + user_cancellation_date - the date premium subscription was cancelled by the user + trial_period - 'TRUE' means that subscription in the free trial status + fatal_renewal_error - if 'TRUE' subscription renewal will not be retried + grant_bundle - the name of features set included into the subscription + +User Premium Transactions.csv: +--------- + provider_id - subscription provider, apple or google + sku - Fitbit name of premium product + timestamp - date and time of the transaction + provider_transaction_id - transaction identifier received from Apple or Google \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Profile.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Profile.csv new file mode 100644 index 0000000..839424d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Profile.csv @@ -0,0 +1,2 @@ +id,full_name,first_name,last_name,display_name_setting,display_name,username,email_address,date_of_birth,child,country,state,city,timezone,locale,member_since,about_me,start_of_week,sleep_tracking,time_display_format,gender,height,weight,stride_length_walking,stride_length_running,weight_unit,distance_unit,height_unit,water_unit,glucose_unit,swim_unit +xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxx,xxxxxxx,xxxx,xxxxxxxxxxx,null,not_a_real_email@example.com,2020-04-13,false,null,null,null,some/path,xxxxx,2020-04-13,null,xxxxxx,xxxxxx,xxxxxx,xxxxxx,111.11111111111111,11.1,11.1,111.11111111111111,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Scales.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Scales.csv new file mode 100644 index 0000000..2ac02c7 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Scales.csv @@ -0,0 +1 @@ +scale_id,short_name,display_bf,display_bf_mass_unit,display_bmi,user_icon_id diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Tracker Optional Configuration.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Tracker Optional Configuration.csv new file mode 100644 index 0000000..f300e0a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Tracker Optional Configuration.csv @@ -0,0 +1,2 @@ +tracker_id,enabled_notification_types,on_right_hand,clock_face,enable_inactivity_alerts,last_updated_ia_time,last_reboot_time,payments_enabled,last_successful_wifi_connection_time,last_successful_wifi_connectionipv4addr,last_successful_wifi_connectionipv6addr,last_successful_wifi_connectionssid,live_data_disabled +1111111111,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,false,,false,xxxxxxxxxxxxxxxxxxxxxxxx,,false,,,,,false diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Trackers.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Trackers.csv new file mode 100644 index 0000000..f763e9d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/Trackers.csv @@ -0,0 +1,2 @@ +tracker_id,date_added,last_sync_date_time,batt_level,hardware_rev,is_display_distance,is_display_calories,is_display_clock,is_display_flower,is_display_elevation,is_display_chatter,is_right_handed,tracker_name,device_type,on_dominant_hand,is_display_active_minutes,clock_face,enable_ancs,is_bonded,is_display_steps,alarm_update_time,is_display_heart_rate,heart_rate_tracking,heart_rate_tracking_update_time,tap_enabled,tap_screen,flick_enabled,flick_screen +1111111111,2020-04-13,xxxxxxxxxxxxxxxxxxxxxxxx,1,11,false,false,false,false,false,false,false,,xxxxxxxxx,false,false,a,false,false,false,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,false, diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/User Profile README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/User Profile README.txt new file mode 100644 index 0000000..a88eb12 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/User Profile README.txt @@ -0,0 +1,49 @@ +User Profile Data Export + +This portion of your data export includes your profile information and settings. + +Files Included: +---------- + +Profile.csv + +Your profile data is represented as follows, with absent fields represented by "null": + + id - the user id associated with your account + full_name - the full name set in your profile settings + first_name - the first name set in your profile settings + last_name - the last name set in your profile settings + display_name_setting - your preferred display name (NAME, USERNAME, FULLNAME) + display_name - your display name as specified by the above setting + username - the username associated with your account + email_address - the email associated with your account + date_of_birth - the birthday set in your profile settings + child - whether your account is flagged as a child account + country - the country set in your profile settings + state - the state set in your profile settings + city - the city set in your profile settings + timezone - the timezone set in your profile settings + locale - the language preference set in your profile settings + member_since - the date (yyyy-MM-dd format) of your account creation + about_me - the 'about me' section set in your profile settings + start_of_week - the first day of the week set in your profile settings + sleep_tracking - the sleep sensitivity selection set in your profile settings + time_display_format - the clock display format in your profile settings (TWELVE_HOUR, TWENTY_FOUR_HOUR) + gender - the gender set in your profile settings + height - the height set in your profile settings, in centimeters + weight - the weight set in your profile settings, in kilograms + stride_length_walking - the walking stride length used for your account, in centimeters + stride_length_running - the running stride length used for your account, in centimeters + weight_unit - the unit system preference for weight data + distance_unit - the unit system preference for distance data + height_unit - the unit system preference for height data + water_unit - the unit system preference for water data + glucose_unit - the unit system preference for glucose data + swim_unit - the unit system preference for swim data + active - whether your account is flagged as recently active + +---------- + +Images + +Your profile avatar will be included separately in a Media subdirectory. If set, your cover photo will be included there as well. \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/height-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/height-2020-04-13.json new file mode 100644 index 0000000..f81e531 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/height-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1111" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1111" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/iOS App Notification Settings.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/iOS App Notification Settings.csv new file mode 100644 index 0000000..2d1d7cb --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/iOS App Notification Settings.csv @@ -0,0 +1 @@ +user_id,tracker_id,mobile_app_name,is_app_enabled,show_partial_message,is_default_message_app,created_on,modified_on diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/weight-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/weight-2020-04-13.json new file mode 100644 index 0000000..2e59d2d --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Personal & Account/weight-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "logId": 1111111111111, + "weight": 111.1, + "bmi": 11.11, + "date": "04/13/20", + "time": "10:09:08", + "source": "xxx" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Active Zone Minutes - 2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Active Zone Minutes - 2020-04-13.csv new file mode 100644 index 0000000..8780246 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Active Zone Minutes - 2020-04-13.csv @@ -0,0 +1,21 @@ +date_time,heart_zone_id,total_minutes +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,CARDIO,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals README.txt new file mode 100644 index 0000000..de26baa --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals README.txt @@ -0,0 +1,21 @@ +Activity Goals Data Export + +Activity Goals collects all information about the goals set by the user in the Fitbit ecosystem. + +Files Included: +----------- + +Activity Goals.csv + +This is the data related to goal values and their attributes. + + type - The goal type + frequency - The frequency set to complete the goal + target - The goal target value + result - The current progress towards the target value + status - The description of the current progress towards the target value + is_primary - The main goal of the user + start_date - The start date to achieve the goal + end_date - The end date to achieve the goal + created_on - The timestamp when the goal was created by the user + edited_on - The timestamp when the goal was edited by the user \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals.csv new file mode 100644 index 0000000..0cdcfbb --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Activity Goals.csv @@ -0,0 +1,13 @@ +type,frequency,target,result,status,is_primary,start_date,end_date,created_on,edited_on +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,11.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,111.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxx,xxxxx,1111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,1.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxx,xxxxxx,11111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,1111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness README.txt new file mode 100644 index 0000000..bc9cb2a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness README.txt @@ -0,0 +1,19 @@ +The Daily Readiness score helps active users optimize their exercise routine by telling them when they are ready to work out and when they need to prioritize recovery. + +The included files contain daily values of the score and its components. + +date: the date of the score +readiness_score_value: the final daily readiness score +readiness_state: the description of the overall readiness state +activity_subcomponent: the activity subcomponent +sleep_subcomponent the recent sleep subcomponent +hrv_subcomponent: the heart rate variability subcomponent +activity_state: the activity state description +sleep_state: the recent sleep state description +hrv_state: the heart rate variability state description + +An included file contains user properties values. + +property_type: property type from previous period (recent activity or sleep) +value: numeric value representing the property type +last_update: date when the property value was last updated diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness User Properties - 2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness User Properties - 2020-04-13.csv new file mode 100644 index 0000000..6fad122 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/Daily Readiness User Properties - 2020-04-13.csv @@ -0,0 +1 @@ +property_type,value,last_update \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/calories-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/calories-2020-04-13.json new file mode 100644 index 0000000..852c755 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/calories-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1.11" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1.11" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/demographic_vo2_max-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/demographic_vo2_max-2020-04-13.json new file mode 100644 index 0000000..eb6581b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/demographic_vo2_max-2020-04-13.json @@ -0,0 +1,20 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": { + "demographicVO2Max": 11.11111, + "demographicVO2MaxError": 1.1111111111111112, + "filteredDemographicVO2Max": 11.11111111111111, + "filteredDemographicVO2MaxError": 0.1111111111111111 + } + }, + { + "dateTime": "04/13/20 10:09:08", + "value": { + "demographicVO2Max": 11.11111111111111, + "demographicVO2MaxError": 1.1111111111111112, + "filteredDemographicVO2Max": 11.11111111111111, + "filteredDemographicVO2MaxError": 0.1111111111111111 + } + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/distance-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/distance-2020-04-13.json new file mode 100644 index 0000000..f40ace4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/distance-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/exercise-100.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/exercise-100.json new file mode 100644 index 0000000..97abe45 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/exercise-100.json @@ -0,0 +1,137 @@ +[ + { + "logId": 11111111111, + "activityName": "xxxxxxxxxxxx", + "activityTypeId": 1111, + "activityLevel": [ + { + "minutes": 1, + "name": "xxxxxxxxx" + }, + { + "minutes": 1, + "name": "xxxxxxx" + } + ], + "averageHeartRate": 111, + "calories": 111, + "duration": 1111111, + "activeDuration": 1111111, + "logType": "xxxxxxxxxxxxx", + "manualValuesSpecified": { + "calories": false, + "distance": false, + "steps": false + }, + "heartRateZones": [ + { + "name": "xxxxxxxxxxxx", + "min": 11, + "max": 111, + "minutes": 11, + "caloriesOut": 111.11111111111111 + }, + { + "name": "xxxxxxxx", + "min": 111, + "max": 111, + "minutes": 11, + "caloriesOut": 111.11111 + } + ], + "activeZoneMinutes": { + "totalMinutes": 11, + "minutesInHeartRateZones": [ + { + "minutes": 11, + "zoneName": "xxxxxx", + "order": 1, + "type": "xxxxxx", + "minuteMultiplier": 1 + }, + { + "minutes": 11, + "zoneName": "xxxxxxxx", + "order": 1, + "type": "xxxxxxxx", + "minuteMultiplier": 1 + } + ] + }, + "lastModified": "04/13/20 10:09:08", + "startTime": "04/13/20 10:09:08", + "originalStartTime": "04/13/20 10:09:08", + "originalDuration": 1111111, + "hasGps": false, + "shouldFetchDetails": false, + "hasActiveZoneMinutes": false + }, + { + "logId": 11111111111, + "activityName": "xxxx", + "activityTypeId": 11111, + "activityLevel": [ + { + "minutes": 1, + "name": "xxxxxxxxx" + }, + { + "minutes": 1, + "name": "xxxxxxx" + } + ], + "averageHeartRate": 111, + "calories": 11, + "duration": 1111111, + "activeDuration": 1111111, + "steps": 1111, + "logType": "xxxxxxxxxxxxx", + "manualValuesSpecified": { + "calories": false, + "distance": false, + "steps": false + }, + "heartRateZones": [ + { + "name": "xxxxxxxxxxxx", + "min": 11, + "max": 111, + "minutes": 11, + "caloriesOut": 11.11111111111111 + }, + { + "name": "xxxxxxxx", + "min": 111, + "max": 111, + "minutes": 1, + "caloriesOut": 1.11111 + } + ], + "activeZoneMinutes": { + "totalMinutes": 1, + "minutesInHeartRateZones": [ + { + "minutes": 1, + "zoneName": "xxxxxx", + "order": 1, + "type": "xxxxxx", + "minuteMultiplier": 1 + }, + { + "minutes": 1, + "zoneName": "xxxxxxxx", + "order": 1, + "type": "xxxxxxxx", + "minuteMultiplier": 1 + } + ] + }, + "lastModified": "04/13/20 10:09:08", + "startTime": "04/13/20 10:09:08", + "originalStartTime": "04/13/20 10:09:08", + "originalDuration": 1111111, + "hasGps": false, + "shouldFetchDetails": false, + "hasActiveZoneMinutes": false + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/heart_rate-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/heart_rate-2020-04-13.json new file mode 100644 index 0000000..b4fc48f --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/heart_rate-2020-04-13.json @@ -0,0 +1,16 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": { + "bpm": 11, + "confidence": 1 + } + }, + { + "dateTime": "04/13/20 10:09:08", + "value": { + "bpm": 11, + "confidence": 1 + } + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/lightly_active_minutes-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/lightly_active_minutes-2020-04-13.json new file mode 100644 index 0000000..f40ace4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/lightly_active_minutes-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/moderately_active_minutes-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/moderately_active_minutes-2020-04-13.json new file mode 100644 index 0000000..f40ace4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/moderately_active_minutes-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/resting_heart_rate-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/resting_heart_rate-2020-04-13.json new file mode 100644 index 0000000..e0753f3 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/resting_heart_rate-2020-04-13.json @@ -0,0 +1,26 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": { + "date": "some/path", + "value": 11.1111111111111, + "error": 1.111111111111111 + } + }, + { + "dateTime": "04/13/20 10:09:08", + "value": { + "date": "some/path", + "value": 11.11111111111111, + "error": 1.111111111111111 + } + }, + { + "dateTime": "04/13/20 00:00:00", + "value" : { + "date" : null, + "value" : 0.0, + "error" : 0.0 + } + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/sedentary_minutes-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/sedentary_minutes-2020-04-13.json new file mode 100644 index 0000000..f81e531 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/sedentary_minutes-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1111" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1111" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/steps-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/steps-2020-04-13.json new file mode 100644 index 0000000..f40ace4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/steps-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/swim_lengths_data-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/swim_lengths_data-2020-04-13.json new file mode 100644 index 0000000..27312d5 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/swim_lengths_data-2020-04-13.json @@ -0,0 +1,20 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": { + "lapDurationSec": 11, + "strokeCount": 1, + "swimStrokeType": "xxxxxxx", + "swimAlgorithmType": "xxxxxx" + } + }, + { + "dateTime": "04/13/20 10:09:08", + "value": { + "lapDurationSec": 11, + "strokeCount": 1, + "swimStrokeType": "xxxxxxx", + "swimAlgorithmType": "xxxxxx" + } + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/time_in_heart_rate_zones-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/time_in_heart_rate_zones-2020-04-13.json new file mode 100644 index 0000000..6cc5725 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/time_in_heart_rate_zones-2020-04-13.json @@ -0,0 +1,13 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": { + "valuesInZones": { + "BELOW_DEFAULT_ZONE_1": 111, + "IN_DEFAULT_ZONE_2": 1, + "IN_DEFAULT_ZONE_1": 1, + "IN_DEFAULT_ZONE_3": 1 + } + } + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/very_active_minutes-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/very_active_minutes-2020-04-13.json new file mode 100644 index 0000000..f40ace4 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Physical Activity/very_active_minutes-2020-04-13.json @@ -0,0 +1,10 @@ +[ + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + }, + { + "dateTime": "04/13/20 10:09:08", + "value": "1" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Programs/Fitbit Health Programs Data README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Programs/Fitbit Health Programs Data README.txt new file mode 100644 index 0000000..04fbb11 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Programs/Fitbit Health Programs Data README.txt @@ -0,0 +1,41 @@ +Fitbit Health Programs + +The Fitbit Care category of your data export includes all of the content related to your Fitbit Health Programs +connected to your account. +This includes your health programs data, tasks data in particular programs + +Files Included: +---------- + +{PROGRAM_ID}_{PROGRAM_CREATED_DATE}.json + +File names include the next data: + PROGRAM_ID - Program id + PROGRAM_CREATED_DATE - Date of start of user participation in the program + +File content is json that includes the next data related to the health programs: + + + summary - Summary of the health program + state - Actual state of the health program. + tasks - List of tasks that connected to particular health programs + +Where summary included the next data: + + title - Subtitle of the health program + subtitle - Subtitle of the health program + status - Actual health program status, e.g. ACTIVE, COMPLETE, etc + startedAt - Date of start of user participation in the health program + endedAt - Date of end of user participation in the health program + detail - Details of the health program + +Where each task in the tasks list include the next data: + + id - Health Task id + title - Title of the health task + subtitle - Subtitle of the health task + status - Actual health task status, e.g. ACTIVE, COMPLETED, OBSOLETE + dataJson - Specific data for the health task related to health program + dueDate - Due date for health task + +------------------ diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Heart Rate Variability Summary README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Heart Rate Variability Summary README.txt new file mode 100644 index 0000000..c1a7434 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Heart Rate Variability Summary README.txt @@ -0,0 +1,7 @@ +Heart rate variability (HRV) is the physiological phenomenon of variation in the time interval between heartbeats. It is measured by the variation in the beat-to-beat interval. + +The "Daily Heart Rate Variability Summary" files include daily granularity recordings of your HRV during a sleep. The description for the values of each row is as follows: + +rmssd: Root mean squared value of the successive differences of time interval between successive heart beats., measured during sleep. +nremhr: Heart rate measured during non-REM sleep (i.e. light and deep sleep stages). +entropy: Entropy quantifies randomness or disorder in a system. High entropy indicates high HRV. Entropy is measured from the histogram of time interval between successive heart beats values measured during sleep. diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Respiratory Rate Summary README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Respiratory Rate Summary README.txt new file mode 100644 index 0000000..392222a --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily Respiratory Rate Summary README.txt @@ -0,0 +1,5 @@ +The respiratory rate (or breathing rate) is the rate at which breathing occurs. This is usually measured in breaths per minute. + +The "Daily Respiration Rate Summary" files include daily granularity recordings of your Respiratory Rate during a sleep. The description is as follows: + +daily_respiratory_rate: Breathing rate average estimated from deep sleep when possible, and from light sleep when deep sleep data is not available. \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 - 2020-03-13-2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 - 2020-03-13-2020-04-13.csv new file mode 100644 index 0000000..2e66913 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 - 2020-03-13-2020-04-13.csv @@ -0,0 +1,10 @@ +timestamp,average_value,lower_bound,upper_bound +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,111.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 README.txt new file mode 100644 index 0000000..11b3c79 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Daily SpO2 README.txt @@ -0,0 +1,10 @@ +Blood oxygen saturation (SpO2) estimates the amount of oxygen in your blood as a percentage of the maximum it could contain. +After a period of sleep, a single value is computed from all gathered data. + +The "Daily SpO2" files contain the SpO2 values measured per day during sleep. + +Values per row: +- timestamp: The time when the sleep period has ended +- average_value: The average SpO2 value computed during sleep +- lower_bound: Lower bound of SpO2 values +- upper_bound: Upper bound of SpO2 values \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Device Temperature - 2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Device Temperature - 2020-04-13.csv new file mode 100644 index 0000000..688bed9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Device Temperature - 2020-04-13.csv @@ -0,0 +1 @@ +recorded_time,temperature,sensor_type \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Heart Rate Variability README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Heart Rate Variability README.txt new file mode 100644 index 0000000..87aea54 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Heart Rate Variability README.txt @@ -0,0 +1,14 @@ +Heart rate variability (HRV) is the physiological phenomenon of variation in the time interval between heartbeats. It is measured by the variation in the beat-to-beat interval. + +The "Heart Rate Variability Details" files include 5 minutes granularity recordings of your HRV during a sleep. The description for the values of each row is as follows: + +- timestamp: the start of the 5 minutes interval for which the following values were computed +- rmssd: "root mean square of successive differences" - the square root of the mean of the squares of the successive differences between adjacent beat-to-beat intervals +- coverage: the number of data points in the interval, multiplied by the mean beat-to-beat of the interval in seconds and divided by the number of seconds in the interval (300 seconds) +- low_frequency: measures long term variations in heart rate and reflects activity from both the sympathetic and parasympathetic branches +- high_frequency: measures short term variations in heart rate and captures parasympathetic activity + +The "Heart Rate Variability Histogram" files include histograms that shows the spread in the beat-to-beat intervals for your sleeps: + +- timestamp: the wake time +- bucket_values: 29 bins grouping and counting the similar beat-to-beat durations during your sleep. The bins interval is 0.05s, and the bins start at 0.3s. E.g. 0.3s, 0.35s, 0.4s, etc \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 - 2020-04-13.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 - 2020-04-13.csv new file mode 100644 index 0000000..872efc3 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 - 2020-04-13.csv @@ -0,0 +1,21 @@ +timestamp,value +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 README.txt new file mode 100644 index 0000000..b17e5c9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Minute SpO2 README.txt @@ -0,0 +1,12 @@ +Blood oxygen saturation (SpO2) estimates the amount of oxygen in your blood as a percentage of the maximum it could contain. +This is measured per minute. + +The "Minute SpO2" files contain the SpO2 measured per minute during sleep. + +Values per row: +- timestamp: the time when SpO2 was measured +- value: SpO2 value +- confidence: An estimation of how much confidence we have in the SpO2 measurement +- coverage: The number of seconds in the last minute that were used in the calculation +- valid: False if low confidence +- unused: whether is used or not in the summary computation \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Respiratory Rate README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Respiratory Rate README.txt new file mode 100644 index 0000000..a51cff2 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Respiratory Rate README.txt @@ -0,0 +1,17 @@ +The respiratory rate (or breathing rate) is the rate at which breathing occurs. This is usually measured in breaths per minute. + +The "Respiratory Rate Summary" files include summaries of your respiratory rate during sleeps: + +- timestamp: the wake time +- full_sleep_breathing_rate: the respiratory rate average for your entire sleep +- full_sleep_standard_deviation: measures the amount your respiratory rate variates during the entire sleep +- full_sleep_signal_to_noise: the signal-to-noise value for the entire sleep +- deep_sleep_breathing_rate: the respiratory rate average for your deep sleep periods +- deep_sleep_standard_deviation: measures the amount your respiratory rate variates during the deep sleep periods +- deep_sleep_signal_to_noise: the signal-to-noise value for the deep sleep periods +- light_sleep_breathing_rate: the respiratory rate average for your light sleep periods +- light_sleep_standard_deviation: measures the amount your respiratory rate variates during the light sleep periods +- light_sleep_signal_to_noise: the signal-to-noise value for the light sleep periods +- rem_sleep_breathing_rate: the respiratory rate average for your REM sleep periods +- rem_sleep_standard_deviation:measures the amount your respiratory rate variates during the REM sleep periods +- rem_sleep_signal_to_noise: the signal-to-noise value for the REM sleep periods \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Snore README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Snore README.txt new file mode 100644 index 0000000..50a61a9 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/Snore README.txt @@ -0,0 +1,11 @@ +Snore Details is providing the acoustic noise metrics during sleep. The acoustic noise metrics include noise level and snoring events. +The acoustic noise metrics are computed every segment (default 30 second). +The noise level is reported in A-weighting which is adjusted for loudness perceived by the human ear. The noise level is computed every 100 milliseconds. +- timestamp: the start time of this segment. +- mean_dba: average A-weighted noise level in decibels measured on the device in this segment. +- max_dba: maximum A-weighted noise level in decibels measured on the device in this segment. +- min_dba: minimum A-weighted noise level in decibels measured on the device in this segment. +- events_number: the number of sound events detected in this segment. +- snoring_events_number: the number of snoring events detected in this segment. +- snore_label: a binary label indicating whether there is enough snoring events in this segment. +- sample_duration: the duration of this segment, the default is 30 seconds. \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-03-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-03-13.json new file mode 100644 index 0000000..0731e1b --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-03-13.json @@ -0,0 +1,132 @@ +[ + { + "logId": 11111111111, + "dateOfSleep": "2020-03-13", + "startTime": "2020-03-13T10:09:08.000", + "endTime": "2020-03-13T10:09:08.000", + "duration": 11111111, + "minutesToFallAsleep": 1, + "minutesAsleep": 111, + "minutesAwake": 11, + "minutesAfterWakeup": 1, + "timeInBed": 111, + "efficiency": 11, + "type": "xxxxxx", + "infoCode": 1, + "logType": "xxxxxxxxxxxxx", + "levels": { + "summary": { + "deep": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "wake": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "light": { + "count": 11, + "minutes": 111, + "thirtyDayAvgMinutes": 111 + }, + "rem": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + } + }, + "data": [ + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + }, + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxxx", + "seconds": 111 + } + ], + "shortData": [ + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + }, + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + } + ] + }, + "mainSleep": false + }, + { + "logId": 11111111111, + "dateOfSleep": "2020-03-13", + "startTime": "2020-03-13T10:09:08.000", + "endTime": "2020-03-13T10:09:08.000", + "duration": 11111111, + "minutesToFallAsleep": 1, + "minutesAsleep": 111, + "minutesAwake": 11, + "minutesAfterWakeup": 1, + "timeInBed": 111, + "efficiency": 11, + "type": "xxxxxx", + "infoCode": 1, + "logType": "xxxxxxxxxxxxx", + "levels": { + "summary": { + "deep": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "wake": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "light": { + "count": 11, + "minutes": 111, + "thirtyDayAvgMinutes": 111 + }, + "rem": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + } + }, + "data": [ + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + }, + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxxx", + "seconds": 111 + } + ], + "shortData": [ + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + }, + { + "dateTime": "2020-03-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + } + ] + }, + "mainSleep": false + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-04-13.json b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-04-13.json new file mode 100644 index 0000000..a9a7b45 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep-2020-04-13.json @@ -0,0 +1,132 @@ +[ + { + "logId": 11111111111, + "dateOfSleep": "2020-04-13", + "startTime": "2020-04-13T10:09:08.000", + "endTime": "2020-04-13T10:09:08.000", + "duration": 11111111, + "minutesToFallAsleep": 1, + "minutesAsleep": 111, + "minutesAwake": 11, + "minutesAfterWakeup": 1, + "timeInBed": 111, + "efficiency": 11, + "type": "xxxxxx", + "infoCode": 1, + "logType": "xxxxxxxxxxxxx", + "levels": { + "summary": { + "deep": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "wake": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "light": { + "count": 11, + "minutes": 111, + "thirtyDayAvgMinutes": 111 + }, + "rem": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + } + }, + "data": [ + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + }, + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxxx", + "seconds": 111 + } + ], + "shortData": [ + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + }, + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + } + ] + }, + "mainSleep": false + }, + { + "logId": 11111111111, + "dateOfSleep": "2020-04-13", + "startTime": "2020-04-13T10:09:08.000", + "endTime": "2020-04-13T10:09:08.000", + "duration": 11111111, + "minutesToFallAsleep": 1, + "minutesAsleep": 111, + "minutesAwake": 11, + "minutesAfterWakeup": 1, + "timeInBed": 111, + "efficiency": 11, + "type": "xxxxxx", + "infoCode": 1, + "logType": "xxxxxxxxxxxxx", + "levels": { + "summary": { + "deep": { + "count": 1, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "wake": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + }, + "light": { + "count": 11, + "minutes": 111, + "thirtyDayAvgMinutes": 111 + }, + "rem": { + "count": 11, + "minutes": 11, + "thirtyDayAvgMinutes": 11 + } + }, + "data": [ + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 111 + }, + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxxx", + "seconds": 111 + } + ], + "shortData": [ + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + }, + { + "dateTime": "2020-04-13T10:09:08.000", + "level": "xxxx", + "seconds": 11 + } + ] + }, + "mainSleep": false + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep_score.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep_score.csv new file mode 100644 index 0000000..a356f03 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Sleep/sleep_score.csv @@ -0,0 +1,20 @@ +sleep_log_entry_id,timestamp,overall_score,composition_score,revitalization_score,duration_score,deep_sleep_in_minutes,resting_heart_rate,restlessness +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.110000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Social/Feed README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Social/Feed README.txt new file mode 100644 index 0000000..409a5fc --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Social/Feed README.txt @@ -0,0 +1,63 @@ +Social Feed Data Export + +The Social Feed category of your data export includes all of the content you have posted to the Fitbit community Feed and Groups. This includes cheers, comments, posts, groups, and images. + +Files Included: +---------- + +Feed Cheers.csv + +This is the list of all Posts and Comments you have cheered + + post_id - the id of the post + comment_id - if a comment was cheered, the id of the comment + author_id - the id of the user whose content you cheered + author_name - the name of the user whose content you cheered + +---------- + +Feed Comments.csv + +This is the list of all Comments you have made on a Post + + id - the id of the comment + content - the text content of the comment + timestamp - the date and time the comment was made + post_id - the id of the post the comment was replying to + +---------- + +Feed Posts.csv + +This is the list of all your Posts made to the community Feed and any to group you have been a apart of + + id - the id of the post + text_content - the text content of the post + timestamp - the date and time the post was made + comment_count - the number of comments made on the post + cheer_count - the number of Cheers + type - the type of post (mostly used for internal purposes) + group_id - if posted to a group, the id of the group + image_filename - if an image was included, the name of the image + embedded_url - a shared url + +---------- + +Groups.csv + +This is the list of all Groups you are a member of. description, avatar_url, cover_image_url, and members will only be populated for user created groups (i.e. groups with type "Closed group") + + id - the id of the group + name - the name of the group + description - the description of the group + avatar_url - the url of the group avatar image + cover_image_url - the url of the group cover image + type - the type of the group (e.g. "Public group") + members - the number of users who have joined the group + +---------- + +Images + +Any images you posted as part of a Post will be included separately in a Media/Posts subdirectory +Any avatar/cover images for user created groups you are a member of will be included separately in a Media/Groups subdirectory diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Social/badge.json b/test/fixtures/fitbit-2026-02/FullHumanName/Social/badge.json new file mode 100644 index 0000000..5b64c29 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Social/badge.json @@ -0,0 +1,29 @@ +[ + { + "encodedId": "xxxxxx", + "badgeType": "DAILY_STEPS", + "value": 1111, + "timesAchieved": 111, + "dateTime": "2020-04-13", + "name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "shortName": "xxxxxxxxx", + "earnedMessage": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "description": "xxxxxxxxxxxxxxxxxxxx", + "category": "xxxxxxxxxxx", + "shareText": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, + { + "encodedId": "xxxxxx", + "badgeType": "LIFETIME_DISTANCE", + "value": 1111, + "unit": "xxxxx", + "timesAchieved": 1, + "dateTime": "2020-04-13", + "name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "shortName": "xxxxx", + "earnedMessage": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "description": "xxxxxxxxxxxxxxxxxxxx", + "category": "xxxxxxxxxxxxxxxxx", + "shareText": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + } +] diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Journal README.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Journal README.txt new file mode 100644 index 0000000..ac2b3fc --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Journal README.txt @@ -0,0 +1,41 @@ +Stress Journal Data Export + +Your Stress Journal Data export includes your body responses, cEDA data, and certain information you log within the stress experience. + +Files Included: +---------- + +Stress Daily Summaries.csv + +Your Stress Daily Summaries data includes the following: + + body_response_start — Date and start time of a body response + body_response_end — Date and end time of a body response + logged_mood — Any moods you logged for a body response + +---------- + +Stress Weekly Summaries.csv + +Your Stress Weekly Summaries data includes the following: + + date — Last day of the week in which you selected a focus area + selected_focus — Any focus areas you logged in the stress experience + +---------- + +CEDA Data.csv + +Your cEDA Data, also known as continuous electrodermal activity data, includes the following: + + timestamp — Date and time when EDA data was collected + eda_level_real — Electrodermal activity level collected by the cEDA sensor on your device + +---------- + +Survey Data.csv + +Your Survey Data includes the following: + + logged_value — Any moods you logged within the stress experience + timestamp — Date and time when mood data was logged \ No newline at end of file diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score Readme.txt b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score Readme.txt new file mode 100644 index 0000000..1f3f575 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score Readme.txt @@ -0,0 +1,22 @@ +Stress Score Data Export + +Description: Users who have access to the Stress Management experience receive a daily Stress Management Score, which is a round number comprised of 3 subscores. + +Files Included: +---------- + +Stress Score.csv + +Stress Score for each of the days it was attempted to be calculated + +date - date when Stress Score was recorded +updated_at - time when score was created or update last time +stress_score - value of Stress Score +sleep_points - points from sleep components +max_sleep_points - maximal possible points from sleep components a user can get +responsiveness_points - points from responsiveness components +max_responsiveness_points - maximal points from responsiveness components a user can get +exertion_points - points from exertion components +max_exertion_points - maximal points from exertion components a user can get +status - IN_PROGRESS (calculations is in progress) / READY (Stress Score was calculated) +calculation_failed - whether stress score calculation has failed for a certain date diff --git a/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score.csv b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score.csv new file mode 100644 index 0000000..63a3434 --- /dev/null +++ b/test/fixtures/fitbit-2026-02/FullHumanName/Stress/Stress Score.csv @@ -0,0 +1 @@ +DATE,UPDATED_AT,STRESS_SCORE,SLEEP_POINTS,MAX_SLEEP_POINTS,RESPONSIVENESS_POINTS,MAX_RESPONSIVENESS_POINTS,EXERTION_POINTS,MAX_EXERTION_POINTS,STATUS,CALCULATION_FAILED diff --git a/test/fixtures/snapchat-2023-11.md b/test/fixtures/snapchat-2023-11.md new file mode 100644 index 0000000..7cd92da --- /dev/null +++ b/test/fixtures/snapchat-2023-11.md @@ -0,0 +1,83 @@ +# Snapchat + +Exported from the web exporter + +## Manual Edits + +* memories and chat_media placeholders +* Snapchat seemed to have events exported where the `+` in emails broke my parsing and the email contained a ' ' instead, so I fixed that +* Keys use unique dates in `json/in_app_surveys.json` +* Keys in `json/chat_history.json` use user ids, had to manually truncate and edit + +## Notes + +* `memories/` + * No exif data + * Does not seem to have any correlating .json file. It's just a dump to the disk + * files are like `2020-01-01_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-main.jpg` + * Date has no time, just date + * `aaaaa...` seems to be a guid + * `main` | `overlay` at the end, with the same guid + * `main` is just the image + * `overlay` looks to be like a filter or some other applied thing that was saved with the memory + * Images may be rotated +* `chat_media/` + * No exif + * files are like `2020-01-01_b~xxxx.jpeg` + * sometimes they have `main` | `overlay` or something + * No idea what the `b~` means or if the xxx is an id or what. Perhaps base64 encoded protobuf, but nothing I decoded seemed to correlate to any identifier in the export + * Only referenced from ... oh... it's broken. The `type: "MEDIA"` in snapchats exporter has all empty "content" fields. Amazing... So this will have to be pieced together some other way + * This will most likel have to be manually repaired +* `json/` + * Scrubbed + * See manual changes + + +* Comes with both an html and json export (I will only keep the json after deduping) + * NOTE: That the html export has explanations which might be useful to explain some of these fields... + * I compared all .html to .json side by side (browser <-> text editor) and all of them were present in both and had the same data except `snap_history.html` (was empty in .html) and `faq.html` (just informational) +* I noticed on chat history html pages it puts _every_ category, not just the ones I have. Might be useful future reference + +``` +Frequently Asked Questions +Login History and Account Information +Snap History Metadata +Chat History Metadata +My AI +Our Story & Spotlight Content +Spotlight Replies +Purchase History +Snapchat Support History +User Profile +Public Profiles +Friends +Ranking +Story History +Account History +Location +Search History +Terms History +Subscriptions +Bitmoji +In-app Surveys +Reported Content +Bitmoji Kit +Connected Apps +Talk History +Ads Manager +My Lenses +Memories +Cameos +Email Campaign History +Snap Tokens +Payouts +Orders +Snap Map Places +Shopping Favorites +Payments +My Sounds +Photoshoot Snaps +Feature Emails +AI Selfies +``` + diff --git a/test/fixtures/snapchat-2023-11/json/account.json b/test/fixtures/snapchat-2023-11/json/account.json new file mode 100644 index 0000000..5c6c8cd --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/account.json @@ -0,0 +1,38 @@ +{ + "Basic Information": { + "Username": "xxxxxxxxx", + "Name": "xxxxx", + "Creation Date": "2020-04-13 10:09:08 UTC", + "Registration IP": "", + "Country": "" + }, + "Device Information": { + "Make": "", + "Model ID": "", + "Model Name": "", + "Language": "", + "OS Type": "", + "OS Version": "", + "Connection Type": "" + }, + "Device History": [], + "Privacy Policy and Terms of Service Acceptance History": [], + "Custom Creative Tools Terms": [], + "Login History": [ + { + "IP": "1.1.1.1", + "Country": "xx", + "Created": "2020-04-13 10:09:08 UTC", + "Status": "xxxxxxx", + "Device": "some/path" + }, + { + "IP": "1.1.1.1", + "Country": "xx", + "Created": "2020-04-13 10:09:08 UTC", + "Status": "xxxxxxx", + "Device": "some/path" + } + ], + "Family Center": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/account_history.json b/test/fixtures/snapchat-2023-11/json/account_history.json new file mode 100644 index 0000000..634fdf9 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/account_history.json @@ -0,0 +1,47 @@ +{ + "Display Name Change": [ + { + "Date": "2020-04-13 10:09:08 UTC", + "Display Name": "xxxxx" + }, + { + "Date": "", + "Display Name": "xxxxxx" + } + ], + "Email Change": [ + { + "Date": "2020-04-13 10:09:08 UTC", + "Email Address": "not_a_real_email@example.com" + } + ], + "Mobile Number Change": [], + "Password Change": [ + { + "Date": "2020-04-13 10:09:08 UTC" + }, + { + "Date": "2020-04-13 10:09:08 UTC" + } + ], + "Snapchat Linked to Bitmoji": [ + { + "Date": "2020-04-13 10:09:08 UTC" + } + ], + "Spectacles": [], + "Two-Factor Authentication": [], + "Account deactivated / reactivated": [], + "Download My Data Reports": [ + { + "Date": "2020-04-13 10:09:08 UTC", + "Status": "xxxxxxx", + "Email Address": "not_a_real_email@example.com" + }, + { + "Date": "2020-04-13 10:09:08 UTC", + "Status": "xxxxxxxxx", + "Email Address": "not_a_real_email@example.com" + } + ] +} diff --git a/test/fixtures/snapchat-2023-11/json/bitmoji.json b/test/fixtures/snapchat-2023-11/json/bitmoji.json new file mode 100644 index 0000000..ff8ddde --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/bitmoji.json @@ -0,0 +1,31 @@ +{ + "Basic Information": { + "First Name": "", + "Last Name": "", + "Email": "", + "Phone Number": "", + "Account Creation Date": "2020-04-13 10:09:08 UTC", + "Account Creation User Agent": "" + }, + "Analytics": { + "App Open Count": 1, + "Avatar Gender": "xxxx", + "Outfit Save Count": 1, + "Share Count": 1 + }, + "Terms of Service Acceptance History": [ + { + "Version": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Acceptance Date": "2020-04-13 10:09:08" + }, + { + "Version": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Acceptance Date": "2020-04-13 10:09:08" + } + ], + "Search History": [], + "Support Cases": [], + "Selfies": [], + "Keyboard Enable Full Access History (iOS only)": [], + "Connected Apps": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/cameos_metadata.json b/test/fixtures/snapchat-2023-11/json/cameos_metadata.json new file mode 100644 index 0000000..9afbe54 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/cameos_metadata.json @@ -0,0 +1,8 @@ +{ + "Cameos Selfie": { + "Cameos Body Selected": "xxxxxxxxxxxx", + "Hairstyle": "xxxxxxxxxxxx", + "Use My Cameos Selfie": "xxxxxxx" + }, + "Cameos Stories": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/chat_history.json b/test/fixtures/snapchat-2023-11/json/chat_history.json new file mode 100644 index 0000000..7c5ed49 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/chat_history.json @@ -0,0 +1,42 @@ +{ + "some_friend": [ + { + "From": "xxxxxxxxx", + "Media Type": "xxxxx", + "Created": "2020-04-13 10:09:08 UTC", + "Content": "", + "Conversation Title": null, + "IsSender": false, + "Created(microseconds)": 1111111111111 + }, + { + "From": "xxxxxxxxx", + "Media Type": "xxxx", + "Created": "2020-04-13 10:09:08 UTC", + "Content": "xxxxxxxxxxxxxxxxxx", + "Conversation Title": null, + "IsSender": false, + "Created(microseconds)": 1111111111111 + } + ], + "some_friend_too": [ + { + "From": "xxxxxxxxxxxxxx", + "Media Type": "xxxxx", + "Created": "2020-04-13 10:09:08 UTC", + "Content": "", + "Conversation Title": "xxxxxxxxxxxxxxxx", + "IsSender": false, + "Created(microseconds)": 1111111111111 + }, + { + "From": "xxxxxxxxxxxxx", + "Media Type": "xxxx", + "Created": "2020-04-13 10:09:08 UTC", + "Content": "xxxxxxxxxxxxxxxxxxxxxx", + "Conversation Title": "xxxxxxxxxxxxxxxx", + "IsSender": false, + "Created(microseconds)": 1111111111111 + } + ] +} diff --git a/test/fixtures/snapchat-2023-11/json/connected_apps.json b/test/fixtures/snapchat-2023-11/json/connected_apps.json new file mode 100644 index 0000000..6b20ab9 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/connected_apps.json @@ -0,0 +1,11 @@ +{ + "Login History": [], + "Permissions": [ + { + "App": "xxxxxxx", + "Time": "2020-04-13 10:09:08 UTC", + "Type": "xxxxxxx" + } + ], + "Connected Applications": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/email_campaign_history.json b/test/fixtures/snapchat-2023-11/json/email_campaign_history.json new file mode 100644 index 0000000..2e03ff2 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/email_campaign_history.json @@ -0,0 +1,13 @@ +{ + "Email Campaign Subscriptions": [ + { + "Email Campaign": "xxxxxxxxxxxxxxxx", + "Opt Out Status": "xxxxxxxxxxxx" + }, + { + "Email Campaign": "xxxxxxxxxxxxxxx", + "Opt Out Status": "xxxxxxxxxxxx" + } + ], + "Email Campaign History": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/friends.json b/test/fixtures/snapchat-2023-11/json/friends.json new file mode 100644 index 0000000..604d5cb --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/friends.json @@ -0,0 +1,100 @@ +{ + "Friends": [ + { + "Username": "xxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxxxx" + } + ], + "Friend Requests Sent": [ + { + "Username": "xxxxxxxxxx", + "Display Name": "xxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxxx", + "Display Name": "xxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxxxxxxxx" + } + ], + "Blocked Users": [ + { + "Username": "xxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + } + ], + "Deleted Friends": [ + { + "Username": "xxxxxx", + "Display Name": "xxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + } + ], + "Hidden Friend Suggestions": [], + "Ignored Snapchatters": [ + { + "Username": "xxxxxxxxx", + "Display Name": "xxxxxxxxxxxxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxx", + "Display Name": "xxxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + } + ], + "Pending Requests": [ + { + "Username": "xxxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + }, + { + "Username": "xxxxxxxxxxxxxx", + "Display Name": "xxxxxxxxxxxxx", + "Creation Timestamp": "2020-04-13 10:09:08 UTC", + "Last Modified Timestamp": "2020-04-13 10:09:08 UTC", + "Source": "xxxxxxxxxxxxxxxx" + } + ], + "Shortcuts": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/in_app_surveys.json b/test/fixtures/snapchat-2023-11/json/in_app_surveys.json new file mode 100644 index 0000000..6ffd77c --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/in_app_surveys.json @@ -0,0 +1,26 @@ +{ + "Survey 2020/04/12": [ + { + "Time": "xxxxxxxxxxxx", + "Survey Question": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Survey Response": "xxxxxxxxxx" + }, + { + "Time": "xxxxxxxxxxxx", + "Survey Question": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Survey Response": "xxx" + } + ], + "Survey 2020/04/13": [ + { + "Time": "xxxxxxxxxxxx", + "Survey Question": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Survey Response": "xxxxxxxxxxxxxx" + }, + { + "Time": "xxxxxxxxxxxx", + "Survey Question": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Survey Response": "some/path" + } + ] +} diff --git a/test/fixtures/snapchat-2023-11/json/location_history.json b/test/fixtures/snapchat-2023-11/json/location_history.json new file mode 100644 index 0000000..6754237 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/location_history.json @@ -0,0 +1,23 @@ +{ + "Frequent Locations": [], + "Latest Location": [ + { + "City": "", + "Country": "", + "Region": "" + } + ], + "Home & Work": {}, + "Daily Top Locations": [], + "Top Locations Per Six-Day Period": [], + "Location History": [], + "Businesses and public places you may have visited": [], + "Areas you may have visited in the last two years": [ + { + "Time": "some/path", + "City": "xxxxxx", + "Region": "xxxxxxxx", + "Postal Code": "11111" + } + ] +} diff --git a/test/fixtures/snapchat-2023-11/json/ranking.json b/test/fixtures/snapchat-2023-11/json/ranking.json new file mode 100644 index 0000000..ca60e14 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/ranking.json @@ -0,0 +1,6 @@ +{ + "Number of Stories Viewed": [ + 1 + ], + "Content Interests": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/shared_story.json b/test/fixtures/snapchat-2023-11/json/shared_story.json new file mode 100644 index 0000000..bb15c44 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/shared_story.json @@ -0,0 +1,11 @@ +{ + "Shared Story": [], + "Spotlight History": [ + { + "Story Date": "2020-04-13 10:09:08 UTC", + "Story URL": "url://somewhere", + "Action Type": "xxxx", + "View Time": "xxxxxxxxxxxxx" + } + ] +} diff --git a/test/fixtures/snapchat-2023-11/json/snapchat_ai.json b/test/fixtures/snapchat-2023-11/json/snapchat_ai.json new file mode 100644 index 0000000..892aecd --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/snapchat_ai.json @@ -0,0 +1,4 @@ +{ + "My AI Content": [], + "My AI Memory": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/subscriptions.json b/test/fixtures/snapchat-2023-11/json/subscriptions.json new file mode 100644 index 0000000..30eb8ab --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/subscriptions.json @@ -0,0 +1,10 @@ +{ + "Public Users": [ + "xxxxxxxxxxxxxxx" + ], + "Publishers": [], + "Stories": [], + "Last Active Timezone": "some/path", + "Push Notifications": [], + "Hidden Category Sections": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/terms_history.json b/test/fixtures/snapchat-2023-11/json/terms_history.json new file mode 100644 index 0000000..d6321d7 --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/terms_history.json @@ -0,0 +1,15 @@ +{ + "Snap Inc. Terms of Service": [ + { + "Version": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Acceptance Date": "2020-04-13 10:09:08 UTC" + }, + { + "Version": "xxxxxxxxxxxxxxxxxxxxxxxxx", + "Acceptance Date": "2020-04-13 10:09:08 UTC" + } + ], + "Custom Creative Tools Terms": [], + "Business Services Terms": [], + "Games Terms": [] +} diff --git a/test/fixtures/snapchat-2023-11/json/user_profile.json b/test/fixtures/snapchat-2023-11/json/user_profile.json new file mode 100644 index 0000000..d5cde8c --- /dev/null +++ b/test/fixtures/snapchat-2023-11/json/user_profile.json @@ -0,0 +1,39 @@ +{ + "App Profile": { + "Country": "xx", + "Creation Time": "2020-04-13 10:09:08 UTC", + "Account Creation Country": "xxxxxxx", + "Platform Version": "xxxxxxx", + "In-app Language": "xx" + }, + "Demographics": { + "Cohort Age": "", + "Derived Ad Demographic": "" + }, + "Subscriptions": [], + "Engagement": [], + "Discover Channels Viewed": [], + "Breakdown of Time Spent on App": [], + "Ads You Interacted With": [], + "Interest Categories": [ + "xxxxxx", + "xxxxxxxxxxxxxxxxxxx" + ], + "Content Categories": [ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "some/path" + ], + "Geographic Information": [], + "Interactions": { + "Web Interactions": [ + "xxxxxxxxxxxxx", + "xxxxxxxxxxxxxxxxxxxxxx" + ], + "App Interactions": [ + "url://somewhere", + "url://somewhere" + ] + }, + "Off-Platform Sharing": [], + "Mobile Ad Id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +} diff --git a/test/fixtures/snapchat-2023-11/memories/2020-01-01_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-main.jpg b/test/fixtures/snapchat-2023-11/memories/2020-01-01_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-main.jpg new file mode 100644 index 0000000..2762dca Binary files /dev/null and b/test/fixtures/snapchat-2023-11/memories/2020-01-01_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-main.jpg differ diff --git a/test/scrub.ts b/test/scrub.ts new file mode 100644 index 0000000..9881931 --- /dev/null +++ b/test/scrub.ts @@ -0,0 +1,135 @@ +import path from "node:path"; +import { test } from "node:test"; +import { strict as assert } from "node:assert"; +import { scrubPrimitive } from "../util/scrub_primitive.ts"; +import { $ } from "zx"; + +const scriptDir = path.dirname(new URL(import.meta.url).pathname); +const defsDir = path.join(scriptDir, "..", "util"); + +async function jqScrubPrimitive(value: unknown): Promise { + const input = JSON.stringify(value); + const result = await $`echo ${input} | jq -L ${defsDir} 'include "scrub"; scrub_primitive'`; + return JSON.parse(result.stdout.trim()); + // const result = await $`jq -f ${scrubJq} --argjson input ${input} -n '$input | scrub_primitive'`; + // return JSON.parse(result.stdout.trim()); +} + +interface TestCase { + name: string; + input: unknown; + expected: unknown; +} + +const cases: TestCase[] = [ + // === Strings === + { name: "IPv4 address", input: "192.168.1.1", expected: "1.1.1.1" }, + { name: "IPv4 address 2", input: "10.0.0.255", expected: "1.1.1.1" }, + { name: "IPv6 full", input: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", expected: "2000:0000:0000:0000:0000:0000:0000:0000" }, + { name: "IPv6 shortened", input: "fe80::1", expected: "2000:0000:0000:0000:0000:0000:0000:0000" }, + { name: "IPv6 collapsed", input: "::1", expected: "2000:0000:0000:0000:0000:0000:0000:0000" }, + + { name: "email simple", input: "user@example.com", expected: "not_a_real_email@example.com" }, + { name: "email complex", input: "john.doe+tag@sub.domain.org", expected: "not_a_real_email@example.com" }, + + { name: "http URL", input: "https://www.example.com/path?q=1", expected: "url://somewhere" }, + { name: "ftp URL", input: "ftp://files.example.com/doc.pdf", expected: "url://somewhere" }, + { name: "custom scheme URL", input: "myapp://deep/link", expected: "url://somewhere" }, + + { name: "unix path", input: "/home/user/documents", expected: "some/path" }, + { name: "relative path", input: "some/relative/path", expected: "some/path" }, + + { name: "ISO datetime with tz no millis", input: "2023-11-15T14:30:00+05:00", expected: "2020-04-13T10:09:08+00:00" }, + { name: "ISO datetime with tz no tz colon no T", input: "2023-11-15 14:30:00+0500", expected: "2020-04-13 10:09:08+0000" }, + { name: "ISO datetime with negative tz no millis", input: "2023-11-15T14:30:00-08:00", expected: "2020-04-13T10:09:08+00:00" }, + { name: "ISO datetime with millis and tz", input: "2023-11-15T14:30:00.123+05:00", expected: "2020-04-13T10:09:08.000000+00:00" }, + { name: "ISO datetime with 6 digit millis and tz", input: "2023-11-15T14:30:00.123456+05:00", expected: "2020-04-13T10:09:08.000000+00:00" }, + { name: "ISO datetime with millis no tz", input: "2023-11-15T14:30:00.123", expected: "2020-04-13T10:09:08.000" }, + { name: "ISO datetime no millis no tz (falls through to millis branch)", input: "2023-11-15T14:30:00", expected: "2020-04-13T10:09:08.000" }, + { name: "ISO datetime with Z", input: "2023-11-15T14:30:00Z", expected: "2020-04-13T10:09:08Z" }, + { name: "ISO datetime with Z with millis", input: "2023-11-15T14:30:00.000Z", expected: "2020-04-13T10:09:08.000000Z" }, + { name: "ISO datetime with no seconds", input: "2023-11-15T14:30", expected: "2020-04-13T10:09" }, + { name: "ISO datetime range no T", input: "2023-11-15 14:30:08 - 2022-11-11 11:11:11", expected: "2020-04-13 10:09:08 - 2020-04-13 10:09:08" }, + { name: "date only", input: "2023-11-15", expected: "2020-04-13" }, + { name: "date only 2", input: "1999-01-01", expected: "2020-04-13" }, + { name: "datetime with UTC suffix", input: "2023-11-15 14:30:00 UTC", expected: "2020-04-13 10:09:08 UTC" }, + { name: "datetime without UTC suffix", input: "2023-11-15 14:30:00", expected: "2020-04-13 10:09:08" }, + { name: "datetime with /s and MM/DD/YY (whyyyy, fitbit, whyyy)", input: "11/15/25 14:30:00", expected: "04/13/20 10:09:08" }, + { name: "datetime with Mon Apr 13 10:09:08 UTC 2020 format", input: "Tue Apr 14 11:11:11 UTC 1999", expected: "Mon Apr 13 10:09:08 UTC 2020" }, + { name: "UTC offset only", input: "+04:00", expected: "+00:00" }, + { name: "UTC offset only negative", input: "-04:00", expected: "-00:00" }, + + { name: "numeric string short", input: "42", expected: "11" }, + { name: "numeric string long", input: "1234567890", expected: "1111111111" }, + { name: "numeric string single digit", input: "0", expected: "1" }, + + { name: "decimal string", input: "3.14", expected: "1.11" }, + { name: "negative decimal string", input: "-123.456", expected: "-111.111" }, + { name: "negative integer string", input: "-42", expected: "-11" }, + + { name: "hex string short", input: "deadbeef", expected: "a1a1a1a1" }, + { name: "hex string odd length", input: "abc", expected: "a1a" }, + { name: "hex string uppercase", input: "DEADBEEF", expected: "a1a1a1a1" }, + { name: "hex string mixed case", input: "AbCd01", expected: "a1a1a1" }, + + { name: "empty string", input: "", expected: "" }, + + { name: "string 'true'", input: "true", expected: "false" }, + { name: "string 'false'", input: "false", expected: "false" }, + { name: "string 'null'", input: "null", expected: "null" }, + + { name: "generic string short", input: "hello", expected: "xxxxx" }, + { name: "generic string with spaces", input: "hello world!", expected: "xxxxxxxxxxxx" }, + { name: "generic string single char", input: "z", expected: "x" }, + { name: "generic string special chars", input: "foo-bar_baz", expected: "xxxxxxxxxxx" }, + + // === Strings: Media file extensions (passthrough) === + // TODO: Fix + // { name: "jpg file path", input: "photo.jpg", expected: "photo.jpg" }, + // { name: "png file path", input: "image.png", expected: "image.png" }, + // { name: "mp4 file path", input: "video.mp4", expected: "video.mp4" }, + // { name: "mp3 file path", input: "song.mp3", expected: "song.mp3" }, + // { name: "svg file path", input: "icon.svg", expected: "icon.svg" }, + // { name: "webm file path", input: "clip.webm", expected: "clip.webm" }, + // { name: "flac file path", input: "track.flac", expected: "track.flac" }, + // { name: "case insensitive JPG", input: "photo.JPG", expected: "photo.JPG" }, + + // === Numbers === + { name: "unix timestamp low boundary", input: 946702800, expected: (((946702800 % 31557600 + 1704067200) / 5000 | 0) * 5000) }, + { name: "unix timestamp mid", input: 1700000000, expected: (((1700000000 % 31557600 + 1704067200) / 5000 | 0) * 5000) }, + { name: "unix timestamp high boundary", input: 1893474000, expected: (((1893474000 % 31557600 + 1704067200) / 5000 | 0) * 5000) }, + + { name: "integer single digit", input: 5, expected: 1 }, + { name: "integer two digits", input: 42, expected: 11 }, + { name: "integer three digits", input: 999, expected: 111 }, + { name: "integer large", input: 123456, expected: 111111 }, + { name: "integer zero", input: 0, expected: 1 }, + + // TODO: Fix, I dont care about negatives rn + // { name: "negative integer", input: -42, expected: -11 }, + // { name: "negative integer large", input: -123456, expected: -111111 }, + + { name: "decimal simple", input: 3.14, expected: 1.11 }, + { name: "decimal long fraction", input: 123.4567, expected: 111.1111 }, + { name: "decimal short fraction", input: 0.5, expected: 0.1 }, + // { name: "decimal negative", input: -3.14, expected: -1.11 }, + // { name: "decimal negative with zero int", input: -0.75, expected: -0.11 }, + + // === Misc === + { name: "boolean true", input: true, expected: false }, + { name: "boolean false", input: false, expected: false }, + + { name: "null", input: null, expected: null }, + +]; + +for (const { name, input, expected } of cases) { + test(`scrub() - ${name} TypeScript scrubPrimitive(${JSON.stringify(input)}) === ${JSON.stringify(expected)}`, () => { + const result = scrubPrimitive(input); + assert.deepEqual(result, expected); + }); + test(`scrub() - ${name} jq scrub_primitive(${JSON.stringify(input)}) === ${JSON.stringify(expected)}`, async () => { + const result = await jqScrubPrimitive(input); + assert.deepEqual(result, expected); + }); +} diff --git a/test/snapshots/FullHumanName_Fitbit___AFib_Enrollmentsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___AFib_Enrollmentsnapshot.csv new file mode 100644 index 0000000..3df8f22 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___AFib_Enrollmentsnapshot.csv @@ -0,0 +1,2 @@ +consented,onboarded,enrolled,last_updated,last_notified +false,false,false,Fri Apr 13 10:09:08 UTC 2020,null \ No newline at end of file diff --git a/test/snapshots/FullHumanName_Fitbit___Account_Access_Eventssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Account_Access_Eventssnapshot.csv new file mode 100644 index 0000000..bb49f1e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Account_Access_Eventssnapshot.csv @@ -0,0 +1,10 @@ +timestamp,event_name,email,location,ip,outcome,reason,application,device_info +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxxxxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Account_Management_Eventssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Account_Management_Eventssnapshot.csv new file mode 100644 index 0000000..af34107 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Account_Management_Eventssnapshot.csv @@ -0,0 +1,4 @@ +timestamp,event_name,email,location,ip,outcome,reason +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, diff --git a/test/snapshots/FullHumanName_Fitbit___Active_Zone_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Active_Zone_Minutessnapshot.csv new file mode 100644 index 0000000..8780246 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Active_Zone_Minutessnapshot.csv @@ -0,0 +1,21 @@ +date_time,heart_zone_id,total_minutes +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,CARDIO,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 diff --git a/test/snapshots/FullHumanName_Fitbit___Activity_Goalssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Activity_Goalssnapshot.csv new file mode 100644 index 0000000..0cdcfbb --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Activity_Goalssnapshot.csv @@ -0,0 +1,13 @@ +type,frequency,target,result,status,is_primary,start_date,end_date,created_on,edited_on +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,11.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,111.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxx,xxxxx,1111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,1.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxx,xxxxxx,11111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,1111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null diff --git a/test/snapshots/FullHumanName_Fitbit___Badgessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Badgessnapshot.csv new file mode 100644 index 0000000..697a792 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Badgessnapshot.csv @@ -0,0 +1,3 @@ +"encodedId","badgeType","value","timesAchieved","dateTime","name","shortName","category" +"xxxxxx","DAILY_STEPS",1111,111,"2020-04-13","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxx","xxxxxxxxxxx" +"xxxxxx","LIFETIME_DISTANCE",1111,1,"2020-04-13","xxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxx","xxxxxxxxxxxxxxxxx" diff --git a/test/snapshots/FullHumanName_Fitbit___Caloriessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Caloriessnapshot.csv new file mode 100644 index 0000000..8c772f4 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Caloriessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1.11" +"04/13/20 10:09:08","1.11" diff --git a/test/snapshots/FullHumanName_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv new file mode 100644 index 0000000..f788a78 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv @@ -0,0 +1 @@ +property_type,value,last_update diff --git a/test/snapshots/FullHumanName_Fitbit___Daily_SpO2snapshot.csv b/test/snapshots/FullHumanName_Fitbit___Daily_SpO2snapshot.csv new file mode 100644 index 0000000..2e66913 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Daily_SpO2snapshot.csv @@ -0,0 +1,10 @@ +timestamp,average_value,lower_bound,upper_bound +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,111.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 diff --git a/test/snapshots/FullHumanName_Fitbit___Demographic_VO2_Maxsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Demographic_VO2_Maxsnapshot.csv new file mode 100644 index 0000000..96f29d5 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Demographic_VO2_Maxsnapshot.csv @@ -0,0 +1,3 @@ +dateTime,demographicVO2Max,demographicVO2MaxError,filteredDemographicVO2Max,filteredDemographicVO2MaxError +"04/13/20 10:09:08",11.11111,1.1111111111111112,11.11111111111111,0.1111111111111111 +"04/13/20 10:09:08",11.11111111111111,1.1111111111111112,11.11111111111111,0.1111111111111111 diff --git a/test/snapshots/FullHumanName_Fitbit___Device_Temperaturesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Device_Temperaturesnapshot.csv new file mode 100644 index 0000000..ce7ead7 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Device_Temperaturesnapshot.csv @@ -0,0 +1 @@ +recorded_time,temperature,sensor_type diff --git a/test/snapshots/FullHumanName_Fitbit___Devicessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Devicessnapshot.csv new file mode 100644 index 0000000..91dfbeb --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Devicessnapshot.csv @@ -0,0 +1,2 @@ +wire_id,device_type,serial_number,enabled,fw_version +a1a1a1a1a1a1,xxxxxxxxx,,false,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Distancesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Distancesnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Distancesnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/FullHumanName_Fitbit___Email_Auditsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Email_Auditsnapshot.csv new file mode 100644 index 0000000..0ac579c --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Email_Auditsnapshot.csv @@ -0,0 +1,2 @@ +previous_email,change_time,request_id +not_a_real_email@example.com,2020-04-13T10:09:08.000000Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Estimated_Oxygen_Variationsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Estimated_Oxygen_Variationsnapshot.csv new file mode 100644 index 0000000..3370911 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Estimated_Oxygen_Variationsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,Infrared to Red Signal Ratio +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 diff --git a/test/snapshots/FullHumanName_Fitbit___Exercisessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Exercisessnapshot.csv new file mode 100644 index 0000000..8db9c80 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Exercisessnapshot.csv @@ -0,0 +1,3 @@ +logId,activityName,activityTypeId,averageHeartRate,calories,duration,activeDuration,steps,logType,startTime,hasGps +11111111111,"xxxxxxxxxxxx",1111,111,111,1111111,1111111,"","xxxxxxxxxxxxx","04/13/20 10:09:08",false +11111111111,"xxxx",11111,111,11,1111111,1111111,1111,"xxxxxxxxxxxxx","04/13/20 10:09:08",false diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Active_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Active_Minutessnapshot.csv new file mode 100644 index 0000000..4087a1b --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Active_Minutessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,light,moderate,very,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Active_Zone_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Active_Zone_Minutessnapshot.csv new file mode 100644 index 0000000..ab4b025 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Active_Zone_Minutessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone,total minutes,data source +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Activity_Levelsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Activity_Levelsnapshot.csv new file mode 100644 index 0000000..1235454 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Activity_Levelsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,level,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_App_Setting_Datasnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_App_Setting_Datasnapshot.csv new file mode 100644 index 0000000..72f34b5 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_App_Setting_Datasnapshot.csv @@ -0,0 +1,8 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx +2020-04-13 10:09:08+0000,xxxxxxxxxxx,11 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Body_Temperaturesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Body_Temperaturesnapshot.csv new file mode 100644 index 0000000..5ac3ef0 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Body_Temperaturesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,temperature celsius,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Calibration_Statussnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Calibration_Statussnapshot.csv new file mode 100644 index 0000000..ab6a223 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Calibration_Statussnapshot.csv @@ -0,0 +1,3 @@ +feature,remaining_days,calibration_start_date,latest_completion_date,latest_update_date +xxxxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 +xxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv new file mode 100644 index 0000000..e3b7f03 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,kcal,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Caloriessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Caloriessnapshot.csv new file mode 100644 index 0000000..af53ec5 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Caloriessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,calories,data source +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv new file mode 100644 index 0000000..86d142e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv @@ -0,0 +1,21 @@ +timestamp,ratio,label,data source +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv new file mode 100644 index 0000000..7e38e65 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,min observed load,max observed load,data source +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.11111111111111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Loadsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Loadsnapshot.csv new file mode 100644 index 0000000..6cadf11 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Cardio_Loadsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,workout,background,total,data source +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.111111111111111,1.111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv new file mode 100644 index 0000000..1d8de24 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,average heart rate variability milliseconds,non rem heart rate beats per minute,entropy,deep sleep root mean square of successive differences milliseconds,data source +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.11,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv new file mode 100644 index 0000000..6b9bf17 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart_rate_zone,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv new file mode 100644 index 0000000..15ed566 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv @@ -0,0 +1,19 @@ +timestamp,average percentage,lower bound percentage,upper bound percentage,baseline percentage,standard deviation percentage,data source +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Readinesssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Readinesssnapshot.csv new file mode 100644 index 0000000..5613c74 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Readinesssnapshot.csv @@ -0,0 +1,19 @@ +timestamp,score,type,readiness level,sleep readiness,heart rate variability readiness,resting heart rate readiness,data source +2020-04-13,11,xxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,111,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv new file mode 100644 index 0000000..18459a7 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,breaths per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..6308621 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Demographic_Datasnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Demographic_Datasnapshot.csv new file mode 100644 index 0000000..b30edf0 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Demographic_Datasnapshot.csv @@ -0,0 +1,4 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxx,xxxxxx +2020-04-13 10:09:08+0000,xxxxxxxx,some/path diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv new file mode 100644 index 0000000..e94ce5c --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,demographic vo2max,data source +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Distancesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Distancesnapshot.csv new file mode 100644 index 0000000..411ae14 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Distancesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,distance,data source +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Exercisessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Exercisessnapshot.csv new file mode 100644 index 0000000..0bc11e0 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Exercisessnapshot.csv @@ -0,0 +1,21 @@ +exercise_id,exercise_start,exercise_end,utc_offset,exercise_created,exercise_last_updated,activity_name,log_type,pool_length,pool_length_unit,intervals,distance_units,tracker_total_calories,tracker_total_steps,tracker_total_distance_mm,tracker_total_altitude_mm,tracker_avg_heart_rate,tracker_peak_heart_rate,tracker_avg_pace_mm_per_second,tracker_avg_speed_mm_per_second,tracker_peak_speed_mm_per_second,tracker_auto_stride_run_mm,tracker_auto_stride_walk_mm,tracker_swim_lengths,tracker_pool_length,tracker_pool_length_unit,tracker_cardio_load,manually_logged_total_calories,manually_logged_total_steps,manually_logged_total_distance_mm,manually_logged_pool_length,manually_logged_pool_length_unit,events,activity_type_probabilities,autodetected_confirmed,autodetected_start_timestamp,autodetected_end_timestamp,autodetected_utc_offset,autodetected_activity_name,autodetected_sensor_based_activity_name,deletion_reason,activity_label,suggested_start_timestamp,suggested_end_timestamp,reconciliation_status +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Goal_Settings_Historysnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Goal_Settings_Historysnapshot.csv new file mode 100644 index 0000000..1ec3636 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Goal_Settings_Historysnapshot.csv @@ -0,0 +1,21 @@ +name,objectives,schedule,status,update_time,meta,title,subtitle,rationale,domain,progress_start_time,progress_end_time,progress +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv new file mode 100644 index 0000000..5c11870 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,root mean square of successive differences milliseconds,standard deviation milliseconds,data source +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Heart_Ratesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..ed5bfe9 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Heart_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Heightsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Heightsnapshot.csv new file mode 100644 index 0000000..83c4312 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Heightsnapshot.csv @@ -0,0 +1,9 @@ +timestamp,height millimeters,data source +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_IRN_User_Statesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_IRN_User_Statesnapshot.csv new file mode 100644 index 0000000..24f272f --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_IRN_User_Statesnapshot.csv @@ -0,0 +1,2 @@ +enrollment_state,last_processed_time,last_conclusive_window,last_processed_timestamps,last_notified_time +xxxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Legacy_Setting_Datasnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Legacy_Setting_Datasnapshot.csv new file mode 100644 index 0000000..e61031f --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Legacy_Setting_Datasnapshot.csv @@ -0,0 +1,5 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxx,some/path +2020-04-13 10:09:08+0000,xxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Live_Pacesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Live_Pacesnapshot.csv new file mode 100644 index 0000000..24f88b0 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Live_Pacesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,steps,distance millimeters,altitude gain millimeters,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_MBD_Datasnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_MBD_Datasnapshot.csv new file mode 100644 index 0000000..812762e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_MBD_Datasnapshot.csv @@ -0,0 +1,12 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1111 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,2020-04-13T10:09:08.000000Z +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Oxygen_Saturationsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Oxygen_Saturationsnapshot.csv new file mode 100644 index 0000000..b4c0b2a --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Oxygen_Saturationsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,oxygen saturation percentage,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Profile_Datasnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Profile_Datasnapshot.csv new file mode 100644 index 0000000..626c2b8 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Profile_Datasnapshot.csv @@ -0,0 +1,2 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxx,xxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv new file mode 100644 index 0000000..4df7fb0 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,deep sleep stats - milli breaths per minute,deep sleep stats - standard deviation milli breaths per minute,deep sleep stats - signal to noise,light sleep stats - milli breaths per minute,light sleep stats - standard deviation milli breaths per minute,light sleep stats - signal to noise,rem sleep stats - milli breaths per minute,rem sleep stats - standard deviation milli breaths per minute,rem sleep stats - signal to noise,full sleep stats - milli breaths per minute,full sleep stats - standard deviation milli breaths per minute,full sleep stats - signal to noise,data source +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,11.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Sedentary_Periodsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Sedentary_Periodsnapshot.csv new file mode 100644 index 0000000..b0f8b9b --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Sedentary_Periodsnapshot.csv @@ -0,0 +1,3 @@ +start time,end time,data source +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Scoressnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Scoressnapshot.csv new file mode 100644 index 0000000..8a7935a --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Scoressnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_score_id,data_source,score_utc_offset,score_time,overall_score,duration_score,composition_score,revitalization_score,sleep_time_minutes,deep_sleep_minutes,rem_sleep_percent,resting_heart_rate,sleep_goal_minutes,waso_count_long_wakes,waso_count_all_wake_time,restlessness_normalized,hr_below_resting_hr,sleep_score_created,sleep_score_last_updated +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.11111111111111,-1,-1,-1,111,11,11.11111111111111,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,1.1111111111111111,11,111,11.1,11,1.111111111111111111,1.111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,11111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.11111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,111,11.11,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.1111111111111111,11,1.1111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,11.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,11.1,11,1.111111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.1111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Stagessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Stagessnapshot.csv new file mode 100644 index 0000000..6537327 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Sleep_Stagessnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_stage_id,sleep_stage_type,start_utc_offset,sleep_stage_start,end_utc_offset,sleep_stage_end,data_source,sleep_stage_created,sleep_stage_last_updated +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Sleepssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Sleepssnapshot.csv new file mode 100644 index 0000000..96ad74e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Sleepssnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_type,minutes_in_sleep_period,minutes_after_wake_up,minutes_to_fall_asleep,minutes_asleep,minutes_awake,minutes_longest_awakening,minutes_to_persistent_sleep,start_utc_offset,sleep_start,end_utc_offset,sleep_end,data_source,sleep_created,sleep_last_updated +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,111,1,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,11,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Stepssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Stepssnapshot.csv new file mode 100644 index 0000000..6b681d5 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Stepssnapshot.csv @@ -0,0 +1,21 @@ +timestamp,steps,data source +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Swim_Lengthssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Swim_Lengthssnapshot.csv new file mode 100644 index 0000000..6a6760f --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Swim_Lengthssnapshot.csv @@ -0,0 +1,21 @@ +timestamp,lap time,stroke count,stroke type,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Time_in_HR_Zonesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Time_in_HR_Zonesnapshot.csv new file mode 100644 index 0000000..d535eab --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Time_in_HR_Zonesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,data source +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Google_Weightsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Google_Weightsnapshot.csv new file mode 100644 index 0000000..538b3f9 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Google_Weightsnapshot.csv @@ -0,0 +1,2 @@ +timestamp,weight grams,data source +2020-04-13T10:09:08Z,11111,xxxxxxxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___HR_Notification_Alertssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___HR_Notification_Alertssnapshot.csv new file mode 100644 index 0000000..196ed56 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___HR_Notification_Alertssnapshot.csv @@ -0,0 +1 @@ +id,start_timestamp,end_timestamp,type,threshold,value \ No newline at end of file diff --git a/test/snapshots/FullHumanName_Fitbit___HR_Notification_Profilesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___HR_Notification_Profilesnapshot.csv new file mode 100644 index 0000000..070b26e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___HR_Notification_Profilesnapshot.csv @@ -0,0 +1 @@ +threshold_high_custom,threshold_low_custom,use_custom_threshold_high,use_custom_threshold_low,alert_high_on,alert_low_on \ No newline at end of file diff --git a/test/snapshots/FullHumanName_Fitbit___Heart_Ratesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Heart_Ratesnapshot.csv new file mode 100644 index 0000000..faee435 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Heart_Ratesnapshot.csv @@ -0,0 +1,3 @@ +dateTime,bpm,confidence +"04/13/20 10:09:08",11,1 +"04/13/20 10:09:08",11,1 diff --git a/test/snapshots/FullHumanName_Fitbit___Heightsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Heightsnapshot.csv new file mode 100644 index 0000000..2463fbb --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Heightsnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1111" +"04/13/20 10:09:08","1111" diff --git a/test/snapshots/FullHumanName_Fitbit___Lightly_Active_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Lightly_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Lightly_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/FullHumanName_Fitbit___Menstrual_Birth_Controlsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Menstrual_Birth_Controlsnapshot.csv new file mode 100644 index 0000000..06ada91 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Menstrual_Birth_Controlsnapshot.csv @@ -0,0 +1 @@ +birth_control_type,event_date,has_started diff --git a/test/snapshots/FullHumanName_Fitbit___Menstrual_Cyclessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Menstrual_Cyclessnapshot.csv new file mode 100644 index 0000000..75655a4 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Menstrual_Cyclessnapshot.csv @@ -0,0 +1 @@ +id,cycle_start_date,cycle_end_date,ovulation_start_date,ovulation_end_date,ovulation_source,period_start_date,period_end_date,period_source,fertile_start_date,fertile_end_date,fertile_source diff --git a/test/snapshots/FullHumanName_Fitbit___Menstrual_Settingssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Menstrual_Settingssnapshot.csv new file mode 100644 index 0000000..4067b1e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Menstrual_Settingssnapshot.csv @@ -0,0 +1 @@ +pregnancy_history,birth_control_history,avg_period_days,avg_cycle_days diff --git a/test/snapshots/FullHumanName_Fitbit___Menstrual_Symptomssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Menstrual_Symptomssnapshot.csv new file mode 100644 index 0000000..d084e09 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Menstrual_Symptomssnapshot.csv @@ -0,0 +1 @@ +timestamp,fluids,flow,conditions,sex,ovulation_test,cycle_altering_event,mood diff --git a/test/snapshots/FullHumanName_Fitbit___Minute_SpO2snapshot.csv b/test/snapshots/FullHumanName_Fitbit___Minute_SpO2snapshot.csv new file mode 100644 index 0000000..872efc3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Minute_SpO2snapshot.csv @@ -0,0 +1,21 @@ +timestamp,value +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 diff --git a/test/snapshots/FullHumanName_Fitbit___Moderately_Active_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Moderately_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Moderately_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/FullHumanName_Fitbit___Profilesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Profilesnapshot.csv new file mode 100644 index 0000000..839424d --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Profilesnapshot.csv @@ -0,0 +1,2 @@ +id,full_name,first_name,last_name,display_name_setting,display_name,username,email_address,date_of_birth,child,country,state,city,timezone,locale,member_since,about_me,start_of_week,sleep_tracking,time_display_format,gender,height,weight,stride_length_walking,stride_length_running,weight_unit,distance_unit,height_unit,water_unit,glucose_unit,swim_unit +xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxx,xxxxxxx,xxxx,xxxxxxxxxxx,null,not_a_real_email@example.com,2020-04-13,false,null,null,null,some/path,xxxxx,2020-04-13,null,xxxxxx,xxxxxx,xxxxxx,xxxxxx,111.11111111111111,11.1,11.1,111.11111111111111,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx diff --git a/test/snapshots/FullHumanName_Fitbit___Resting_Heart_Ratesnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Resting_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..5c6a9dd --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Resting_Heart_Ratesnapshot.csv @@ -0,0 +1,4 @@ +dateTime,value,error +"04/13/20 10:09:08",11.1111111111111,1.111111111111111 +"04/13/20 10:09:08",11.11111111111111,1.111111111111111 +"04/13/20 00:00:00",0,0 diff --git a/test/snapshots/FullHumanName_Fitbit___Retired_Passwordssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Retired_Passwordssnapshot.csv new file mode 100644 index 0000000..8bcaf99 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Retired_Passwordssnapshot.csv @@ -0,0 +1,2 @@ +date_changed,reason +2020-04-13T10:09:08.000000Z,some/path diff --git a/test/snapshots/FullHumanName_Fitbit___Scalessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Scalessnapshot.csv new file mode 100644 index 0000000..2ac02c7 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Scalessnapshot.csv @@ -0,0 +1 @@ +scale_id,short_name,display_bf,display_bf_mass_unit,display_bmi,user_icon_id diff --git a/test/snapshots/FullHumanName_Fitbit___Sedentary_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Sedentary_Minutessnapshot.csv new file mode 100644 index 0000000..2463fbb --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Sedentary_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1111" +"04/13/20 10:09:08","1111" diff --git a/test/snapshots/FullHumanName_Fitbit___Sleep_Scoresnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Sleep_Scoresnapshot.csv new file mode 100644 index 0000000..a356f03 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Sleep_Scoresnapshot.csv @@ -0,0 +1,20 @@ +sleep_log_entry_id,timestamp,overall_score,composition_score,revitalization_score,duration_score,deep_sleep_in_minutes,resting_heart_rate,restlessness +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.110000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 diff --git a/test/snapshots/FullHumanName_Fitbit___Sleepsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Sleepsnapshot.csv new file mode 100644 index 0000000..4e1ee42 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Sleepsnapshot.csv @@ -0,0 +1,5 @@ +logId,dateOfSleep,startTime,endTime,duration,minutesToFallAsleep,minutesAsleep,minutesAwake,minutesAfterWakeup,timeInBed,efficiency,type,infoCode,logType,mainSleep,deepMinutes,wakeMinutes,lightMinutes,remMinutes +11111111111,"2020-03-13","2020-03-13T10:09:08.000","2020-03-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-03-13","2020-03-13T10:09:08.000","2020-03-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-04-13","2020-04-13T10:09:08.000","2020-04-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-04-13","2020-04-13T10:09:08.000","2020-04-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 diff --git a/test/snapshots/FullHumanName_Fitbit___Stepssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Stepssnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Stepssnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/FullHumanName_Fitbit___Stress_Scoresnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Stress_Scoresnapshot.csv new file mode 100644 index 0000000..63a3434 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Stress_Scoresnapshot.csv @@ -0,0 +1 @@ +DATE,UPDATED_AT,STRESS_SCORE,SLEEP_POINTS,MAX_SLEEP_POINTS,RESPONSIVENESS_POINTS,MAX_RESPONSIVENESS_POINTS,EXERTION_POINTS,MAX_EXERTION_POINTS,STATUS,CALCULATION_FAILED diff --git a/test/snapshots/FullHumanName_Fitbit___Swim_Lengthssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Swim_Lengthssnapshot.csv new file mode 100644 index 0000000..9ed897e --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Swim_Lengthssnapshot.csv @@ -0,0 +1,3 @@ +dateTime,lapDurationSec,strokeCount,swimStrokeType,swimAlgorithmType +"04/13/20 10:09:08",11,1,"xxxxxxx","xxxxxx" +"04/13/20 10:09:08",11,1,"xxxxxxx","xxxxxx" diff --git a/test/snapshots/FullHumanName_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv new file mode 100644 index 0000000..d75f94c --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv @@ -0,0 +1,2 @@ +dateTime,BELOW_DEFAULT_ZONE_1,IN_DEFAULT_ZONE_1,IN_DEFAULT_ZONE_2,IN_DEFAULT_ZONE_3 +"04/13/20 10:09:08",111,1,1,1 diff --git a/test/snapshots/FullHumanName_Fitbit___Tracker_Optional_Configurationsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Tracker_Optional_Configurationsnapshot.csv new file mode 100644 index 0000000..f300e0a --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Tracker_Optional_Configurationsnapshot.csv @@ -0,0 +1,2 @@ +tracker_id,enabled_notification_types,on_right_hand,clock_face,enable_inactivity_alerts,last_updated_ia_time,last_reboot_time,payments_enabled,last_successful_wifi_connection_time,last_successful_wifi_connectionipv4addr,last_successful_wifi_connectionipv6addr,last_successful_wifi_connectionssid,live_data_disabled +1111111111,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,false,,false,xxxxxxxxxxxxxxxxxxxxxxxx,,false,,,,,false diff --git a/test/snapshots/FullHumanName_Fitbit___Trackerssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Trackerssnapshot.csv new file mode 100644 index 0000000..f763e9d --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Trackerssnapshot.csv @@ -0,0 +1,2 @@ +tracker_id,date_added,last_sync_date_time,batt_level,hardware_rev,is_display_distance,is_display_calories,is_display_clock,is_display_flower,is_display_elevation,is_display_chatter,is_right_handed,tracker_name,device_type,on_dominant_hand,is_display_active_minutes,clock_face,enable_ancs,is_bonded,is_display_steps,alarm_update_time,is_display_heart_rate,heart_rate_tracking,heart_rate_tracking_update_time,tap_enabled,tap_screen,flick_enabled,flick_screen +1111111111,2020-04-13,xxxxxxxxxxxxxxxxxxxxxxxx,1,11,false,false,false,false,false,false,false,,xxxxxxxxx,false,false,a,false,false,false,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,false, diff --git a/test/snapshots/FullHumanName_Fitbit___Very_Active_Minutessnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Very_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Very_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/FullHumanName_Fitbit___Weightsnapshot.csv b/test/snapshots/FullHumanName_Fitbit___Weightsnapshot.csv new file mode 100644 index 0000000..3e21787 --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___Weightsnapshot.csv @@ -0,0 +1,2 @@ +logId,weight,bmi,date,time,source +1111111111111,111.1,11.11,"04/13/20","10:09:08","xxx" diff --git a/test/snapshots/FullHumanName_Fitbit___iOS_App_Notification_Settingssnapshot.csv b/test/snapshots/FullHumanName_Fitbit___iOS_App_Notification_Settingssnapshot.csv new file mode 100644 index 0000000..2d1d7cb --- /dev/null +++ b/test/snapshots/FullHumanName_Fitbit___iOS_App_Notification_Settingssnapshot.csv @@ -0,0 +1 @@ +user_id,tracker_id,mobile_app_name,is_app_enabled,show_partial_message,is_default_message_app,created_on,modified_on diff --git a/test/snapshots/FullHumanName_base_data_manager_metadatasnapshot.csv b/test/snapshots/FullHumanName_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..081db9f --- /dev/null +++ b/test/snapshots/FullHumanName_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,38 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Fitbit___Email_Audit,,fitbit,, +Fitbit___Retired_Passwords,,fitbit,, +Fitbit___AFib_Enrollment,,fitbit,, +Fitbit___HR_Notification_Alerts,,fitbit,, +Fitbit___HR_Notification_Profile,,fitbit,, +Fitbit___Menstrual_Cycles,,fitbit,, +Fitbit___Menstrual_Symptoms,,fitbit,, +Fitbit___Menstrual_Birth_Control,,fitbit,, +Fitbit___Menstrual_Settings,,fitbit,, +Fitbit___Profile,,fitbit,, +Fitbit___Devices,,fitbit,, +Fitbit___Trackers,,fitbit,, +Fitbit___Scales,,fitbit,, +Fitbit___Tracker_Optional_Configuration,,fitbit,, +Fitbit___iOS_App_Notification_Settings,,fitbit,, +Fitbit___Activity_Goals,,fitbit,, +Fitbit___Sleep_Score,,fitbit,, +Fitbit___Badges,,fitbit,"any,text,numeric,numeric,isodatetime,text,text,text", +Fitbit___Stress_Score,,fitbit,, +Fitbit___Google_Calibration_Status,,fitbit,, +Fitbit___Google_Goal_Settings_History,,fitbit,, +Fitbit___Google_IRN_User_State,,fitbit,, +Fitbit___Google_App_Setting_Data,,fitbit,, +Fitbit___Google_Demographic_Data,,fitbit,, +Fitbit___Google_Legacy_Setting_Data,,fitbit,, +Fitbit___Google_MBD_Data,,fitbit,, +Fitbit___Google_Profile_Data,,fitbit,, +Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratio,,fitbit,, +Fitbit___Google_Cardio_Load_Observed_Interval,,fitbit,, +Fitbit___Google_Daily_Heart_Rate_Variability,,fitbit,, +Fitbit___Google_Daily_Heart_Rate_Zones,,fitbit,, +Fitbit___Google_Daily_Readiness,,fitbit,, +Fitbit___Google_Daily_Respiratory_Rate,,fitbit,, +Fitbit___Google_Daily_Resting_Heart_Rate,,fitbit,, +Fitbit___Google_Demographic_VO2_Max,,fitbit,, +Fitbit___Google_Height,,fitbit,, +Fitbit___Google_Weight,,fitbit,, diff --git a/test/snapshots/README.md b/test/snapshots/README.md new file mode 100644 index 0000000..c827bc0 --- /dev/null +++ b/test/snapshots/README.md @@ -0,0 +1,5 @@ +# snapshots + +Expected CSV output for all of the tests. + +We don't use `node:test`'s version of snapshots as they store template strings which normalize `\r\n` and fuck up our normalization/testing \ No newline at end of file diff --git a/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_0000000000000000snapshot.csv b/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_0000000000000000snapshot.csv new file mode 100644 index 0000000..cf516ab --- /dev/null +++ b/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_0000000000000000snapshot.csv @@ -0,0 +1,5 @@ +"id","timestamp","author","discriminator","content","attachment" +"111111111111111111","2020-04-13T10:09:08.000000+00:00","xxxxxxxx","1111","xxxxxxxxxxxxxxxxxx","" +"111111111111111111","2020-04-13T10:09:08.000000+00:00","xxxxxxxx","1111","xxxxxxxxx","" +"111111111111111111","2020-04-13T10:09:08.000000+00:00","xxxxxxxx","1111","https://example.com/example.png","" +"111111111111111111","2020-04-13T10:09:08.000000+00:00","xxxxxxxx","1111","xxx","GuildName - Text Channels - ChannelName [0000000000000000].json_Files/unknown-SUFFIX.png" diff --git a/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_Metasnapshot.csv b/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_Metasnapshot.csv new file mode 100644 index 0000000..bd0148f --- /dev/null +++ b/test/snapshots/discord-chat-exporter-2026-02_DiscordCE___Messages_Metasnapshot.csv @@ -0,0 +1,2 @@ +id,guild_name,channel_name,channel_type,channel_category,channel_topic,message_count +"DiscordCE___Channel_0000000000000000","xxxxxxxx","xxxxxxx","xxxxxxxxxxxxx","xxxxxxxxxxxxx","",111 diff --git a/test/snapshots/discord-chat-exporter-2026-02_base_data_manager_metadatasnapshot.csv b/test/snapshots/discord-chat-exporter-2026-02_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..0bb069e --- /dev/null +++ b/test/snapshots/discord-chat-exporter-2026-02_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,2 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +DiscordCE___Messages_0000000000000000,"""{4}"" from {2} at {1}","discord,message","any,isodatetime,sender,any,text,url",DiscordCE___Messages_Meta diff --git a/test/snapshots/discord-json-2021-01_Discord___Activity_Statssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Activity_Statssnapshot.csv new file mode 100644 index 0000000..86257b7 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Activity_Statssnapshot.csv @@ -0,0 +1,3 @@ +"application_id","last_played_at","total_duration" +"111111111111111111","2020-04-13T10:09:08.000000+00:00",1111 +"111111111111111111","2020-04-13T10:09:08.000000+00:00",111111 diff --git a/test/snapshots/discord-json-2021-01_Discord___Activity_analyticssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Activity_analyticssnapshot.csv new file mode 100644 index 0000000..afbe842 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Activity_analyticssnapshot.csv @@ -0,0 +1,3 @@ +"event_type","timestamp","channel_id","guild_id","message_id","game_name","channel_name","guild_name" +"xxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxx","","" +"xxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxx","","" diff --git a/test/snapshots/discord-json-2021-01_Discord___Activity_modelingsnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Activity_modelingsnapshot.csv new file mode 100644 index 0000000..7ad5bc0 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Activity_modelingsnapshot.csv @@ -0,0 +1,3 @@ +"event_type","timestamp","channel_id","guild_id","message_id","game_name","channel_name","guild_name" +"xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxx","","" +"xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxx","","" diff --git a/test/snapshots/discord-json-2021-01_Discord___Activity_reportingsnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Activity_reportingsnapshot.csv new file mode 100644 index 0000000..5bc1c11 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Activity_reportingsnapshot.csv @@ -0,0 +1,3 @@ +"event_type","timestamp","channel_id","guild_id","message_id","game_name","channel_name","guild_name" +"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxxxxxxxxxx","","" +"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxxxxxxxxxx","","" diff --git a/test/snapshots/discord-json-2021-01_Discord___Activity_tnssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Activity_tnssnapshot.csv new file mode 100644 index 0000000..f337b1f --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Activity_tnssnapshot.csv @@ -0,0 +1,3 @@ +"event_type","timestamp","channel_id","guild_id","message_id","game_name","channel_name","guild_name" +"xxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxx","xxxxxxxxxxx" +"xxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxx","111111111111111111","111111111111111111","111111111111111111","xxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxx","xxxxxxxxxxx" diff --git a/test/snapshots/discord-json-2021-01_Discord___Connectionssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Connectionssnapshot.csv new file mode 100644 index 0000000..b1aebfb --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Connectionssnapshot.csv @@ -0,0 +1,3 @@ +"type","name","id","verified","visibility" +"xxxxxxxxx","xxxxxxxxxxx","xxxxxxxxxxx",false,1 +"xxxxxxx","xxxxxxxx","xxxxxxxx",false,1 diff --git a/test/snapshots/discord-json-2021-01_Discord___Messages_11111111111111111snapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Messages_11111111111111111snapshot.csv new file mode 100644 index 0000000..4908c7a --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Messages_11111111111111111snapshot.csv @@ -0,0 +1,2 @@ +id,timestamp,content,attachment +8888888888,2022-02-22 22:22:22.222222+00:00,Heyo, diff --git a/test/snapshots/discord-json-2021-01_Discord___Messages_222222222222222222snapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Messages_222222222222222222snapshot.csv new file mode 100644 index 0000000..36a7ce6 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Messages_222222222222222222snapshot.csv @@ -0,0 +1,2 @@ +id,timestamp,content,attachment +2222222222222,2022-22-22 22:22:22.22222+00:00,Heyo, diff --git a/test/snapshots/discord-json-2021-01_Discord___Messages_333333333333333333snapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Messages_333333333333333333snapshot.csv new file mode 100644 index 0000000..91e17ef --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Messages_333333333333333333snapshot.csv @@ -0,0 +1,6 @@ +id,timestamp,content,attachment +000000000000000005,2011-02-02 02:05:02.000000+00:00,Huh what the heck is this message, +000000000000000004,2011-02-02 02:04:02.000000+00:00,<:thonk:000000000000000000><:thonk:000000000000000000><:thonk:000000000000000000>, +000000000000000003,2011-02-02 02:03:02.000000+00:00,"(so <@00000000000000000> who are you)", +000000000000000002,2011-02-02 02:02:02.000000+00:00,,https://cdn.discordapp.com/attachments/000000000000000000/000000000000000000/image.png +000000000000000001,2011-02-02 02:01:02.000000+00:00,https://google.com/whatever, diff --git a/test/snapshots/discord-json-2021-01_Discord___Messages_Metasnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Messages_Metasnapshot.csv new file mode 100644 index 0000000..082a1cb --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Messages_Metasnapshot.csv @@ -0,0 +1,4 @@ +id,type,name,guild_id,guild_name,recipients +"Discord___Channel_333333333333333333",0,"generalchat","333333333333333332","xxx","" +"Discord___Channel_222222222222222222",1,"","","","00000000000000000,1111111111111111" +"Discord___Channel_11111111111111111",0,"","","","" diff --git a/test/snapshots/discord-json-2021-01_Discord___Notessnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Notessnapshot.csv new file mode 100644 index 0000000..daba937 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Notessnapshot.csv @@ -0,0 +1,2 @@ +"user_id","note" +"111111111111111111","xxxx" diff --git a/test/snapshots/discord-json-2021-01_Discord___Paymentssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Paymentssnapshot.csv new file mode 100644 index 0000000..2c0a2f8 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Paymentssnapshot.csv @@ -0,0 +1,3 @@ +"created_at","description","amount","currency","status" +"2020-04-13T10:09:08.000000+00:00","xxxxxxxxxxxxxxxxxxxx",1111,"xxx",1 +"2020-04-13T10:09:08.000000+00:00","xxxxxxxxxxxxxxxxxxxx",1111,"xxx",1 diff --git a/test/snapshots/discord-json-2021-01_Discord___Relationshipssnapshot.csv b/test/snapshots/discord-json-2021-01_Discord___Relationshipssnapshot.csv new file mode 100644 index 0000000..a42cfd3 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_Discord___Relationshipssnapshot.csv @@ -0,0 +1,3 @@ +"username","discriminator","type" +"xxxxxxxxxxxx","1111",1 +"xxxx","1111",1 diff --git a/test/snapshots/discord-json-2021-01_base_data_manager_metadatasnapshot.csv b/test/snapshots/discord-json-2021-01_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..8148b91 --- /dev/null +++ b/test/snapshots/discord-json-2021-01_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,13 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Discord___Messages_333333333333333333,"""{2}"" at {1}","discord,message,content_by_me","any,isodatetime,text,url",Discord___Messages_Meta +Discord___Messages_222222222222222222,"""{2}"" at {1}","discord,message,content_by_me","any,isodatetime,text,url",Discord___Messages_Meta +Discord___Messages_11111111111111111,"""{2}"" at {1}","discord,message,content_by_me","any,isodatetime,text,url",Discord___Messages_Meta +Discord___Connections,"{0} account ""{1}""",discord,"text,text,any,any,any", +Discord___Relationships,{0}#{1} (relationship type {2}),discord,"text,any,any", +Discord___Payments,{1}: {2} {3} on {0},"discord,payment","isodatetime,text,numeric,text,any", +Discord___Activity_Stats,"App {0}: {2}s played, last at {1}",discord,"any,isodatetime,numeric", +Discord___Notes,"Note on {0}: ""{1}""",discord,"any,text", +Discord___Activity_tns,{0} at {1},"discord,activity","text,isodatetime,any,any,any,text,text,text", +Discord___Activity_reporting,{0} at {1},"discord,activity","text,isodatetime,any,any,any,text,text,text", +Discord___Activity_modeling,{0} at {1},"discord,activity","text,isodatetime,any,any,any,text,text,text", +Discord___Activity_analytics,{0} at {1},"discord,activity","text,isodatetime,any,any,any,text,text,text", diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Album_0_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Album_0_jsonsnapshot.csv new file mode 100644 index 0000000..ace1ee0 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Album_0_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"album","uri","creation_timestamp" +"xxx","photos_and_videos/CoverPhotos_yyyyyy/200x200png.png","2024-03-07T15:23:20Z" +"xxx","photos_and_videos/CoverPhotos_yyyyyy/200x200png.png","2024-07-01T07:46:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Dating_Messages_0_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Dating_Messages_0_jsonsnapshot.csv new file mode 100644 index 0000000..b742d9b --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Dating_Messages_0_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","body" +"Me","xxx","2024-01-13T07:13:20Z","xxx" +"Me","xxx","2024-01-13T07:13:20Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_Metasnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_Metasnapshot.csv new file mode 100644 index 0000000..feed355 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_Metasnapshot.csv @@ -0,0 +1,5 @@ +id,title,is_still_participant,thread_type,thread_path,participants +"Facebook___Messages_randomuser4_xxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser3_xxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser2_xxxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser_xxxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..f4cba1e --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..02d6532 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..02d6532 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..f4cba1e --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_activity_jsonsnapshot.csv new file mode 100644 index 0000000..2304918 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","ip","user_agent","datr_cookie","city","region","country","site_name","timestamp" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-05-01T07:53:20Z" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_status_changes_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_status_changes_jsonsnapshot.csv new file mode 100644 index 0000000..5c555f7 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___account_status_changes_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"status","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-02-13T14:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___accounts_and_profiles_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___accounts_and_profiles_jsonsnapshot.csv new file mode 100644 index 0000000..9950713 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___accounts_and_profiles_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"service_name","native_app_id","username","email","phone_number","name" +"xxx",69,"xxx","not_a_real_email@example.com","xxx","xxx" +"xxx",1707005000,"xxx","not_a_real_email@example.com",,"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___administrative_records_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___administrative_records_jsonsnapshot.csv new file mode 100644 index 0000000..b373fe8 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___administrative_records_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"event","created_timestamp","ip_address","user_agent","datr_cookie" +"xxx","2024-05-01T07:53:20Z",,, +"xxx","2024-02-13T14:36:40Z",,, diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___apps_and_websites_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___apps_and_websites_jsonsnapshot.csv new file mode 100644 index 0000000..aaaceeb --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___apps_and_websites_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","added_timestamp" +"xxx","2024-12-29T08:13:20Z" +"xxx","2024-09-02T12:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___authorized_logins_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___authorized_logins_jsonsnapshot.csv new file mode 100644 index 0000000..19f1932 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___authorized_logins_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"name","created_timestamp","updated_timestamp","ip_address","user_agent","location","app","session_type","datr_cookie" +"xxx","2024-08-22T01:26:40Z","2024-05-11T15:06:40Z","1.1.1.1","some/path","","","","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___comments_jsonsnapshot.csv new file mode 100644 index 0000000..ec79f41 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","data","title" +"2024-02-08T19:20:00Z","TODO","xxx" +"2024-01-17T14:00:00Z","TODO","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___contact_verifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___contact_verifications_jsonsnapshot.csv new file mode 100644 index 0000000..c497c66 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___contact_verifications_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","email","contact_type" +"2024-10-18T07:03:20Z","not_a_real_email@example.com",69 +"2024-01-21T22:10:00Z","not_a_real_email@example.com",69 diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___followers_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___followers_jsonsnapshot.csv new file mode 100644 index 0000000..e8b2d05 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___followers_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name" +"xxx" +"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___following_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___following_jsonsnapshot.csv new file mode 100644 index 0000000..dad0756 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___following_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___friends_jsonsnapshot.csv new file mode 100644 index 0000000..b56d490 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-02-13T13:13:20Z" +"xxx","2024-10-31T00:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___instant_games_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___instant_games_jsonsnapshot.csv new file mode 100644 index 0000000..9dc167d --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___instant_games_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"game","added_timestamp" +"xxx","2024-11-03T16:06:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___items_sold_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___items_sold_jsonsnapshot.csv new file mode 100644 index 0000000..2248995 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___items_sold_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","price","seller","created_timestamp","latitude","longitude","description" +"xxx","xxx","xxx","2024-12-18T05:33:20Z",69,69,"xxx" +"xxx","xxx","xxx","2024-12-18T05:33:20Z",69,69,"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___logins_and_logouts_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___logins_and_logouts_jsonsnapshot.csv new file mode 100644 index 0000000..93709b9 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___logins_and_logouts_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","timestamp","site","ip_address" +"xxx","2024-05-01T07:53:20Z","xxx","1.1.1.1" +"xxx","2024-04-23T17:56:40Z","xxx","1.1.1.1" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___notifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___notifications_jsonsnapshot.csv new file mode 100644 index 0000000..406ab3f --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___notifications_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","unread","href","text" +"2024-04-30T08:16:40Z",true,"url://somewhere","xxx" +"2024-04-30T08:16:40Z",true,"url://somewhere","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pages_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pages_jsonsnapshot.csv new file mode 100644 index 0000000..dad0756 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pages_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___payment_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___payment_history_jsonsnapshot.csv new file mode 100644 index 0000000..9b2b197 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___payment_history_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","amount","currency","type","status","payment_method","created_timestamp" +"xxx","xxx","xxx","xxx","xxx","xxx","xxx","2024-05-05T21:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___people_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___people_jsonsnapshot.csv new file mode 100644 index 0000000..ac5d794 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___people_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","uri","timestamp" +"xxx","url://somewhere","2024-01-15T12:00:00Z" +"xxx","url://somewhere","2024-01-12T06:13:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pokes_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pokes_jsonsnapshot.csv new file mode 100644 index 0000000..3503bc6 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___pokes_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","rank","timestamp" +"xxx","xxx",69,"2024-07-22T19:03:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___posts_and_comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___posts_and_comments_jsonsnapshot.csv new file mode 100644 index 0000000..df23a2d --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___posts_and_comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp","reaction" +,"2024-01-14T06:50:00Z","xxx" +,"2024-01-14T06:50:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___profile_update_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___profile_update_history_jsonsnapshot.csv new file mode 100644 index 0000000..24245d5 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___profile_update_history_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +,"2024-10-06T08:56:40Z" +,"2024-10-06T08:56:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___received_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___received_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..acd88c4 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___received_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-02-08T16:33:20Z" +"xxx","2024-09-24T19:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___rejected_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___rejected_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..ace6522 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___rejected_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-09-27T15:13:20Z" +"xxx","2024-08-24T00:40:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___removed_friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___removed_friends_jsonsnapshot.csv new file mode 100644 index 0000000..50414de --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___removed_friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-01-14T06:50:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___sent_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___sent_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..63dc692 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___sent_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-06-23T05:20:00Z" +"xxx","2024-05-25T08:16:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___story_reactions_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___story_reactions_jsonsnapshot.csv new file mode 100644 index 0000000..46eaac5 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___story_reactions_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-04-28T20:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___support_correspondences_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___support_correspondences_jsonsnapshot.csv new file mode 100644 index 0000000..98d50fb --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___support_correspondences_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","subject","message","timestamp" +"not_a_real_email@example.com","xxx","xxx","xxx","2024-10-16T06:26:40Z" +"xxx","xxx","xxx","url://somewhere","2024-10-16T06:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___unfollowed_pages_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___unfollowed_pages_jsonsnapshot.csv new file mode 100644 index 0000000..94ee1ca --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___unfollowed_pages_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"title","timestamp" +"xxx","2024-12-17T08:43:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_group_membership_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_group_membership_activity_jsonsnapshot.csv new file mode 100644 index 0000000..cb9d578 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_group_membership_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-01-14T06:50:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_off_facebook_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_off_facebook_activity_jsonsnapshot.csv new file mode 100644 index 0000000..de1ccc2 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_off_facebook_activity_jsonsnapshot.csv @@ -0,0 +1,5 @@ +"name","id","type","timestamp" +"xxx",69,"xxx","2024-02-11T12:36:40Z" +"xxx",69,"xxx","2024-02-10T19:56:40Z" +"xxx",69,"xxx","2024-02-10T11:36:40Z" +"xxx",69,"xxx","2024-02-07T21:06:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_pinned_posts_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_pinned_posts_jsonsnapshot.csv new file mode 100644 index 0000000..5d62fd2 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_pinned_posts_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","uri","timestamp" +"xxx","url://somewhere","2024-02-27T05:00:00Z" +"xxx","url://somewhere","2024-05-16T03:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_1_jsonsnapshot.csv new file mode 100644 index 0000000..f30e1ea --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","TODO: data","2024-05-01T07:53:20Z" +"xxx","TODO: data","2024-10-31T06:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv new file mode 100644 index 0000000..d640d01 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","TODO","2024-02-08T19:20:00Z" +"xxx","TODO","2024-02-08T19:20:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_search_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_search_history_jsonsnapshot.csv new file mode 100644 index 0000000..1058823 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_Facebook___your_search_history_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","xxx","2024-11-17T06:30:00Z" +"xxx","xxx","2024-11-17T06:30:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01.zip_base_data_manager_metadatasnapshot.csv b/test/snapshots/facebook-json-2021-05-01.zip_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..362bbfe --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01.zip_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,41 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Facebook___notifications_json,"Notification at {0}: ""{3}""","facebook,initiated_by_third_party","isodatetime,any,url,text", +Facebook___accounts_and_profiles_json,"{0} account ""{2}""",facebook,"text,text,text,text,text,text", +Facebook___your_off_facebook_activity_json,{2} event from {0} at {3},facebook,"text,any,text,isodatetime", +Facebook___apps_and_websites_json,"App ""{0}"" added on {1}",facebook,"text,isodatetime", +Facebook___comments_json,"Comment on ""{2}"" at {0}",facebook,"isodatetime,TODO,text", +Facebook___Dating_Messages_0_json,"""{3}"" from {0} to {1} at {2}","facebook,message,dating,content_by_me","sender,receiver,isodatetime,text", +Facebook___instant_games_json,"Played ""{0}"" starting {1}","facebook,gaming","text,isodatetime", +Facebook___unfollowed_pages_json,"Unfollowed ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebook___following_json,"Followed ""{0}"" at {1}",facebook,"receiver,isodatetime", +Facebook___followers_json,{0} follows you,facebook,sender, +Facebook___sent_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___removed_friends_json,{0} at {1},facebook,"text,isodatetime", +Facebook___rejected_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___received_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___friends_json,{0} at {1},facebook,"text,isodatetime", +Facebook___your_group_membership_activity_json,"Joined group ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebook___your_posts_and_comments_in_groups_json,"Group post ""{0}"" at {2}",facebook,"text,TODO,isodatetime", +Facebook___people_json,Interaction with {0} at {2},facebook,"text,url,isodatetime", +Facebook___pages_json,"Liked page ""{0}"" at {1}",facebook,"text,isodatetime", +Facebook___posts_and_comments_json,"{2} on ""{0}"" at {1}",facebook,"text,isodatetime,text", +Facebook___items_sold_json,"Sold ""{0}"" for {1} on {3}","facebook,marketplace","text,numeric,sender,isodatetime,lat,lng,text", +Facebook___Messages_randomuser4_xxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser3_xxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser2_xxxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser_xxxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___pokes_json,{0} poked {1} at {3},facebook,"sender,receiver,numeric,isodatetime", +Facebook___support_correspondences_json,"""{2}"" from {0} to {1} at {4}",facebook,"sender,receiver,text,text,isodatetime", +Facebook___payment_history_json,{2} {3} from {0} to {1} on {7},"facebook,payment","sender,receiver,numeric,text,text,text,text,isodatetime", +Facebook___Album_0_json,"Photo in ""{0}"" at {2}","facebook,photo","text,url,isodatetime", +Facebook___your_pinned_posts_json,"Pinned post ""{0}"" at {2}",facebook,"text,url,isodatetime", +Facebook___your_posts_1_json,"Post ""{0}"" at {2}",facebook,"text,TODO,isodatetime", +Facebook___profile_update_history_json,"Profile update ""{0}"" at {1}",facebook,"text,isodatetime", +Facebook___your_search_history_json,"Searched for ""{1}"" at {2}","facebook,initiated_by_me,content_by_me","text,text,isodatetime", +Facebook___account_status_changes_json,Account {0} at {1},"facebook,security","text,isodatetime", +Facebook___account_activity_json,"{0} from {4}, {6} on {8}","facebook,security","text,text,text,text,text,text,text,text,isodatetime", +Facebook___administrative_records_json,{0} at {1} from {2},"facebook,security","text,isodatetime,text,text,text", +Facebook___authorized_logins_json,"Session ""{0}"" from {5} on {1}","facebook,security","text,isodatetime,isodatetime,text,text,text,text,text,text", +Facebook___contact_verifications_json,{2} verification of {1} at {0},"facebook,security","isodatetime,text,text", +Facebook___logins_and_logouts_json,{0} on {2} at {1} from {3},"facebook,security","text,isodatetime,text,text", +Facebook___story_reactions_json,"Story reaction on ""{0}"" at {1}",facebook,"text,isodatetime", diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Album_0_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Album_0_jsonsnapshot.csv new file mode 100644 index 0000000..ace1ee0 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Album_0_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"album","uri","creation_timestamp" +"xxx","photos_and_videos/CoverPhotos_yyyyyy/200x200png.png","2024-03-07T15:23:20Z" +"xxx","photos_and_videos/CoverPhotos_yyyyyy/200x200png.png","2024-07-01T07:46:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Dating_Messages_0_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Dating_Messages_0_jsonsnapshot.csv new file mode 100644 index 0000000..b742d9b --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Dating_Messages_0_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","body" +"Me","xxx","2024-01-13T07:13:20Z","xxx" +"Me","xxx","2024-01-13T07:13:20Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_Metasnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_Metasnapshot.csv new file mode 100644 index 0000000..feed355 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_Metasnapshot.csv @@ -0,0 +1,5 @@ +id,title,is_still_participant,thread_type,thread_path,participants +"Facebook___Messages_randomuser4_xxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser3_xxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser2_xxxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" +"Facebook___Messages_randomuser_xxxxxxxx___message_1_json","xxx",true,"xxx","some/path","xxx, xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..f4cba1e --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser2_xxxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..02d6532 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser3_xxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..02d6532 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser4_xxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..f4cba1e --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___Messages_randomuser_xxxxxxxx___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___account_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___account_activity_jsonsnapshot.csv new file mode 100644 index 0000000..2304918 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___account_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","ip","user_agent","datr_cookie","city","region","country","site_name","timestamp" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-05-01T07:53:20Z" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___account_status_changes_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___account_status_changes_jsonsnapshot.csv new file mode 100644 index 0000000..5c555f7 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___account_status_changes_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"status","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-02-13T14:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___accounts_and_profiles_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___accounts_and_profiles_jsonsnapshot.csv new file mode 100644 index 0000000..9950713 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___accounts_and_profiles_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"service_name","native_app_id","username","email","phone_number","name" +"xxx",69,"xxx","not_a_real_email@example.com","xxx","xxx" +"xxx",1707005000,"xxx","not_a_real_email@example.com",,"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___administrative_records_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___administrative_records_jsonsnapshot.csv new file mode 100644 index 0000000..b373fe8 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___administrative_records_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"event","created_timestamp","ip_address","user_agent","datr_cookie" +"xxx","2024-05-01T07:53:20Z",,, +"xxx","2024-02-13T14:36:40Z",,, diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___apps_and_websites_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___apps_and_websites_jsonsnapshot.csv new file mode 100644 index 0000000..aaaceeb --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___apps_and_websites_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","added_timestamp" +"xxx","2024-12-29T08:13:20Z" +"xxx","2024-09-02T12:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___authorized_logins_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___authorized_logins_jsonsnapshot.csv new file mode 100644 index 0000000..19f1932 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___authorized_logins_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"name","created_timestamp","updated_timestamp","ip_address","user_agent","location","app","session_type","datr_cookie" +"xxx","2024-08-22T01:26:40Z","2024-05-11T15:06:40Z","1.1.1.1","some/path","","","","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___comments_jsonsnapshot.csv new file mode 100644 index 0000000..ec79f41 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","data","title" +"2024-02-08T19:20:00Z","TODO","xxx" +"2024-01-17T14:00:00Z","TODO","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___contact_verifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___contact_verifications_jsonsnapshot.csv new file mode 100644 index 0000000..c497c66 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___contact_verifications_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","email","contact_type" +"2024-10-18T07:03:20Z","not_a_real_email@example.com",69 +"2024-01-21T22:10:00Z","not_a_real_email@example.com",69 diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___followers_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___followers_jsonsnapshot.csv new file mode 100644 index 0000000..e8b2d05 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___followers_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name" +"xxx" +"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___following_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___following_jsonsnapshot.csv new file mode 100644 index 0000000..dad0756 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___following_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___friends_jsonsnapshot.csv new file mode 100644 index 0000000..b56d490 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-02-13T13:13:20Z" +"xxx","2024-10-31T00:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___instant_games_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___instant_games_jsonsnapshot.csv new file mode 100644 index 0000000..9dc167d --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___instant_games_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"game","added_timestamp" +"xxx","2024-11-03T16:06:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___items_sold_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___items_sold_jsonsnapshot.csv new file mode 100644 index 0000000..2248995 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___items_sold_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","price","seller","created_timestamp","latitude","longitude","description" +"xxx","xxx","xxx","2024-12-18T05:33:20Z",69,69,"xxx" +"xxx","xxx","xxx","2024-12-18T05:33:20Z",69,69,"xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___logins_and_logouts_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___logins_and_logouts_jsonsnapshot.csv new file mode 100644 index 0000000..93709b9 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___logins_and_logouts_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","timestamp","site","ip_address" +"xxx","2024-05-01T07:53:20Z","xxx","1.1.1.1" +"xxx","2024-04-23T17:56:40Z","xxx","1.1.1.1" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___notifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___notifications_jsonsnapshot.csv new file mode 100644 index 0000000..406ab3f --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___notifications_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","unread","href","text" +"2024-04-30T08:16:40Z",true,"url://somewhere","xxx" +"2024-04-30T08:16:40Z",true,"url://somewhere","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___pages_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___pages_jsonsnapshot.csv new file mode 100644 index 0000000..dad0756 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___pages_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-05-01T07:53:20Z" +"xxx","2024-05-01T07:53:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___payment_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___payment_history_jsonsnapshot.csv new file mode 100644 index 0000000..9b2b197 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___payment_history_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","amount","currency","type","status","payment_method","created_timestamp" +"xxx","xxx","xxx","xxx","xxx","xxx","xxx","2024-05-05T21:36:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___people_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___people_jsonsnapshot.csv new file mode 100644 index 0000000..ac5d794 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___people_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","uri","timestamp" +"xxx","url://somewhere","2024-01-15T12:00:00Z" +"xxx","url://somewhere","2024-01-12T06:13:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___pokes_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___pokes_jsonsnapshot.csv new file mode 100644 index 0000000..3503bc6 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___pokes_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","rank","timestamp" +"xxx","xxx",69,"2024-07-22T19:03:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___posts_and_comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___posts_and_comments_jsonsnapshot.csv new file mode 100644 index 0000000..df23a2d --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___posts_and_comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp","reaction" +,"2024-01-14T06:50:00Z","xxx" +,"2024-01-14T06:50:00Z","xxx" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___profile_update_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___profile_update_history_jsonsnapshot.csv new file mode 100644 index 0000000..24245d5 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___profile_update_history_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +,"2024-10-06T08:56:40Z" +,"2024-10-06T08:56:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___received_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___received_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..acd88c4 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___received_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-02-08T16:33:20Z" +"xxx","2024-09-24T19:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___rejected_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___rejected_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..ace6522 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___rejected_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-09-27T15:13:20Z" +"xxx","2024-08-24T00:40:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___removed_friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___removed_friends_jsonsnapshot.csv new file mode 100644 index 0000000..50414de --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___removed_friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-01-14T06:50:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___sent_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___sent_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..63dc692 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___sent_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-06-23T05:20:00Z" +"xxx","2024-05-25T08:16:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___story_reactions_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___story_reactions_jsonsnapshot.csv new file mode 100644 index 0000000..46eaac5 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___story_reactions_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-04-28T20:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___support_correspondences_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___support_correspondences_jsonsnapshot.csv new file mode 100644 index 0000000..98d50fb --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___support_correspondences_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","subject","message","timestamp" +"not_a_real_email@example.com","xxx","xxx","xxx","2024-10-16T06:26:40Z" +"xxx","xxx","xxx","url://somewhere","2024-10-16T06:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___unfollowed_pages_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___unfollowed_pages_jsonsnapshot.csv new file mode 100644 index 0000000..94ee1ca --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___unfollowed_pages_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"title","timestamp" +"xxx","2024-12-17T08:43:20Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_group_membership_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_group_membership_activity_jsonsnapshot.csv new file mode 100644 index 0000000..cb9d578 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_group_membership_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +"xxx","2024-01-14T06:50:00Z" +"xxx","2024-01-14T06:50:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_off_facebook_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_off_facebook_activity_jsonsnapshot.csv new file mode 100644 index 0000000..de1ccc2 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_off_facebook_activity_jsonsnapshot.csv @@ -0,0 +1,5 @@ +"name","id","type","timestamp" +"xxx",69,"xxx","2024-02-11T12:36:40Z" +"xxx",69,"xxx","2024-02-10T19:56:40Z" +"xxx",69,"xxx","2024-02-10T11:36:40Z" +"xxx",69,"xxx","2024-02-07T21:06:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_pinned_posts_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_pinned_posts_jsonsnapshot.csv new file mode 100644 index 0000000..5d62fd2 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_pinned_posts_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","uri","timestamp" +"xxx","url://somewhere","2024-02-27T05:00:00Z" +"xxx","url://somewhere","2024-05-16T03:26:40Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_1_jsonsnapshot.csv new file mode 100644 index 0000000..f30e1ea --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","TODO: data","2024-05-01T07:53:20Z" +"xxx","TODO: data","2024-10-31T06:10:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv new file mode 100644 index 0000000..d640d01 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_posts_and_comments_in_groups_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","TODO","2024-02-08T19:20:00Z" +"xxx","TODO","2024-02-08T19:20:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_Facebook___your_search_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2021-05-01_Facebook___your_search_history_jsonsnapshot.csv new file mode 100644 index 0000000..1058823 --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_Facebook___your_search_history_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","xxx","2024-11-17T06:30:00Z" +"xxx","xxx","2024-11-17T06:30:00Z" diff --git a/test/snapshots/facebook-json-2021-05-01_base_data_manager_metadatasnapshot.csv b/test/snapshots/facebook-json-2021-05-01_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..362bbfe --- /dev/null +++ b/test/snapshots/facebook-json-2021-05-01_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,41 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Facebook___notifications_json,"Notification at {0}: ""{3}""","facebook,initiated_by_third_party","isodatetime,any,url,text", +Facebook___accounts_and_profiles_json,"{0} account ""{2}""",facebook,"text,text,text,text,text,text", +Facebook___your_off_facebook_activity_json,{2} event from {0} at {3},facebook,"text,any,text,isodatetime", +Facebook___apps_and_websites_json,"App ""{0}"" added on {1}",facebook,"text,isodatetime", +Facebook___comments_json,"Comment on ""{2}"" at {0}",facebook,"isodatetime,TODO,text", +Facebook___Dating_Messages_0_json,"""{3}"" from {0} to {1} at {2}","facebook,message,dating,content_by_me","sender,receiver,isodatetime,text", +Facebook___instant_games_json,"Played ""{0}"" starting {1}","facebook,gaming","text,isodatetime", +Facebook___unfollowed_pages_json,"Unfollowed ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebook___following_json,"Followed ""{0}"" at {1}",facebook,"receiver,isodatetime", +Facebook___followers_json,{0} follows you,facebook,sender, +Facebook___sent_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___removed_friends_json,{0} at {1},facebook,"text,isodatetime", +Facebook___rejected_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___received_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebook___friends_json,{0} at {1},facebook,"text,isodatetime", +Facebook___your_group_membership_activity_json,"Joined group ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebook___your_posts_and_comments_in_groups_json,"Group post ""{0}"" at {2}",facebook,"text,TODO,isodatetime", +Facebook___people_json,Interaction with {0} at {2},facebook,"text,url,isodatetime", +Facebook___pages_json,"Liked page ""{0}"" at {1}",facebook,"text,isodatetime", +Facebook___posts_and_comments_json,"{2} on ""{0}"" at {1}",facebook,"text,isodatetime,text", +Facebook___items_sold_json,"Sold ""{0}"" for {1} on {3}","facebook,marketplace","text,numeric,sender,isodatetime,lat,lng,text", +Facebook___Messages_randomuser4_xxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser3_xxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser2_xxxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___Messages_randomuser_xxxxxxxx___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebook___pokes_json,{0} poked {1} at {3},facebook,"sender,receiver,numeric,isodatetime", +Facebook___support_correspondences_json,"""{2}"" from {0} to {1} at {4}",facebook,"sender,receiver,text,text,isodatetime", +Facebook___payment_history_json,{2} {3} from {0} to {1} on {7},"facebook,payment","sender,receiver,numeric,text,text,text,text,isodatetime", +Facebook___Album_0_json,"Photo in ""{0}"" at {2}","facebook,photo","text,url,isodatetime", +Facebook___your_pinned_posts_json,"Pinned post ""{0}"" at {2}",facebook,"text,url,isodatetime", +Facebook___your_posts_1_json,"Post ""{0}"" at {2}",facebook,"text,TODO,isodatetime", +Facebook___profile_update_history_json,"Profile update ""{0}"" at {1}",facebook,"text,isodatetime", +Facebook___your_search_history_json,"Searched for ""{1}"" at {2}","facebook,initiated_by_me,content_by_me","text,text,isodatetime", +Facebook___account_status_changes_json,Account {0} at {1},"facebook,security","text,isodatetime", +Facebook___account_activity_json,"{0} from {4}, {6} on {8}","facebook,security","text,text,text,text,text,text,text,text,isodatetime", +Facebook___administrative_records_json,{0} at {1} from {2},"facebook,security","text,isodatetime,text,text,text", +Facebook___authorized_logins_json,"Session ""{0}"" from {5} on {1}","facebook,security","text,isodatetime,isodatetime,text,text,text,text,text,text", +Facebook___contact_verifications_json,{2} verification of {1} at {0},"facebook,security","isodatetime,text,text", +Facebook___logins_and_logouts_json,{0} on {2} at {1} from {3},"facebook,security","text,isodatetime,text,text", +Facebook___story_reactions_json,"Story reaction on ""{0}"" at {1}",facebook,"text,isodatetime", diff --git a/test/snapshots/facebook-json-2025-11-29_Facebook___Messages_Metasnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebook___Messages_Metasnapshot.csv new file mode 100644 index 0000000..8e97087 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebook___Messages_Metasnapshot.csv @@ -0,0 +1,6 @@ +id,title,is_still_participant,thread_type,thread_path,participants +"Facebookv2___Messages_chatname_000000000000000000___message_1_json","xxx",true,,"some/path","xxx, xxx" +"Facebookv2___Messages_chatname_000000000000000___message_1_json","xxx",true,,"some/path","xxx, xxx" +"Facebookv2___Messages_chatname_00000000000000000___message_1_json","xxx",true,,"some/path","xxx, xxx" +"Facebookv2___Messages_archived_threads___chatnametype2_000000000000000_json","xxx",true,,"some/path","xxx, xxx" +"Facebookv2___Messages_chatname_00000000000000000___message_1_json","xxx",true,,"some/path","xxx, xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_archived_threads___chatnametype2_000000000000000_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_archived_threads___chatnametype2_000000000000000_jsonsnapshot.csv new file mode 100644 index 0000000..5f16fbe --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_archived_threads___chatnametype2_000000000000000_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","some/path" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000000___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000000___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..73ef3f9 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000000___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z", +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_00000000000000000___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_00000000000000000___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..f4cba1e --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_00000000000000000___message_1_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z","xxx" +"xxx","","1970-01-01T00:00:00Z","xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000___message_1_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000___message_1_jsonsnapshot.csv new file mode 100644 index 0000000..23514c1 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___Messages_chatname_000000000000000___message_1_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"from","to","timestamp","content" +"xxx","","1970-01-01T00:00:00Z", diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___account_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___account_activity_jsonsnapshot.csv new file mode 100644 index 0000000..5cedcde --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___account_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","ip","user_agent","datr_cookie","city","region","country","site_name","timestamp" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-11-22T10:06:40Z" +"xxx","1.1.1.1","some/path","xxx","xxx","xxx","xxx","xxx","2024-11-21T23:00:00Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___comments_jsonsnapshot.csv new file mode 100644 index 0000000..78ae6ef --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","data","title" +"2024-02-13T02:06:40Z","TODO","xxx" +"2024-07-12T02:06:40Z","TODO","xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___connected_apps_and_websites_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___connected_apps_and_websites_jsonsnapshot.csv new file mode 100644 index 0000000..69089ff --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___connected_apps_and_websites_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","added_timestamp" +"xxx","2024-01-12T00:40:00Z" +"xxx","2024-06-21T17:13:20Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___email_address_verifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___email_address_verifications_jsonsnapshot.csv new file mode 100644 index 0000000..f6b402d --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___email_address_verifications_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"timestamp","email","contact_type" +"2024-02-07T19:43:20Z","not_a_real_email@example.com",69 diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___group_posts_and_comments_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___group_posts_and_comments_jsonsnapshot.csv new file mode 100644 index 0000000..a757fe2 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___group_posts_and_comments_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","TODO","2024-10-06T06:10:00Z" +"xxx","TODO","2024-01-22T16:13:20Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___items_sold_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___items_sold_jsonsnapshot.csv new file mode 100644 index 0000000..b56e056 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___items_sold_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","price","seller","created_timestamp","latitude","longitude","description" +"xxx","xxx","xxx","2024-10-02T23:00:00Z",69,69,"xxx" +"xxx","xxx","xxx","2024-09-27T01:20:00Z",69,69,"xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___logins_and_logouts_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___logins_and_logouts_jsonsnapshot.csv new file mode 100644 index 0000000..1fcb005 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___logins_and_logouts_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"action","timestamp","site","ip_address" +"xxx","2024-08-10T14:26:40Z","xxx","1.1.1.1" +"xxx","2024-08-10T14:26:40Z","xxx","1.1.1.1" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___notifications_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___notifications_jsonsnapshot.csv new file mode 100644 index 0000000..28c8b09 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___notifications_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"timestamp","unread","href","text" +"2024-11-20T12:16:40Z",true,"url://somewhere","xxx" +"2024-11-15T00:20:00Z",true,"url://somewhere","xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___pages_and_profiles_you_ve_unfollowed_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___pages_and_profiles_you_ve_unfollowed_jsonsnapshot.csv new file mode 100644 index 0000000..00115d8 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___pages_and_profiles_you_ve_unfollowed_jsonsnapshot.csv @@ -0,0 +1,2 @@ +"title","timestamp" +"xxx","2024-02-21T03:10:00Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___people_and_friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___people_and_friends_jsonsnapshot.csv new file mode 100644 index 0000000..fd23fe0 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___people_and_friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","uri","timestamp" +"xxx","url://somewhere","2024-09-11T20:03:20Z" +"xxx","url://somewhere","2024-01-20T12:50:00Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___received_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___received_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..eda73f5 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___received_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-09-10T10:43:20Z" +"xxx","2024-09-02T12:26:40Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___record_details_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___record_details_jsonsnapshot.csv new file mode 100644 index 0000000..1f5e646 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___record_details_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"event","created_timestamp","ip_address","user_agent","datr_cookie" +"xxx","2024-08-11T01:33:20Z",,, +"xxx","2024-08-10T14:26:40Z",,, diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___rejected_friend_requests_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___rejected_friend_requests_jsonsnapshot.csv new file mode 100644 index 0000000..ff94252 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___rejected_friend_requests_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-09-01T14:13:20Z" +"xxx","2024-08-12T08:06:40Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___time_spent_on_facebook_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___time_spent_on_facebook_jsonsnapshot.csv new file mode 100644 index 0000000..c57bf7f --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___time_spent_on_facebook_jsonsnapshot.csv @@ -0,0 +1 @@ +"start","end" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___where_you_re_logged_in_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___where_you_re_logged_in_jsonsnapshot.csv new file mode 100644 index 0000000..5a209f9 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___where_you_re_logged_in_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","created_timestamp","updated_timestamp","ip_address","user_agent","location","app","session_type","datr_cookie" +,"2024-04-04T19:46:40Z","2024-11-23T02:46:40Z","1.1.1.1","some/path","xxx","xxx","xxx","xxx" +,"2024-04-05T06:53:20Z","2024-11-22T10:06:40Z","1.1.1.1","some/path","xxx","xxx","xxx","xxx" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_friends_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_friends_jsonsnapshot.csv new file mode 100644 index 0000000..f484cf4 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_friends_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"name","timestamp" +"xxx","2024-04-01T16:46:40Z" +"xxx","2024-09-07T16:03:20Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_group_membership_activity_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_group_membership_activity_jsonsnapshot.csv new file mode 100644 index 0000000..8a4e820 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_group_membership_activity_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","timestamp" +"xxx","2024-02-12T17:46:40Z" +"xxx","2024-02-12T17:46:40Z" diff --git a/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_search_history_jsonsnapshot.csv b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_search_history_jsonsnapshot.csv new file mode 100644 index 0000000..c87d2a4 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_Facebookv2___your_search_history_jsonsnapshot.csv @@ -0,0 +1,3 @@ +"title","data","timestamp" +"xxx","xxx","2024-12-08T09:26:40Z" +"xxx","xxx","2024-12-28T00:16:40Z" diff --git a/test/snapshots/facebook-json-2025-11-29_base_data_manager_metadatasnapshot.csv b/test/snapshots/facebook-json-2025-11-29_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..e0e41f4 --- /dev/null +++ b/test/snapshots/facebook-json-2025-11-29_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,24 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Facebookv2___connected_apps_and_websites_json,"App ""{0}"" added on {1}",facebook,"text,isodatetime", +Facebookv2___comments_json,"Comment on ""{2}"" at {0}",facebook,"isodatetime,TODO,text", +Facebookv2___Messages_chatname_000000000000000000___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebookv2___Messages_chatname_000000000000000___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebookv2___Messages_chatname_00000000000000000___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebookv2___Messages_archived_threads___chatnametype2_000000000000000_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebookv2___Messages_chatname_00000000000000000___message_1_json,"""{3}"" from {0} at {2}","facebook,message","sender,receiver,isodatetime,text",Facebook___Messages_Meta +Facebookv2___time_spent_on_facebook_json,Active from {0} to {1},facebook,"isodatetime,isodatetime", +Facebookv2___your_group_membership_activity_json,"Joined group ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebookv2___group_posts_and_comments_json,"Group post ""{0}"" at {2}",facebook,"text,TODO,isodatetime", +Facebookv2___pages_and_profiles_you_ve_unfollowed_json,"Unfollowed ""{0}"" at {1}","facebook,initiated_by_me","text,isodatetime", +Facebookv2___your_friends_json,{0} at {1},facebook,"text,isodatetime", +Facebookv2___rejected_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebookv2___received_friend_requests_json,{0} at {1},facebook,"text,isodatetime", +Facebookv2___people_and_friends_json,Interaction with {0} at {2},facebook,"text,url,isodatetime", +Facebookv2___your_search_history_json,"Searched for ""{1}"" at {2}","facebook,initiated_by_me,content_by_me","text,text,isodatetime", +Facebookv2___notifications_json,"Notification at {0}: ""{3}""","facebook,initiated_by_third_party","isodatetime,any,url,text", +Facebookv2___account_activity_json,"{0} from {4}, {6} on {8}","facebook,security","text,text,text,text,text,text,text,text,isodatetime", +Facebookv2___record_details_json,{0} at {1} from {2},"facebook,security","text,isodatetime,text,text,text", +Facebookv2___where_you_re_logged_in_json,"Session ""{0}"" from {5} on {1}","facebook,security","text,isodatetime,isodatetime,text,text,text,text,text,text", +Facebookv2___email_address_verifications_json,{2} verification of {1} at {0},"facebook,security","isodatetime,text,text", +Facebookv2___logins_and_logouts_json,{0} on {2} at {1} from {3},"facebook,security","text,isodatetime,text,text", +Facebookv2___items_sold_json,"Sold ""{0}"" for {1} on {3}","facebook,marketplace","text,numeric,sender,isodatetime,lat,lng,text", diff --git a/test/snapshots/fitbit-2026-02_Fitbit___AFib_Enrollmentsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___AFib_Enrollmentsnapshot.csv new file mode 100644 index 0000000..3df8f22 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___AFib_Enrollmentsnapshot.csv @@ -0,0 +1,2 @@ +consented,onboarded,enrolled,last_updated,last_notified +false,false,false,Fri Apr 13 10:09:08 UTC 2020,null \ No newline at end of file diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Account_Access_Eventssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Account_Access_Eventssnapshot.csv new file mode 100644 index 0000000..bb49f1e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Account_Access_Eventssnapshot.csv @@ -0,0 +1,10 @@ +timestamp,event_name,email,location,ip,outcome,reason,application,device_info +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxxxxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,xxxxxxxxxxxxxxxxxxxxxxx,1.1.1.1,xxxxxxx,xxxxxxxxxxxxxx,xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Account_Management_Eventssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Account_Management_Eventssnapshot.csv new file mode 100644 index 0000000..af34107 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Account_Management_Eventssnapshot.csv @@ -0,0 +1,4 @@ +timestamp,event_name,email,location,ip,outcome,reason +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, +Mon Apr 13 10:09:08 UTC 2020,xxxxxxxxxxxxxxxxxxx,not_a_real_email@example.com,,1.1.1.1,xxxxxxx, diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Active_Zone_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Active_Zone_Minutessnapshot.csv new file mode 100644 index 0000000..8780246 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Active_Zone_Minutessnapshot.csv @@ -0,0 +1,21 @@ +date_time,heart_zone_id,total_minutes +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,CARDIO,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 +2020-04-13T10:09,FAT_BURN,1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Activity_Goalssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Activity_Goalssnapshot.csv new file mode 100644 index 0000000..0cdcfbb --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Activity_Goalssnapshot.csv @@ -0,0 +1,13 @@ +type,frequency,target,result,status,is_primary,start_date,end_date,created_on,edited_on +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,11.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,111.1,null,null,false,null,null,null,null +xxxxxxxxxxxxxxxxxxxx,xxxxx,1111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,1.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxxx,11111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxx,xxxxxx,11.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxxxxxxxxxxxx,xxxxxx,11111.1,111.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null +xxxxxxxxxx,xxxxx,1111.1,1.1,xxxxxxxxxxxxxxxxxxxxxxxx,false,2020-04-13,2020-04-13,2020-04-13T10:09:08.000000+00:00,null diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Badgessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Badgessnapshot.csv new file mode 100644 index 0000000..697a792 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Badgessnapshot.csv @@ -0,0 +1,3 @@ +"encodedId","badgeType","value","timesAchieved","dateTime","name","shortName","category" +"xxxxxx","DAILY_STEPS",1111,111,"2020-04-13","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxx","xxxxxxxxxxx" +"xxxxxx","LIFETIME_DISTANCE",1111,1,"2020-04-13","xxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxx","xxxxxxxxxxxxxxxxx" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Caloriessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Caloriessnapshot.csv new file mode 100644 index 0000000..8c772f4 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Caloriessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1.11" +"04/13/20 10:09:08","1.11" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv new file mode 100644 index 0000000..f788a78 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Daily_Readiness_User_Propertiessnapshot.csv @@ -0,0 +1 @@ +property_type,value,last_update diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Daily_SpO2snapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Daily_SpO2snapshot.csv new file mode 100644 index 0000000..2e66913 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Daily_SpO2snapshot.csv @@ -0,0 +1,10 @@ +timestamp,average_value,lower_bound,upper_bound +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,111.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 +2020-04-13T10:09:08Z,11.1,11.1,11.1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Demographic_VO2_Maxsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Demographic_VO2_Maxsnapshot.csv new file mode 100644 index 0000000..96f29d5 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Demographic_VO2_Maxsnapshot.csv @@ -0,0 +1,3 @@ +dateTime,demographicVO2Max,demographicVO2MaxError,filteredDemographicVO2Max,filteredDemographicVO2MaxError +"04/13/20 10:09:08",11.11111,1.1111111111111112,11.11111111111111,0.1111111111111111 +"04/13/20 10:09:08",11.11111111111111,1.1111111111111112,11.11111111111111,0.1111111111111111 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Device_Temperaturesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Device_Temperaturesnapshot.csv new file mode 100644 index 0000000..ce7ead7 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Device_Temperaturesnapshot.csv @@ -0,0 +1 @@ +recorded_time,temperature,sensor_type diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Devicessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Devicessnapshot.csv new file mode 100644 index 0000000..91dfbeb --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Devicessnapshot.csv @@ -0,0 +1,2 @@ +wire_id,device_type,serial_number,enabled,fw_version +a1a1a1a1a1a1,xxxxxxxxx,,false,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Distancesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Distancesnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Distancesnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Email_Auditsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Email_Auditsnapshot.csv new file mode 100644 index 0000000..0ac579c --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Email_Auditsnapshot.csv @@ -0,0 +1,2 @@ +previous_email,change_time,request_id +not_a_real_email@example.com,2020-04-13T10:09:08.000000Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Estimated_Oxygen_Variationsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Estimated_Oxygen_Variationsnapshot.csv new file mode 100644 index 0000000..3370911 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Estimated_Oxygen_Variationsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,Infrared to Red Signal Ratio +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 +04/13/20 10:09:08,1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Exercisessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Exercisessnapshot.csv new file mode 100644 index 0000000..8db9c80 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Exercisessnapshot.csv @@ -0,0 +1,3 @@ +logId,activityName,activityTypeId,averageHeartRate,calories,duration,activeDuration,steps,logType,startTime,hasGps +11111111111,"xxxxxxxxxxxx",1111,111,111,1111111,1111111,"","xxxxxxxxxxxxx","04/13/20 10:09:08",false +11111111111,"xxxx",11111,111,11,1111111,1111111,1111,"xxxxxxxxxxxxx","04/13/20 10:09:08",false diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Minutessnapshot.csv new file mode 100644 index 0000000..4087a1b --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Minutessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,light,moderate,very,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Zone_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Zone_Minutessnapshot.csv new file mode 100644 index 0000000..ab4b025 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Active_Zone_Minutessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone,total minutes,data source +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Activity_Levelsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Activity_Levelsnapshot.csv new file mode 100644 index 0000000..1235454 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Activity_Levelsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,level,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_App_Setting_Datasnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_App_Setting_Datasnapshot.csv new file mode 100644 index 0000000..72f34b5 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_App_Setting_Datasnapshot.csv @@ -0,0 +1,8 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx +2020-04-13 10:09:08+0000,xxxxxxxxxxx,11 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxx,xx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Body_Temperaturesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Body_Temperaturesnapshot.csv new file mode 100644 index 0000000..5ac3ef0 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Body_Temperaturesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,temperature celsius,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Calibration_Statussnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Calibration_Statussnapshot.csv new file mode 100644 index 0000000..ab6a223 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Calibration_Statussnapshot.csv @@ -0,0 +1,3 @@ +feature,remaining_days,calibration_start_date,latest_completion_date,latest_update_date +xxxxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 +xxxxxxxxxxxxxxxxxxxxxx,1,2020-04-13,2020-04-13,2020-04-13 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv new file mode 100644 index 0000000..e3b7f03 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Calories_in_HR_Zonesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,kcal,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,1.11111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Caloriessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Caloriessnapshot.csv new file mode 100644 index 0000000..af53ec5 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Caloriessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,calories,data source +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv new file mode 100644 index 0000000..86d142e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratiosnapshot.csv @@ -0,0 +1,21 @@ +timestamp,ratio,label,data source +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.11111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13,1.1111111111111111,xxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv new file mode 100644 index 0000000..7e38e65 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Load_Observed_Intervalsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,min observed load,max observed load,data source +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.1,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1,11.11111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.1111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.111111111111111,xxxxxxxxxx +2020-04-13,1.111111111111111,11.11111111111111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Loadsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Loadsnapshot.csv new file mode 100644 index 0000000..6cadf11 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Cardio_Loadsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,workout,background,total,data source +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.111111111111111,1.111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.11111111111111111,1.11111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1111111111111111,1.1111111111111111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv new file mode 100644 index 0000000..1d8de24 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Variabilitysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,average heart rate variability milliseconds,non rem heart rate beats per minute,entropy,deep sleep root mean square of successive differences milliseconds,data source +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.111,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,11.1,1.11,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,1.111,11.1,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv new file mode 100644 index 0000000..6b9bf17 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Heart_Rate_Zonessnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart_rate_zone,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv new file mode 100644 index 0000000..15ed566 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Oxygen_Saturationsnapshot.csv @@ -0,0 +1,19 @@ +timestamp,average percentage,lower bound percentage,upper bound percentage,baseline percentage,standard deviation percentage,data source +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,111.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,11.1,11.1,11.1,1.1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Readinesssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Readinesssnapshot.csv new file mode 100644 index 0000000..5613c74 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Readinesssnapshot.csv @@ -0,0 +1,19 @@ +timestamp,score,type,readiness level,sleep readiness,heart rate variability readiness,resting heart rate readiness,data source +2020-04-13,11,xxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,111,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxx,xxxxxxxxxx +2020-04-13,11,xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxxxx,xxxxxxx,xxxxxxxxxx +2020-04-13,11,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxx,xxxx,xxx,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv new file mode 100644 index 0000000..18459a7 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Respiratory_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,breaths per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..6308621 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Daily_Resting_Heart_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_Datasnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_Datasnapshot.csv new file mode 100644 index 0000000..b30edf0 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_Datasnapshot.csv @@ -0,0 +1,4 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxx,xxxxx +2020-04-13 10:09:08+0000,xxx,xxxxxx +2020-04-13 10:09:08+0000,xxxxxxxx,some/path diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv new file mode 100644 index 0000000..e94ce5c --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Demographic_VO2_Maxsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,demographic vo2max,data source +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx +2020-04-13T10:09:08Z,11.11111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Distancesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Distancesnapshot.csv new file mode 100644 index 0000000..411ae14 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Distancesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,distance,data source +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,1.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx +2020-04-13T10:09:08Z,11.11,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Exercisessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Exercisessnapshot.csv new file mode 100644 index 0000000..0bc11e0 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Exercisessnapshot.csv @@ -0,0 +1,21 @@ +exercise_id,exercise_start,exercise_end,utc_offset,exercise_created,exercise_last_updated,activity_name,log_type,pool_length,pool_length_unit,intervals,distance_units,tracker_total_calories,tracker_total_steps,tracker_total_distance_mm,tracker_total_altitude_mm,tracker_avg_heart_rate,tracker_peak_heart_rate,tracker_avg_pace_mm_per_second,tracker_avg_speed_mm_per_second,tracker_peak_speed_mm_per_second,tracker_auto_stride_run_mm,tracker_auto_stride_walk_mm,tracker_swim_lengths,tracker_pool_length,tracker_pool_length_unit,tracker_cardio_load,manually_logged_total_calories,manually_logged_total_steps,manually_logged_total_distance_mm,manually_logged_pool_length,manually_logged_pool_length_unit,events,activity_type_probabilities,autodetected_confirmed,autodetected_start_timestamp,autodetected_end_timestamp,autodetected_utc_offset,autodetected_activity_name,autodetected_sensor_based_activity_name,deletion_reason,activity_label,suggested_start_timestamp,suggested_end_timestamp,reconciliation_status +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, +1111111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxx,xxxxxxxxxxxxx,1,xxxxxxxxxxx,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,xxxxxxxxxxx,,,, diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Goal_Settings_Historysnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Goal_Settings_Historysnapshot.csv new file mode 100644 index 0000000..1ec3636 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Goal_Settings_Historysnapshot.csv @@ -0,0 +1,21 @@ +name,objectives,schedule,status,update_time,meta,title,subtitle,rationale,domain,progress_start_time,progress_end_time,progress +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxx,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, +xxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,2020-04-13 10:09:08 - 2020-04-13 10:09:08,xxxxxxx,2020-04-13 10:09:08+0000,,,,,xxxxx,,, diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv new file mode 100644 index 0000000..5c11870 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Rate_Variabilitysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,root mean square of successive differences milliseconds,standard deviation milliseconds,data source +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Ratesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..ed5bfe9 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heart_Ratesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,beats per minute,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Heightsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heightsnapshot.csv new file mode 100644 index 0000000..83c4312 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Heightsnapshot.csv @@ -0,0 +1,9 @@ +timestamp,height millimeters,data source +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx +2020-04-13T10:09:08Z,1111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_IRN_User_Statesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_IRN_User_Statesnapshot.csv new file mode 100644 index 0000000..24f272f --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_IRN_User_Statesnapshot.csv @@ -0,0 +1,2 @@ +enrollment_state,last_processed_time,last_conclusive_window,last_processed_timestamps,last_notified_time +xxxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Legacy_Setting_Datasnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Legacy_Setting_Datasnapshot.csv new file mode 100644 index 0000000..e61031f --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Legacy_Setting_Datasnapshot.csv @@ -0,0 +1,5 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxx,some/path +2020-04-13 10:09:08+0000,xxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Live_Pacesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Live_Pacesnapshot.csv new file mode 100644 index 0000000..24f88b0 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Live_Pacesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,steps,distance millimeters,altitude gain millimeters,data source +2020-04-13T10:09:08Z,1,1,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx +2020-04-13T10:09:08Z,1,1111,1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_MBD_Datasnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_MBD_Datasnapshot.csv new file mode 100644 index 0000000..812762e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_MBD_Datasnapshot.csv @@ -0,0 +1,12 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxx,xxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxx,false +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxx,xxxxxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1111 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,1 +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxx,2020-04-13T10:09:08.000000Z +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Oxygen_Saturationsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Oxygen_Saturationsnapshot.csv new file mode 100644 index 0000000..b4c0b2a --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Oxygen_Saturationsnapshot.csv @@ -0,0 +1,21 @@ +timestamp,oxygen saturation percentage,data source +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx +2020-04-13T10:09:08Z,11.1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Profile_Datasnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Profile_Datasnapshot.csv new file mode 100644 index 0000000..626c2b8 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Profile_Datasnapshot.csv @@ -0,0 +1,2 @@ +value_time,setting_name,setting_value +2020-04-13 10:09:08+0000,xxxxxxxxxxxxxxxxxxxxxxx,xxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv new file mode 100644 index 0000000..4df7fb0 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Respiratory_Rate_Sleep_Summarysnapshot.csv @@ -0,0 +1,21 @@ +timestamp,deep sleep stats - milli breaths per minute,deep sleep stats - standard deviation milli breaths per minute,deep sleep stats - signal to noise,light sleep stats - milli breaths per minute,light sleep stats - standard deviation milli breaths per minute,light sleep stats - signal to noise,rem sleep stats - milli breaths per minute,rem sleep stats - standard deviation milli breaths per minute,rem sleep stats - signal to noise,full sleep stats - milli breaths per minute,full sleep stats - standard deviation milli breaths per minute,full sleep stats - signal to noise,data source +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,11.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,11.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,1.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,1.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx +2020-04-13T10:09:08Z,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,11.1,1.1,1.1,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Sedentary_Periodsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sedentary_Periodsnapshot.csv new file mode 100644 index 0000000..b0f8b9b --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sedentary_Periodsnapshot.csv @@ -0,0 +1,3 @@ +start time,end time,data source +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx +2020-04-13T10:09:08Z,2020-04-13T10:09:08Z,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Scoressnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Scoressnapshot.csv new file mode 100644 index 0000000..8a7935a --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Scoressnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_score_id,data_source,score_utc_offset,score_time,overall_score,duration_score,composition_score,revitalization_score,sleep_time_minutes,deep_sleep_minutes,rem_sleep_percent,resting_heart_rate,sleep_goal_minutes,waso_count_long_wakes,waso_count_all_wake_time,restlessness_normalized,hr_below_resting_hr,sleep_score_created,sleep_score_last_updated +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.11111111111111,-1,-1,-1,111,11,11.11111111111111,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,1.1111111111111111,11,111,11.1,11,1.111111111111111111,1.111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,11111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.11111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,111,11.11,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,1,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1.1111111111111111,11,1.1111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.111111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,1,1,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,11.111111111111111,11,1.1111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.111,-1,-1,-1,111,11,11.11,11,111,1.1111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,+00:00,2020-04-13 10:09:08+0000,11.11,-1,-1,-1,111,11,11.11,11,111,11.1,11,1.111111111111111111,1.1111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxxxx,-00:00,2020-04-13 10:09:08+0000,11.1111111111111,-1,-1,-1,111,11,11.111111111111111,11,111,11.111111111111111,11,1.111111111111111111,1.11111111111111111,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Stagessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Stagessnapshot.csv new file mode 100644 index 0000000..6537327 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleep_Stagessnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_stage_id,sleep_stage_type,start_utc_offset,sleep_stage_start,end_utc_offset,sleep_stage_end,data_source,sleep_stage_created,sleep_stage_last_updated +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxxxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,1111111111111111111,xxx,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleepssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleepssnapshot.csv new file mode 100644 index 0000000..96ad74e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Sleepssnapshot.csv @@ -0,0 +1,21 @@ +sleep_id,sleep_type,minutes_in_sleep_period,minutes_after_wake_up,minutes_to_fall_asleep,minutes_asleep,minutes_awake,minutes_longest_awakening,minutes_to_persistent_sleep,start_utc_offset,sleep_start,end_utc_offset,sleep_end,data_source,sleep_created,sleep_last_updated +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,111,1,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,111,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,11,1,1,-00:00,2020-04-13 10:09:08+0000,-00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxxx,11,1,1,11,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 +1111111111111111111,xxxxxx,111,1,1,111,1,1,1,+00:00,2020-04-13 10:09:08+0000,+00:00,2020-04-13 10:09:08+0000,xxxxxxx,2020-04-13 10:09:08+0000,2020-04-13 10:09:08+0000 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Stepssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Stepssnapshot.csv new file mode 100644 index 0000000..6b681d5 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Stepssnapshot.csv @@ -0,0 +1,21 @@ +timestamp,steps,data source +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,11,xxxxxxxxx +2020-04-13T10:09:08Z,1,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Swim_Lengthssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Swim_Lengthssnapshot.csv new file mode 100644 index 0000000..6a6760f --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Swim_Lengthssnapshot.csv @@ -0,0 +1,21 @@ +timestamp,lap time,stroke count,stroke type,data source +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,11,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxxxxxx,1,xxxxxxxxxxxxxxxxxxxxxxxxxxxx,xxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Time_in_HR_Zonesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Time_in_HR_Zonesnapshot.csv new file mode 100644 index 0000000..d535eab --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Time_in_HR_Zonesnapshot.csv @@ -0,0 +1,21 @@ +timestamp,heart rate zone type,data source +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx +2020-04-13T10:09:08Z,xxxxx,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Google_Weightsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Google_Weightsnapshot.csv new file mode 100644 index 0000000..538b3f9 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Google_Weightsnapshot.csv @@ -0,0 +1,2 @@ +timestamp,weight grams,data source +2020-04-13T10:09:08Z,11111,xxxxxxxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Alertssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Alertssnapshot.csv new file mode 100644 index 0000000..196ed56 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Alertssnapshot.csv @@ -0,0 +1 @@ +id,start_timestamp,end_timestamp,type,threshold,value \ No newline at end of file diff --git a/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Profilesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Profilesnapshot.csv new file mode 100644 index 0000000..070b26e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___HR_Notification_Profilesnapshot.csv @@ -0,0 +1 @@ +threshold_high_custom,threshold_low_custom,use_custom_threshold_high,use_custom_threshold_low,alert_high_on,alert_low_on \ No newline at end of file diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Heart_Ratesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Heart_Ratesnapshot.csv new file mode 100644 index 0000000..faee435 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Heart_Ratesnapshot.csv @@ -0,0 +1,3 @@ +dateTime,bpm,confidence +"04/13/20 10:09:08",11,1 +"04/13/20 10:09:08",11,1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Heightsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Heightsnapshot.csv new file mode 100644 index 0000000..2463fbb --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Heightsnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1111" +"04/13/20 10:09:08","1111" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Lightly_Active_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Lightly_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Lightly_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Birth_Controlsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Birth_Controlsnapshot.csv new file mode 100644 index 0000000..06ada91 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Birth_Controlsnapshot.csv @@ -0,0 +1 @@ +birth_control_type,event_date,has_started diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Cyclessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Cyclessnapshot.csv new file mode 100644 index 0000000..75655a4 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Cyclessnapshot.csv @@ -0,0 +1 @@ +id,cycle_start_date,cycle_end_date,ovulation_start_date,ovulation_end_date,ovulation_source,period_start_date,period_end_date,period_source,fertile_start_date,fertile_end_date,fertile_source diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Settingssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Settingssnapshot.csv new file mode 100644 index 0000000..4067b1e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Settingssnapshot.csv @@ -0,0 +1 @@ +pregnancy_history,birth_control_history,avg_period_days,avg_cycle_days diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Symptomssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Symptomssnapshot.csv new file mode 100644 index 0000000..d084e09 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Menstrual_Symptomssnapshot.csv @@ -0,0 +1 @@ +timestamp,fluids,flow,conditions,sex,ovulation_test,cycle_altering_event,mood diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Minute_SpO2snapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Minute_SpO2snapshot.csv new file mode 100644 index 0000000..872efc3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Minute_SpO2snapshot.csv @@ -0,0 +1,21 @@ +timestamp,value +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 +2020-04-13T10:09:08Z,11.1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Moderately_Active_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Moderately_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Moderately_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Profilesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Profilesnapshot.csv new file mode 100644 index 0000000..839424d --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Profilesnapshot.csv @@ -0,0 +1,2 @@ +id,full_name,first_name,last_name,display_name_setting,display_name,username,email_address,date_of_birth,child,country,state,city,timezone,locale,member_since,about_me,start_of_week,sleep_tracking,time_display_format,gender,height,weight,stride_length_walking,stride_length_running,weight_unit,distance_unit,height_unit,water_unit,glucose_unit,swim_unit +xxxxxx,xxxxxxxxxxxxxxxx,xxxxxxxx,xxxxxxx,xxxx,xxxxxxxxxxx,null,not_a_real_email@example.com,2020-04-13,false,null,null,null,some/path,xxxxx,2020-04-13,null,xxxxxx,xxxxxx,xxxxxx,xxxxxx,111.11111111111111,11.1,11.1,111.11111111111111,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx,xxxxx diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Resting_Heart_Ratesnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Resting_Heart_Ratesnapshot.csv new file mode 100644 index 0000000..5c6a9dd --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Resting_Heart_Ratesnapshot.csv @@ -0,0 +1,4 @@ +dateTime,value,error +"04/13/20 10:09:08",11.1111111111111,1.111111111111111 +"04/13/20 10:09:08",11.11111111111111,1.111111111111111 +"04/13/20 00:00:00",0,0 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Retired_Passwordssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Retired_Passwordssnapshot.csv new file mode 100644 index 0000000..8bcaf99 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Retired_Passwordssnapshot.csv @@ -0,0 +1,2 @@ +date_changed,reason +2020-04-13T10:09:08.000000Z,some/path diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Scalessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Scalessnapshot.csv new file mode 100644 index 0000000..2ac02c7 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Scalessnapshot.csv @@ -0,0 +1 @@ +scale_id,short_name,display_bf,display_bf_mass_unit,display_bmi,user_icon_id diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Sedentary_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Sedentary_Minutessnapshot.csv new file mode 100644 index 0000000..2463fbb --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Sedentary_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1111" +"04/13/20 10:09:08","1111" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Sleep_Scoresnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Sleep_Scoresnapshot.csv new file mode 100644 index 0000000..a356f03 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Sleep_Scoresnapshot.csv @@ -0,0 +1,20 @@ +sleep_log_entry_id,timestamp,overall_score,composition_score,revitalization_score,duration_score,deep_sleep_in_minutes,resting_heart_rate,restlessness +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.03000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,111,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.110000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.11000000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 +11111111111,2020-04-13T10:09:08Z,11,,11,,11,11,0.1100000000000000 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Sleepsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Sleepsnapshot.csv new file mode 100644 index 0000000..4e1ee42 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Sleepsnapshot.csv @@ -0,0 +1,5 @@ +logId,dateOfSleep,startTime,endTime,duration,minutesToFallAsleep,minutesAsleep,minutesAwake,minutesAfterWakeup,timeInBed,efficiency,type,infoCode,logType,mainSleep,deepMinutes,wakeMinutes,lightMinutes,remMinutes +11111111111,"2020-03-13","2020-03-13T10:09:08.000","2020-03-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-03-13","2020-03-13T10:09:08.000","2020-03-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-04-13","2020-04-13T10:09:08.000","2020-04-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 +11111111111,"2020-04-13","2020-04-13T10:09:08.000","2020-04-13T10:09:08.000",11111111,1,111,11,1,111,11,"xxxxxx",1,"xxxxxxxxxxxxx","false",11,11,111,11 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Stepssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Stepssnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Stepssnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Stress_Scoresnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Stress_Scoresnapshot.csv new file mode 100644 index 0000000..63a3434 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Stress_Scoresnapshot.csv @@ -0,0 +1 @@ +DATE,UPDATED_AT,STRESS_SCORE,SLEEP_POINTS,MAX_SLEEP_POINTS,RESPONSIVENESS_POINTS,MAX_RESPONSIVENESS_POINTS,EXERTION_POINTS,MAX_EXERTION_POINTS,STATUS,CALCULATION_FAILED diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Swim_Lengthssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Swim_Lengthssnapshot.csv new file mode 100644 index 0000000..9ed897e --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Swim_Lengthssnapshot.csv @@ -0,0 +1,3 @@ +dateTime,lapDurationSec,strokeCount,swimStrokeType,swimAlgorithmType +"04/13/20 10:09:08",11,1,"xxxxxxx","xxxxxx" +"04/13/20 10:09:08",11,1,"xxxxxxx","xxxxxx" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv new file mode 100644 index 0000000..d75f94c --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Time_in_Heart_Rate_Zonessnapshot.csv @@ -0,0 +1,2 @@ +dateTime,BELOW_DEFAULT_ZONE_1,IN_DEFAULT_ZONE_1,IN_DEFAULT_ZONE_2,IN_DEFAULT_ZONE_3 +"04/13/20 10:09:08",111,1,1,1 diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Tracker_Optional_Configurationsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Tracker_Optional_Configurationsnapshot.csv new file mode 100644 index 0000000..f300e0a --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Tracker_Optional_Configurationsnapshot.csv @@ -0,0 +1,2 @@ +tracker_id,enabled_notification_types,on_right_hand,clock_face,enable_inactivity_alerts,last_updated_ia_time,last_reboot_time,payments_enabled,last_successful_wifi_connection_time,last_successful_wifi_connectionipv4addr,last_successful_wifi_connectionipv6addr,last_successful_wifi_connectionssid,live_data_disabled +1111111111,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,false,,false,xxxxxxxxxxxxxxxxxxxxxxxx,,false,,,,,false diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Trackerssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Trackerssnapshot.csv new file mode 100644 index 0000000..f763e9d --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Trackerssnapshot.csv @@ -0,0 +1,2 @@ +tracker_id,date_added,last_sync_date_time,batt_level,hardware_rev,is_display_distance,is_display_calories,is_display_clock,is_display_flower,is_display_elevation,is_display_chatter,is_right_handed,tracker_name,device_type,on_dominant_hand,is_display_active_minutes,clock_face,enable_ancs,is_bonded,is_display_steps,alarm_update_time,is_display_heart_rate,heart_rate_tracking,heart_rate_tracking_update_time,tap_enabled,tap_screen,flick_enabled,flick_screen +1111111111,2020-04-13,xxxxxxxxxxxxxxxxxxxxxxxx,1,11,false,false,false,false,false,false,false,,xxxxxxxxx,false,false,a,false,false,false,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,xxxxxxxxxxxxxxxxxxxxxxxx,false,xxxx,false, diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Very_Active_Minutessnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Very_Active_Minutessnapshot.csv new file mode 100644 index 0000000..66123f3 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Very_Active_Minutessnapshot.csv @@ -0,0 +1,3 @@ +dateTime,value +"04/13/20 10:09:08","1" +"04/13/20 10:09:08","1" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___Weightsnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___Weightsnapshot.csv new file mode 100644 index 0000000..3e21787 --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___Weightsnapshot.csv @@ -0,0 +1,2 @@ +logId,weight,bmi,date,time,source +1111111111111,111.1,11.11,"04/13/20","10:09:08","xxx" diff --git a/test/snapshots/fitbit-2026-02_Fitbit___iOS_App_Notification_Settingssnapshot.csv b/test/snapshots/fitbit-2026-02_Fitbit___iOS_App_Notification_Settingssnapshot.csv new file mode 100644 index 0000000..2d1d7cb --- /dev/null +++ b/test/snapshots/fitbit-2026-02_Fitbit___iOS_App_Notification_Settingssnapshot.csv @@ -0,0 +1 @@ +user_id,tracker_id,mobile_app_name,is_app_enabled,show_partial_message,is_default_message_app,created_on,modified_on diff --git a/test/snapshots/fitbit-2026-02_base_data_manager_metadatasnapshot.csv b/test/snapshots/fitbit-2026-02_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..081db9f --- /dev/null +++ b/test/snapshots/fitbit-2026-02_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,38 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Fitbit___Email_Audit,,fitbit,, +Fitbit___Retired_Passwords,,fitbit,, +Fitbit___AFib_Enrollment,,fitbit,, +Fitbit___HR_Notification_Alerts,,fitbit,, +Fitbit___HR_Notification_Profile,,fitbit,, +Fitbit___Menstrual_Cycles,,fitbit,, +Fitbit___Menstrual_Symptoms,,fitbit,, +Fitbit___Menstrual_Birth_Control,,fitbit,, +Fitbit___Menstrual_Settings,,fitbit,, +Fitbit___Profile,,fitbit,, +Fitbit___Devices,,fitbit,, +Fitbit___Trackers,,fitbit,, +Fitbit___Scales,,fitbit,, +Fitbit___Tracker_Optional_Configuration,,fitbit,, +Fitbit___iOS_App_Notification_Settings,,fitbit,, +Fitbit___Activity_Goals,,fitbit,, +Fitbit___Sleep_Score,,fitbit,, +Fitbit___Badges,,fitbit,"any,text,numeric,numeric,isodatetime,text,text,text", +Fitbit___Stress_Score,,fitbit,, +Fitbit___Google_Calibration_Status,,fitbit,, +Fitbit___Google_Goal_Settings_History,,fitbit,, +Fitbit___Google_IRN_User_State,,fitbit,, +Fitbit___Google_App_Setting_Data,,fitbit,, +Fitbit___Google_Demographic_Data,,fitbit,, +Fitbit___Google_Legacy_Setting_Data,,fitbit,, +Fitbit___Google_MBD_Data,,fitbit,, +Fitbit___Google_Profile_Data,,fitbit,, +Fitbit___Google_Cardio_Acute_Chronic_Workload_Ratio,,fitbit,, +Fitbit___Google_Cardio_Load_Observed_Interval,,fitbit,, +Fitbit___Google_Daily_Heart_Rate_Variability,,fitbit,, +Fitbit___Google_Daily_Heart_Rate_Zones,,fitbit,, +Fitbit___Google_Daily_Readiness,,fitbit,, +Fitbit___Google_Daily_Respiratory_Rate,,fitbit,, +Fitbit___Google_Daily_Resting_Heart_Rate,,fitbit,, +Fitbit___Google_Demographic_VO2_Max,,fitbit,, +Fitbit___Google_Height,,fitbit,, +Fitbit___Google_Weight,,fitbit,, diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Account_Historysnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Account_Historysnapshot.csv new file mode 100644 index 0000000..81ce466 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Account_Historysnapshot.csv @@ -0,0 +1,9 @@ +"change_type","date","detail" +"display_name_change","2020-04-13T10:09:08+00:00","xxxxx" +"display_name_change","","xxxxxx" +"email_change","2020-04-13T10:09:08+00:00","not_a_real_email@example.com" +"password_change","2020-04-13T10:09:08+00:00","" +"password_change","2020-04-13T10:09:08+00:00","" +"linked_to_bitmoji","2020-04-13T10:09:08+00:00","" +"data_download","2020-04-13T10:09:08+00:00","xxxxxxx / not_a_real_email@example.com" +"data_download","2020-04-13T10:09:08+00:00","xxxxxxxxx / not_a_real_email@example.com" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Chat_Historysnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Chat_Historysnapshot.csv new file mode 100644 index 0000000..6ca3901 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Chat_Historysnapshot.csv @@ -0,0 +1,5 @@ +"conversation_with","from","media_type","created","content","is_sender" +"some_friend","xxxxxxxxx","xxxxx","2020-04-13T10:09:08+00:00","","false" +"some_friend","xxxxxxxxx","xxxx","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxx","false" +"some_friend_too","xxxxxxxxxxxxxx","xxxxx","2020-04-13T10:09:08+00:00","","false" +"some_friend_too","xxxxxxxxxxxxx","xxxx","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxxxxxx","false" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Connected_App_Permissionssnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Connected_App_Permissionssnapshot.csv new file mode 100644 index 0000000..0a49dc2 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Connected_App_Permissionssnapshot.csv @@ -0,0 +1,2 @@ +"app","time","type" +"xxxxxxx","2020-04-13T10:09:08+00:00","xxxxxxx" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Email_Campaignssnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Email_Campaignssnapshot.csv new file mode 100644 index 0000000..5db7e01 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Email_Campaignssnapshot.csv @@ -0,0 +1,3 @@ +"campaign","opt_out_status" +"xxxxxxxxxxxxxxxx","xxxxxxxxxxxx" +"xxxxxxxxxxxxxxx","xxxxxxxxxxxx" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Friendssnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Friendssnapshot.csv new file mode 100644 index 0000000..4b6814a --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Friendssnapshot.csv @@ -0,0 +1,13 @@ +"relationship_type","username","display_name","created_at","modified_at","source" +"Friends","xxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxx" +"Friends","xxxxxxxxxxxxxxx","xxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxx" +"Friend Requests Sent","xxxxxxxxxx","xxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxxxxxx" +"Friend Requests Sent","xxxxxxxxx","xxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxxxxxx" +"Blocked Users","xxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxxxxxxxx" +"Blocked Users","xxxxxxxxxxxxxx","xxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Deleted Friends","xxxxxx","xxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Deleted Friends","xxxxxxxxxxxxxxx","xxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Ignored Snapchatters","xxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Ignored Snapchatters","xxxxxxxx","xxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Pending Requests","xxxxxxxxxxxxxxx","xxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" +"Pending Requests","xxxxxxxxxxxxxx","xxxxxxxxxxxxx","2020-04-13T10:09:08+00:00","2020-04-13T10:09:08+00:00","xxxxxxxxxxxxxxxx" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___In_App_Surveyssnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___In_App_Surveyssnapshot.csv new file mode 100644 index 0000000..4aba730 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___In_App_Surveyssnapshot.csv @@ -0,0 +1,5 @@ +"survey","time","question","response" +"Survey 2020/04/12","xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxxx" +"Survey 2020/04/12","xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxx" +"Survey 2020/04/13","xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxx" +"Survey 2020/04/13","xxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","some/path" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Location_Visitssnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Location_Visitssnapshot.csv new file mode 100644 index 0000000..69438b9 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Location_Visitssnapshot.csv @@ -0,0 +1,2 @@ +"time","city","region","postal_code" +"some/path","xxxxxx","xxxxxxxx","11111" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Login_Historysnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Login_Historysnapshot.csv new file mode 100644 index 0000000..c23bad8 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Login_Historysnapshot.csv @@ -0,0 +1,3 @@ +"ip","country","created","status","device" +"1.1.1.1","xx","2020-04-13T10:09:08+00:00","xxxxxxx","some/path" +"1.1.1.1","xx","2020-04-13T10:09:08+00:00","xxxxxxx","some/path" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Spotlightsnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Spotlightsnapshot.csv new file mode 100644 index 0000000..ce69408 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Spotlightsnapshot.csv @@ -0,0 +1,2 @@ +"story_date","story_url","action_type","view_time" +"2020-04-13T10:09:08+00:00","url://somewhere","xxxx","xxxxxxxxxxxxx" diff --git a/test/snapshots/snapchat-2023-11_Snapchat___Terms_Historysnapshot.csv b/test/snapshots/snapchat-2023-11_Snapchat___Terms_Historysnapshot.csv new file mode 100644 index 0000000..69bf844 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_Snapchat___Terms_Historysnapshot.csv @@ -0,0 +1,3 @@ +"version","acceptance_date" +"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00" +"xxxxxxxxxxxxxxxxxxxxxxxxx","2020-04-13T10:09:08+00:00" diff --git a/test/snapshots/snapchat-2023-11_base_data_manager_metadatasnapshot.csv b/test/snapshots/snapchat-2023-11_base_data_manager_metadatasnapshot.csv new file mode 100644 index 0000000..8b4f6e4 --- /dev/null +++ b/test/snapshots/snapchat-2023-11_base_data_manager_metadatasnapshot.csv @@ -0,0 +1,11 @@ +id,perRowDescription,perRowTags,columnMeta,metaId +Snapchat___Login_History,Login from {0} ({1}) on {2},"snapchat,security","text,text,isodatetime,text,text", +Snapchat___Account_History,{0} on {1}: {2},"snapchat,security","text,isodatetime,text", +Snapchat___Friends,{0}: {2} (@{1}) since {3},snapchat,"text,text,text,isodatetime,isodatetime,text", +Snapchat___Chat_History,"""{4}"" from {1} in {0} at {3}","snapchat,message","text,sender,text,isodatetime,text,any", +Snapchat___Location_Visits,"Visited {1}, {2} ({3}) around {0}","snapchat,location","any,text,text,any", +Snapchat___Spotlight,{2} on spotlight at {0},snapchat,"isodatetime,url,text,any", +Snapchat___Terms_History,Accepted terms {0} on {1},snapchat,"text,isodatetime", +Snapchat___Connected_App_Permissions,{2} permission for {0} on {1},snapchat,"text,isodatetime,text", +Snapchat___Email_Campaigns,"Email campaign ""{0}"": {1}",snapchat,"text,text", +Snapchat___In_App_Surveys,"Survey ""{2}"": {3}",snapchat,"text,any,text,text", diff --git a/test/task.ts b/test/task.ts index a87d240..93a8f36 100644 --- a/test/task.ts +++ b/test/task.ts @@ -7,10 +7,8 @@ import { glob as taskGlob, read, cmd, - setId, + assignMeta, verify, - getTSVManifest, - TaskTargetPipelineHelper, } from "../data-export/task.ts"; const THIS_FILE = import.meta.dirname; @@ -19,11 +17,10 @@ const FIXTURE_FILE = nodePath.join(FIXTURE_DIR, 'friends/friends.json'); // -- TaskTarget --------------------------------------------------------------- -test("TaskTarget: constructor initializes path, pipeline, postFns", () => { +test("TaskTarget: constructor initializes path, pipeline", () => { const t = new TaskTarget("/foo/bar"); assert.equal(t.path, "/foo/bar"); assert.deepEqual(t.pipeline, []); - assert.deepEqual(t.postFns, []); }); test("TaskTarget: exists() returns true for a real file", () => { @@ -50,12 +47,12 @@ test("TaskTarget: id throws when no idValue is set", () => { }); test("TaskTarget: id with a string value is safe-ified", () => { - const t = new TaskTarget("/foo").setId("my-id"); + const t = new TaskTarget("/foo").assignMeta({ idValue: "my-id" }); assert.equal(t.id, "my_id"); }); test("TaskTarget: id with a function value is resolved against the target", () => { - const t = new TaskTarget("/foo/bar").setId(tgt => tgt.basename); + const t = new TaskTarget("/foo/bar").assignMeta({ idValue: tgt => tgt.basename }); assert.equal(t.id, "bar"); }); @@ -92,12 +89,17 @@ test("TaskTarget: pushToPipeline throws if read is not the first op", () => { }); test("TaskTarget: clone produces an independent copy", () => { - const t = new TaskTarget("/foo").setId("orig"); + const t = new TaskTarget("/foo").assignMeta({ + idValue: "orig", + columnMeta: ["any"] + }); t.read(); const c = t.clone(); assert.equal(c.path, "/foo"); assert.equal(c.id, "orig"); + assert(c.pipeline !== t.pipeline); // Different object references assert.equal(c.pipeline.length, 1); + assert(c.columnMeta !== t.columnMeta); // Different object references c.path = "/other"; assert.equal(t.path, "/foo"); // original unchanged }); @@ -152,41 +154,41 @@ test("toShell: cmd with function resolves at shell-generation time", () => { // -- module-level functions --------------------------------------------------- -test("cd: clones and changes directory of each target", () => { +test("cd: clones and changes directory of each target", async () => { const targets = [new TaskTarget("/a"), new TaskTarget("/b")]; - const result = cd(targets, "sub"); + const result = await cd("sub")(targets); assert.equal(result[0].path, "/a/sub"); assert.equal(result[1].path, "/b/sub"); assert.equal(targets[0].path, "/a"); // originals unchanged }); -test("read: clones and adds a read op to each target", () => { +test("read: clones and adds a read op to each target", async () => { const targets = [new TaskTarget("/a.txt"), new TaskTarget("/b.txt")]; - const result = read(targets); + const result = await read()(targets); assert.equal(result[0].pipeline[0].type, "read"); assert.equal(result[1].pipeline[0].type, "read"); assert.equal(targets[0].pipeline.length, 0); // originals unchanged }); -test("cmd: clones and appends a cmd op to each target", () => { +test("cmd: clones and appends a cmd op to each target", async () => { const targets = [new TaskTarget("/a.txt")]; targets[0].read(); - const result = cmd(targets, "jq ."); + const result = await cmd("jq .")(targets); assert.equal(result[0].pipeline.length, 2); assert.equal(targets[0].pipeline.length, 1); // original unchanged }); -test("setId: clones and sets id on each target", () => { +test("assignMeta: clones and sets meta on each target", async () => { const targets = [new TaskTarget("/a"), new TaskTarget("/b")]; - const result = setId(targets, "myid"); + const result = await assignMeta({ idValue: "myid" })(targets); assert.equal(result[0].id, "myid"); assert.equal(result[1].id, "myid"); assert.throws(() => targets[0].id); // originals have no id }); -test("taskGlob: returns matching targets across all input targets", () => { +test("taskGlob: returns matching targets across all input targets", async () => { const targets = [new TaskTarget(FIXTURE_DIR)]; - const result = taskGlob(targets, "friends/*.json"); + const result = await taskGlob("friends/*.json")(targets); assert.ok(result.length > 0); assert.ok(result.every(r => r.path.endsWith(".json"))); }); @@ -223,90 +225,3 @@ test("verify: filters a mixed list to only valid targets", async () => { assert.equal(result[0], good); }); -// -- getTSVManifest ----------------------------------------------------------- - -test("getTSVManifest: produces idshell for a single target", () => { - const t = new TaskTarget("/foo/bar.txt"); - t.setId("myid"); - t.read(); - assert.equal(getTSVManifest([t]), "myid\tcat /foo/bar.txt"); -}); - -test("getTSVManifest: joins multiple targets with newlines", () => { - const t1 = new TaskTarget("/a.txt"); t1.setId("a"); t1.read(); - const t2 = new TaskTarget("/b.txt"); t2.setId("b"); t2.read(); - assert.equal(getTSVManifest([t1, t2]), "a\tcat /a.txt\nb\tcat /b.txt"); -}); - -// -- TaskTargetPipelineHelper ------------------------------------------------- - -test("TaskTargetPipelineHelper: pipeline() promotes a plain array", () => { - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/a")]); - assert.ok(p instanceof TaskTargetPipelineHelper); -}); - -test("TaskTargetPipelineHelper: pipeline() is idempotent", () => { - const arr = [new TaskTarget("/a")]; - const p1 = TaskTargetPipelineHelper.pipeline(arr); - const p2 = TaskTargetPipelineHelper.pipeline(p1); - assert.equal(p1, p2); -}); - -test("TaskTargetPipelineHelper: cd returns a new helper with paths changed", () => { - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/a"), new TaskTarget("/b")]); - const p2 = p.cd("sub"); - assert.ok(p2 instanceof TaskTargetPipelineHelper); - assert.equal(p2[0].path, "/a/sub"); - assert.equal(p2[1].path, "/b/sub"); -}); - -test("TaskTargetPipelineHelper: read returns a new helper with read ops added", () => { - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/a.txt")]); - const p2 = p.read(); - assert.ok(p2 instanceof TaskTargetPipelineHelper); - assert.equal(p2[0].pipeline[0].type, "read"); -}); - -test("TaskTargetPipelineHelper: cmd returns a new helper with cmd ops added", () => { - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/a.txt")]); - const p2 = p.read().cmd("jq ."); - assert.equal(p2[0].toShell(), "cat /a.txt | jq ."); -}); - -// -- collect ------------------------------------------------------------------ - -test("collect: the final end of a chain is added to the collection set", () => { - const collection = new Set(); - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/foo")]); - p.collect(collection); - - const p2 = p.cd("sub"); - assert.equal(collection.size, 1); - assert.ok(collection.has(p2)); -}); - -test("collect: moving the chain end removes the old element and adds the new one", () => { - const collection = new Set(); - const p = TaskTargetPipelineHelper.pipeline([new TaskTarget("/foo")]); - p.collect(collection); - - const p2 = p.cd("sub"); - const p3 = p2.read(); - assert.equal(collection.size, 1); - assert.ok(collection.has(p3)); - assert.ok(!collection.has(p2)); -}); - -test("collect: gathers the ends of multiple independent pipeline branches", () => { - const collection = new Set(); - - const b1 = TaskTargetPipelineHelper.pipeline([new TaskTarget("/a.txt")]).collect(collection).read(); - const b2 = TaskTargetPipelineHelper.pipeline([new TaskTarget("/b.txt")]).collect(collection).read(); - - assert.equal(collection.size, 2); - assert.ok(collection.has(b1)); - assert.ok(collection.has(b2)); - - const allTargets = [...collection].flat(); - assert.equal(allTargets.length, 2); -}); diff --git a/test/utils/csvUtils.ts b/test/utils/csvUtils.ts new file mode 100644 index 0000000..7c20bd2 --- /dev/null +++ b/test/utils/csvUtils.ts @@ -0,0 +1,154 @@ +import { strict as assert } from "node:assert"; +import { type TestContextAssert } from "node:test"; +import { parse } from "csv-parse/sync"; + +function formatCSVForSnapshot(id: string, csv: string) { + return `# === ${id} ===\n${csv}`; +} + +/**Custom serializer options for id + csv tuples. The default node:test snapshot serialization + * results in CLI output that looks like the following + * ``` + * '\n[\n "\\\\"album\\\\",\\\\"uri\\\\"...' + * ``` + * which is nearly useless to try to find what went wrong in + * So instead we output the plain csvs + id in a flatter, plainer serialized format + * to compare against*/ +export function idAndCSVsSnapshotOpts(idAndCSVs: [string, string][]): Parameters { + function idAndCSVsSnapshotSerializer(idAndCSVs: [string, string][]) { + return idAndCSVs.map((idAndCSV)=>formatCSVForSnapshot(...idAndCSV)); + } + // Keep stable ordering for snapshots + idAndCSVs.sort(); + return [idAndCSVs, { + serializers: [idAndCSVsSnapshotSerializer] + }]; +} + +/**Scores CSV rows on whether or not we can determine if it has headers + * In this case + * ``` + * score < 0 - First row does follow observed patterns in rows [1,rowsToSample), most likely does not have headers + * score === 0 - Header + * score > 0 - First row does NOT follow patterns observed in rows [1,rowsToSample), most likely has headers + * ``` + * Compare the output like `> 0` or `>= 0` depending on your needs + * + * The theory here comes from Python's implementation of has_headers which + * does a similar thing + * https://github.com/python/cpython/blob/main/Lib/csv.py#L453 + * + * Scan over dataRows (every row after mightBeHeader) and collect the pattern of + * the length of the values in the column as well as the type of the values + * in that column. + * If the mightBeHeader has the same type as the dataRows and is not a string + * there's a good chance it's a header. Same if all the dataRows have the + * same string length but the header has a different string length + */ +function getHasHeaderScore(rows: string[][], rowsToSample = 20): number { + const mightBeHeader = rows[0]; + const dataRows = rows + .slice(1) // Remove header + .slice(0, rowsToSample); // Select only the first rowsToSample rows + + function typeFromValue(v: string) { + const maybeNum = Number(v); + if (!isNaN(maybeNum)) { + return "number" as const; + } + return "string" as const; + } + + interface ColumnInfo { + type?: "number" | "string"; + length?: number; + } + function deriveColumnInfoFromValue(v: string) { + return { + type: typeFromValue(v), + length: v.length + }; + } + function combineColumnInfo(a: ColumnInfo | undefined, b: ColumnInfo) { + if (!a) { + // Don't have a previous value yet + return b; + } + + // Combine each piece of info, if it differs + return { + type: a.type === b.type ? a.type : undefined, + length: a.length === b.length ? a.length : undefined + }; + } + function scoreColumnInfo(mightBeHeader: ColumnInfo, dataRow?: ColumnInfo) { + let typeScore = 0; + if (dataRow?.type !== undefined && dataRow.type !== "string") { + typeScore = dataRow.type !== mightBeHeader.type ? 1 : -1; + } + let lengthScore = 0; + if (dataRow?.length !== undefined) { + lengthScore = dataRow.length !== mightBeHeader.length ? 1 : -1; + } + return typeScore + lengthScore; + } + + // Maps column index to the ColumnInfo derived for that row + const colInfos: (ColumnInfo | undefined)[] = []; + // For every sampled row, collect the pattern info across the columns + for (const row of dataRows) { + for (const [colIdx, value] of row.entries()) { + const maybeColInfo = colInfos[colIdx]; + const newColInfo = deriveColumnInfoFromValue(value); + colInfos[colIdx] = combineColumnInfo(maybeColInfo, newColInfo); + } + } + + // Score headers for differences from the above observed patterns + let score = 0; + for (const [idx, headerValue] of mightBeHeader.entries()) { + const headerColInfo = deriveColumnInfoFromValue(headerValue); + const maybeDataRowColInfo = colInfos[idx]; + score += scoreColumnInfo(headerColInfo, maybeDataRowColInfo); + } + + return score; +} +function hasHeader(rows: string[][], rowsToSample = 20): boolean { + return getHasHeaderScore(rows, rowsToSample) > 0; +} + +// Inline test +assert(hasHeader([["name", "place", "count"], ["who", "where", "2"], ["some", "one", "5"]]) === true, "Inline hasHeader unit-test 1"); +assert(hasHeader([["bingus_column", "nothing", "nothing"], ["bingus", "ggg", "hhhhh"], ["bingus", "ffff", "aaaaaaa"]]) === true, "Inline hasHeader unit-test 2"); +assert(hasHeader([["not", "a", "header"], ["marco", "polo", "afafaf"], ["g", "f", "a"]]) === false, "Inline hasHeader unit-test 3"); + +/**Makes sure the csv passed follows a set of guidelines*/ +export function assertCSVWellFormed(csv: string, msg?: string) { + // ends in a newline + // TODO: Fix these, fitbit export apparently fails both of these :( + //assert(csv[csv.length - 1] === "\n", `${msg} CSV must end in a new line`); + //assert(!csv.includes("\r"), `${msg} CSV included carriage returns, but we dont want those in our output`); + + // This throws if: + // * it finds mismatching lengths of rows + // * ... others, see below + // Also see https://csv.js.org/parse/errors/#runtime-errors + const rows = parse(csv, { + record_delimiter: '\n', // Default is autodiscovery, but we only want '\n' + + // Explicitly define these even though they're the default. This is what we + // want to cause a throw if the csv comes in poorly + relax_column_count: false, + relax_quotes: false, + skip_records_with_error: false, + skip_empty_lines: false, + skip_records_with_empty_values: false + }); + + assert(rows.length > 0, `${msg} CSV had no rows`); + // Use >= 0 here so if it's ambiguous we just let it pass, some of the tables + // we output done have any "observable" patterns w.r.t how getHasHeaderScore() + // works + assert(getHasHeaderScore(rows) >= 0, `${msg} CSVs should have headers`); +} diff --git a/test/utils/general.ts b/test/utils/general.ts new file mode 100644 index 0000000..e8d2d73 --- /dev/null +++ b/test/utils/general.ts @@ -0,0 +1,38 @@ +import { diffLines } from "diff"; +import { strict as assert } from "node:assert"; + +function color(text: string, c: "red" | "green") { + const codes = { red: '\x1b[31m', green: '\x1b[32m' }; + return `${codes[c]}${text}\x1b[0m`; +} + +/**Asserts two strings are equal and diffs them in the assertion error if they are + * not*/ +export function assertStringEq(actual: string, expected: string, msg: string) { + if (actual === expected) { + return; + } + const diff = diffLines(actual, expected); + const assertionMsg = `${msg}\n` + diff + .map(part => { + if (!part.added && !part.removed) { + return part.value; + } + const prefix = part.added ? "+" : "-"; + return color(`${prefix}${part.value}`, part.added ? "green" : "red"); + }) + .join(""); + assert(actual === expected, assertionMsg); +} + +/**Catches any p Promise throws and instead returns those in a tuple*/ +export async function ptry( + p: Promise +): Promise<[TError, undefined] | [undefined, TRet]> { + try { + const result = await p; + return [undefined, result]; + } catch (err) { + return [err as TError, undefined]; + } +} \ No newline at end of file diff --git a/timelinize.ts b/timelinize.ts new file mode 100644 index 0000000..a0dc95f --- /dev/null +++ b/timelinize.ts @@ -0,0 +1,225 @@ +import { type SQLOutputValue, type DatabaseSync } from "node:sqlite"; +import { createWriteStream } from 'node:fs'; +import { fileURLToPath } from "node:url"; +import "./data-export/facebook.ts"; +import { facebook } from "./data-export/facebook.ts"; +import { execPaths, COLUMN_TYPES } from "./data-export/task.ts"; +import * as DataIO from "./data-export/io.ts"; +import { + startTime, + elapsed, + loadTaskInNewDb +} from "./main.ts"; + +const __filename = fileURLToPath(import.meta.url); + +function dumpDBTableToCSV(db: DatabaseSync, tableName: string, outputFile: string) { + const stream = createWriteStream(outputFile); + const stmt = db.prepare(`SELECT * FROM ${tableName}`); + + let headerWritten = false; + for (const row of stmt.iterate()) { + if (!headerWritten) { + stream.write(Object.keys(row).join(',') + '\n'); + headerWritten = true; + } + stream.write(Object.values(row).map(v => `"${String(v ?? '').replace(/"/g, '""')}"`).join(',') + '\n'); + } + + stream.end(); +} +function getColumnNames(db: DatabaseSync, tableName: string) { + return db.prepare(`PRAGMA table_info(${tableName})`).all().map(c => c.name) as string[]; +} +function templateToSql(template: string, columns: string[]) { + // Convert '{0}, {1}' to '%s, %s' + const args: string[] = []; + const sqlTemplate = template.replace(/\{(\d+)\}/g, (match, index) => { + args.push(columns[parseInt(index)]); + return '%s'; + }); + return `printf('${sqlTemplate}', ${args.join(', ')})`; +} +function sqlLiteral(str: string | undefined | null): string { + if (str === null || str === undefined) { + return 'NULL'; + } + + // Escape single quotes by doubling them + const escaped = str.replace(/'/g, "''"); + + // Wrap in single quotes + return `'${escaped}'`; +} + +async function main() { + // Configure the tasks to run + console.log(`${elapsed()} - Building targets`); + const targets = await execPaths([ + {path: "/home/cobertos/Seafile/archive/ExportedServiceData/facebook/formapcast_facebook-DEADNAME-May2021-json", op: facebook()} + ]); + console.log(`${elapsed()} - Found ${targets.filter(t => !t.aggregate).length} possible targets`); + const db = await loadTaskInNewDb(targets); + + // New output tables + db.exec(`CREATE TABLE combined (timestamp TEXT, description TEXT, type TEXT, sender TEXT, receiver TEXT, lat REAL, lng REAL, tags TEXT);`); + + //(message, email, note, + // social, location, media, event, document, + // bookmark; defaults to note) + + type ColumnMetaType = (keyof typeof COLUMN_TYPES); + interface MetadataRow { + id: string, + perRowDescription?: string, + perRowTags?: string, + columnMeta: ColumnMetaType[], + columnNames: string[], + metaId?: string + } + function verifyMetdataRow(input: Record): undefined | MetadataRow { + const { id, perRowDescription, perRowTags, columnMeta: columnMetaCSV, metaId } = input; + if (!id) { + console.error("Row did not have id/tableName, skipping"); + return undefined; + } + if (typeof id !== "string") { + console.error(`Id must be string, got ${typeof id}, ${id}`); + return undefined; + } + if (!columnMetaCSV) { + console.warn(`${id} did not have columnMeta, nothing to do. Skipping`); + return undefined; // No column information + } + if (typeof columnMetaCSV !== "string") { + console.warn(`${id} did not have columnMeta of type string. Skipping`); + return undefined; + } + const columnMeta = columnMetaCSV.split(",") as ColumnMetaType[]; + + // Get the column names from the table id + const columnNames = getColumnNames(db, id); + if (columnNames.length !== columnMeta.length) { + console.error(`columnNames and columnMeta did not have same length. skipping`); + return undefined; + } + + if (typeof perRowDescription !== "string" && perRowDescription !== undefined && perRowDescription !== null) { + console.warn(`Invalid typeof perRowDescription, was ${typeof perRowDescription}, value ${perRowDescription}`); + return undefined; + } + if (typeof perRowTags !== "string" && perRowTags !== undefined && perRowTags !== null) { + console.warn(`Invalid typeof perRowTags, was ${typeof perRowTags}, value ${perRowTags}`); + return undefined; + } + if (typeof metaId !== "string" && metaId !== undefined && metaId !== null) { + console.warn(`Invalid typeof metaId, was ${typeof metaId}, value ${metaId}`); + return undefined; + } + + return { + id, + perRowDescription: perRowDescription ?? undefined, + perRowTags: perRowTags ?? undefined, + columnMeta, + columnNames, + metaId: metaId ?? undefined + }; + } + + /**Maps columnMeta names to the column names*/ + function metaToNames(meta: MetadataRow): Partial> { + const out: Partial> = {}; + for (const [idx, name] of meta.columnNames.entries()) { + const metaName = meta.columnMeta[idx]; + if (out[metaName]) { + console.warn(`Duplicate column with metaName "${metaName}". The current one which will be used is "${out[metaName]}". Skipping the duplicate.`); + continue; + } + out[metaName] = name; + } + return out; + } + function metaParts(metaNameToColumnName: Partial>): Record { + const out: Record = {} as any; + for (const type of Object.keys(COLUMN_TYPES) as ColumnMetaType[]) { + if (!metaNameToColumnName[type]) { + out[type] = "NULL"; + continue; + } + // Wrap in brackets so column names like "from" don't cause any issues + out[type] = `[${metaNameToColumnName[type]}]` + } + return out; + } + + // Iterate over all the tables and their metadata + const statement = db.prepare(`SELECT id, perRowDescription, perRowTags, columnMeta, metaId FROM base_data_manager_metadata`); + for (const row of statement.iterate()) { + const verified = verifyMetdataRow(row); + if (!verified) { + continue; + } + const { id, perRowDescription, perRowTags, columnMeta, columnNames, metaId } = verified; + const metaNameToColumnName = metaToNames(verified); + const part = metaParts(metaNameToColumnName); + + // Now find what to insert into each row of the combined + // Per row tags is an string of csv'd items but needs to be made a literal + // TODO: Make this either a template string or have jq do something + // tagsPart = templateToSqlExpr(target.perRowTags, columnNames); + const tagsPart = sqlLiteral(perRowTags); + + // Choose what to do with this table based on what meta is present + if ( + !!metaNameToColumnName.sender + && !!metaNameToColumnName.isodatetime + ) { + if (!metaId) { + console.warn(`Chat ${id} with .sender but no .metaId. Skipping`); + continue; + } + + // First pull the name of the conversation out of the metaId + const receiverThreadTitle = db.prepare(`SELECT title FROM ${metaId} WHERE (id=${sqlLiteral(id)})`).get()?.title; + if (!receiverThreadTitle || typeof receiverThreadTitle !== "string") { + console.warn(`Chat ${id} with .metaId ${metaId} returned invalid receiverThreadTitle ${typeof receiverThreadTitle}. Skipping`); + continue; + } + const receiverPart = sqlLiteral(receiverThreadTitle); + + // Put this table into the combined table + db.exec(`INSERT INTO combined SELECT ${part.isodatetime}, ${part.text}, 'message', ${part.sender}, ${receiverPart}, ${part.lat}, ${part.lng}, ${tagsPart} FROM ${id};`); + } + else if (!!metaNameToColumnName.isodatetime) { + // Put this table into the combined table + let descriptionPart = perRowDescription + ? templateToSql(perRowDescription, columnNames) + : `'An entry from the ${id} table'`; // Default is just kinda garbo... + db.exec(`INSERT INTO combined SELECT ${part.isodatetime}, ${descriptionPart}, 'node', NULL, NULL, ${part.lat}, ${part.lng}, ${tagsPart} FROM ${id};`); + } + else { + console.warn(`Table with id ${id} had no isodatetime or anything else of value, skipping...`); + } + } + + const count = db.prepare(`SELECT COUNT(*) as count FROM combined`).get()?.count; + console.log(`${elapsed()} - Combined database built with ${count} rows`); + + // Dump it to the disk for debugging + const sqlitePath = "debug_your.csv.db"; + console.log(`${elapsed()} - Writing database to disk at "${sqlitePath}"`); + await DataIO.dumpDBToDisk(db, sqlitePath); + + console.log(`${elapsed()} - Database written to disk`); + + // Dump it all to the path specified + dumpDBTableToCSV(db, "combined", "your.csv"); + console.log(`${elapsed()} - Combined database written to disk as CSV`); + db.close(); +} + +if (process.argv[1] === __filename) { + main(); +} + diff --git a/util/scrub.jq b/util/scrub.jq index 887d02b..53ed108 100644 --- a/util/scrub.jq +++ b/util/scrub.jq @@ -3,46 +3,134 @@ # fd -t f .json -0 | xargs -I % -0 -- jq -f scrub.jq "%" > "%" # (Though you should remove the end `> "%"` first to get just the output without # persisting to be sure it's what you want first) -def scrub: - walk( - if type == "string" then - if test("^(([0-9]{1,3}\\.){3}[0-9]{1,3})$") then - "1.1.1.1" - elif test("^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$") then - "2000:0000:0000:0000:0000:0000:0000:0000" - elif test("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") then - "not_a_real_email@example.com" - elif test("\\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff|mp3|wav|flac|aac|ogg|wma|m4a|mp4|avi|mkv|mov|wmv|flv|webm)$"; "i") then - # Leave these alone, you will have to manually go through these later and replace with - # placeholders - # TODO: jq 1.7 adds debug(), use this instead when I can upgrade jq, otherwise - # you need to manually grep for MANUAL REPAIR NEEDED for now - ("MANUAL REPAIR NEEDED: \(.)" | stderr) | . - elif test("://") then - "url://somewhere" - elif test("/") then - "some/path" - else - "xxx" - end - elif type == "number" then - if 946702800 <= . and . <= 1893474000 then - # Take modulo 1 year to get variance in the output, then add offset to bring to ~2024 - ((((. % 31557600) + 1704067200) / 5000 | floor) * 5000) - else - 69 - end - elif type == "array" then - # Keep only 2 elements, but scrub *those* elements - if length > 1 then - [ (.[0] | scrub), (.[1] | scrub) ] - elif length > 0 then - [ (.[0] | scrub) ] - else - [] - end + +def scrub_key: + if test("^[0-9]+$") then + ("1" * length) + else + . + end; + +def scrub_primitive: + if type == "string" then + if test("^(([0-9]{1,3}\\.){3}[0-9]{1,3})$") then + # IPv4 + "1.1.1.1" + elif test("^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$") then + # IPv6 + "2000:0000:0000:0000:0000:0000:0000:0000" + elif test("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") then + # Email-like + "not_a_real_email@example.com" + elif test("\\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff|mp3|wav|flac|aac|ogg|wma|m4a|mp4|avi|mkv|mov|wmv|flv|webm)$"; "i") then + # Leave these alone, you will have to manually go through these later and replace with + # placeholders + # TODO: jq 1.7 adds debug(), use this instead when I can upgrade jq, otherwise + # you need to manually grep for MANUAL REPAIR NEEDED for now + ("MANUAL REPAIR NEEDED: \(.)" | stderr) | . + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+\\-][0-9]{2}:[0-9]{2}$") then + # iso date time without millis with timezone + "2020-04-13T10:09:08+00:00" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,6})?[+\\-][0-9]{2}:[0-9]{2}$") then + # iso date time with millis with timezone + "2020-04-13T10:09:08.000000+00:00" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,6})?$") then + # iso date time with millis no timezone + "2020-04-13T10:09:08.000" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$") then + # iso date time with z and the end (from fitbit export) + "2020-04-13T10:09:08Z" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{1,6}Z$") then + # iso date time with z and the end and millis (from fitbit export) + "2020-04-13T10:09:08.000000Z" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$") then + # iso date time but no seconds (from fitbit export) + "2020-04-13T10:09" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} - [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$") then + # iso date time range no T (from fitbit export) + "2020-04-13 10:09:08 - 2020-04-13 10:09:08" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,6})?[+\\-][0-9]{4}$") then + # iso date time with millis with timezone no colon (fitbit export) + "2020-04-13 10:09:08+0000" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2}$") then + # Just date + "2020-04-13" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC$") then + # Date format from snapchat export + "2020-04-13 10:09:08 UTC" + elif test("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$") then + # Date format from snapchat export + "2020-04-13 10:09:08" + elif test("^[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$") then + # Date format from fitbit export (wtf ugh) + "04/13/20 10:09:08" + elif test("^\\w{3} \\w{3} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC [0-9]{4}$") then + # Date format from fitbit export (uughhh) + "Mon Apr 13 10:09:08 UTC 2020" + elif test("^\\+[0-9]{2}:[0-2]{2}$") then + # UTC offset (from fitbit export) + "+00:00" + elif test("^-[0-9]{2}:[0-2]{2}$") then + # negative UTC offset (from fitbit export) + "-00:00" + elif test("^[0-9]+$") then + # preserve length of the string + "1" * length + elif test("^-?[0-9]+(\\.[0-9]+)?$") then + # decmial number in a string + gsub("[0-9]"; "1") + elif test("^[0-9a-fA-F]+$") then #hexadecimal string + # repeat the hex pattern and truncate to original length + ("a1" * length)[:length] + elif . == "" then + # prevents empty string from just returning null instead of empty string + "" + elif . == "true" or . == "false" then + "false" + elif test("://") then + "url://somewhere" + elif test("/") then + "some/path" + elif . == "null" then + # From fitbit export in csv + "null" else - . + # Preserve string length for other strings + "x" * length end - ); -scrub \ No newline at end of file + elif type == "number" then + if 946702800 <= . and . <= 1893474000 then + # Take modulo 1 year to get variance in the output, then add offset to bring to ~2024 + ((((. % 31557600) + 1704067200) / 5000 | floor) * 5000) + elif . == (. | floor) then + # Integer - preserve digit count + (tostring | length) as $len | ("1" * $len) | tonumber + else + # Decimal - preserve digit count and leading zero + tostring | split(".") | + (.[0] | if . == "0" or . == "-0" then . else ("1" * length) end) as $int_part | + (.[1] | ("1" * length)) as $frac_part | + ($int_part + "." + $frac_part) | tonumber + end + elif type == "boolean" then + # Replace all booleans with false, this can give sensative info away based + # on what the key was in the data + false + else + . + end; + +def scrub: + if type == "object" then + # Apply scrubbing to both keys and values + with_entries(.key |= scrub_key | .value |= scrub) + elif type == "array" then + # Keep only 2 elements, but scrub *those* elements + .[:2] | map(scrub) + else + # Scrub a primitive value + scrub_primitive + end; + +# Call scrub +#scrub diff --git a/util/scrub.ts b/util/scrub.ts index 07024c4..20f12b1 100755 --- a/util/scrub.ts +++ b/util/scrub.ts @@ -1,8 +1,13 @@ #!/usr/bin/env -S node -import { $, argv, path } from "zx"; +import { $, path, minimist } from "zx"; import { strict as assert } from "node:assert"; import fs from "node:fs/promises"; +import { parse } from "csv-parse/sync"; +import { stringify } from "csv-stringify/sync"; +import { scrubPrimitive } from "./scrub_primitive.ts"; + +$.verbose = true; /**Catches any p Promise throws and instead returns those in a tuple*/ async function ptry( @@ -16,46 +21,88 @@ async function ptry( } } -$.verbose = true; +class UnsupportedScrubError extends Error {} + +interface ScrubOptions { + hasHeaders?: boolean; + overrideType?: "csv" | "json" +} + +/**Scrubs a file, json or csv*/ +async function scrubFile(inFile: string, outFile: string, options?: ScrubOptions): Promise<[(Error | undefined), undefined]> { + if (inFile.endsWith(".csv") || overrideType === "csv") { + const [maybeErr, inCSV] = await ptry(fs.readFile(inFile, { encoding: "utf8" })); + if (maybeErr) { + return [maybeErr, undefined]; + } + const hasHeaders = options?.hasHeaders ?? false; + + const rows = parse(inCSV); + const MAX_ROWS = 20; + let scrubbedRows; + if (hasHeaders) { + const header = rows[0]; + const scrubbedRest = rows.slice(1, MAX_ROWS + 1).map(row => row.map(cell => scrubPrimitive(cell))); + scrubbedRows = [header, ...scrubbedRest]; + } + else { + scrubbedRows = rows.slice(0, MAX_ROWS).map(row => row.map(cell => scrubPrimitive(cell))); + } + + const outCSV = stringify(scrubbedRows); + const [maybeErr2] = await ptry(fs.writeFile(outFile, outCSV, { encoding: "utf8" })); + return [maybeErr2, undefined]; + } + else if (inFile.endsWith(".json") || overrideType === "json") { + const [jqErr] = await ptry($`cat ${inFile} | jq -L ${scriptDir} 'include "scrub"; scrub' > ${outFile}`); + return [jqErr, undefined]; + } + else { + return [new UnsupportedScrubError(`No method for scrubbing file '${inFile}'`), undefined]; + } +} const scriptDir = path.dirname(new URL(import.meta.url).pathname); -const scrubJq = path.join(scriptDir, "scrub.jq"); -const targetDir = argv._[0]; +const argv = minimist(process.argv.slice(2), { + boolean: ["has-headers"], + string: ["override-type"], +}); +const fileOrGlob = argv._[0]; +const hasHeaders = argv["has-headers"]; // already a boolean, no need for !! +const overrideType = argv["override-type"]; +assert(fileOrGlob, "Usage: ./scrub.ts [--has-headers] [--override-type=csv] "); +assert(overrideType === undefined || overrideType === "" || overrideType === "csv" || overrideType === "json", "Override type must be either 'json' or 'csv'"); -assert(targetDir, "Usage: ./scrub.ts "); +console.log(`Matching files against passed file_or_glob: '${fileOrGlob}'`); -const targetPath = path.resolve(targetDir); +const filePaths: string[] = []; +for await (const file of fs.glob(fileOrGlob)) { + const resolved = path.resolve(file); + filePaths.push(resolved); +} -// const stat = await fs.stat(targetPath); -// assert(stat.isDirectory(), ""); - -const [notADir] = await ptry($`test -d ${targetPath}`); -assert(!notADir, `Error: '${targetPath}' is not a directory`); - -const [noScrubJq] = await ptry($`test -f ${scrubJq}`); -assert(!noScrubJq, `Error: scrub.jq not found at ${scrubJq}`); - -console.log(`Scrubbing JSON files in: ${targetPath}`); -console.log(`Using scrub.jq from: ${scrubJq}`); -console.log(); - -const [findErr, files] = await ptry($`fdfind -t f '\\.json$' ${targetPath} -0`.quiet()); -assert(!findErr, `Error finding JSON files: ${findErr}`); - -const filePaths = files.stdout.split("\0").filter(Boolean); console.log("filePaths", filePaths); +assert(filePaths.length > 0, `No files found matching: ${fileOrGlob}`); for (const file of filePaths) { console.log(`Processing: ${file}`); const tmpFile = `${file}.tmp`; + const piiFile = `${file}.DELETE-THIS-HAS-PII`; + + const [scrubError] = await scrubFile(file, tmpFile, { hasHeaders, overrideType }); + if (scrubError instanceof UnsupportedScrubError) { + console.warn(scrubError.message); + continue; + } + assert(!scrubError, `Error processing ${file}: ${scrubError}`); - const [jqErr] = await ptry($`jq -f ${scrubJq} ${file} > ${tmpFile}`); - assert(!jqErr, `Error processing ${file}: ${jqErr}`); - - const [mvErr] = await ptry($`mv ${tmpFile} ${file}`); - assert(!mvErr, `Error moving ${tmpFile} to ${file}: ${mvErr}`); + const [mvErr] = await ptry($`mv ${file} ${piiFile}`); + assert(!mvErr, `Error moving ${file} to ${piiFile}: ${mvErr}`); + + const [mv2Err] = await ptry($`mv ${tmpFile} ${file}`); + assert(!mv2Err, `Error moving ${tmpFile} to ${file}: ${mv2Err}`); } console.log(); -console.log("Done!"); \ No newline at end of file +console.log("Done!"); diff --git a/util/scrub_primitive.ts b/util/scrub_primitive.ts new file mode 100644 index 0000000..80cf646 --- /dev/null +++ b/util/scrub_primitive.ts @@ -0,0 +1,146 @@ +/**Scrubs a primitive the same as scrub.jq + * @todo Keep in sync with scrub.jq, as ideally they'd just be the same code... but diff + * languages ugh*/ +export function scrubPrimitive(value: unknown): unknown { + if (typeof value === "string") { + if (/^(([0-9]{1,3}\.){3}[0-9]{1,3})$/.test(value)) { + // IPv4 + return "1.1.1.1"; + } + else if (/^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/.test(value)) { + // IPv6 + return "2000:0000:0000:0000:0000:0000:0000:0000"; + } + else if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)) { + // Email-like + return "not_a_real_email@example.com"; + } + else if (/\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff|mp3|wav|flac|aac|ogg|wma|m4a|mp4|avi|mkv|mov|wmv|flv|webm)$/i.test(value)) { + // Leave these alone, you will have to manually go through these later and replace with + // placeholders + console.error(`MANUAL REPAIR NEEDED: ${value}`); + return value; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}$/.test(value)) { + // iso date time without millis with timezone + return "2020-04-13T10:09:08+00:00"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,6})?[+-][0-9]{2}:[0-9]{2}$/.test(value)) { + // iso date time with millis with timezone + return "2020-04-13T10:09:08.000000+00:00"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,6})?$/.test(value)) { + // iso date time with millis with timezone + return "2020-04-13T10:09:08.000"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(value)) { + // iso date time with z and the end (from fitbit export) + return "2020-04-13T10:09:08Z"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{1,6}Z$/.test(value)) { + // iso date time with z and the end (from fitbit export) + return "2020-04-13T10:09:08.000000Z"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$/.test(value)) { + // iso date time no seconds (from fitbit export) + return "2020-04-13T10:09"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} - [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(value)) { + // iso date time range (from fitbit export) + return "2020-04-13 10:09:08 - 2020-04-13 10:09:08"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,6})?[+-][0-9]{4}$/.test(value)) { + // iso date time with millis with timezone no colon, no T (from fitbit export) + return "2020-04-13 10:09:08+0000"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(value)) { + // just date + return "2020-04-13"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC$/.test(value)) { + // Date format from snapchat export + return "2020-04-13 10:09:08 UTC"; + } + else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(value)) { + // Date format from snapchat export + return "2020-04-13 10:09:08"; + } + else if (/^[0-9]{2}\/[0-9]{2}\/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(value)) { + // Date format from fitbit export (MM/DD/YY) + return "04/13/20 10:09:08"; + } + else if (/^\w{3} \w{3} \d{2} \d{2}:\d{2}:\d{2} UTC \d{4}$/.test(value)) { + // Date format from fitbit export (uughhh) + return "Mon Apr 13 10:09:08 UTC 2020"; + } + else if (/^\+[0-9]{2}:[0-2]{2}$/.test(value)) { + // UTC offset (from fitbit export) + return "+00:00"; + } + else if (/^-[0-9]{2}:[0-2]{2}$/.test(value)) { + // UTC offset (from fitbit export) + return "-00:00" + } + else if (/^[0-9]+$/.test(value)) { + // preserve length of the string + return "1".repeat(value.length); + } + else if (/^-?[0-9]*(\.[0-9]*)?$/.test(value) && /[0-9]/.test(value)) { + // Decimal string - preserve shape + return value.replace(/[0-9]/g, "1"); + } + else if (/^[0-9a-fA-F]+$/.test(value)) { + // hexadecimal string - repeat the hex pattern and truncate to original length + return "a1".repeat(value.length) + .slice(0, value.length); + } + else if (value === "") { + // prevents empty string from just returning null instead of empty string + return ""; + } + else if (value === "true" || value === "false") { + return "false"; + } + else if (/:\/\//.test(value)) { + return "url://somewhere"; + } + else if (/\//.test(value)) { + return "some/path"; + } + else if (value === "null") { + return "null"; + } + else { + // Preserve string length for other strings + return "x".repeat(value.length); + } + } + else if (typeof value === "number") { + if (946702800 <= value && value <= 1893474000) { + // Take modulo 1 year to get variance in the output, then add offset to bring to ~2024 + return Math.floor(((value % 31557600) + 1704067200) / 5000) * 5000; + } + else if (Number.isInteger(value)) { + // Integer - preserve digit count + const len = value.toString() + .length; + return Number("1".repeat(len)); + } + else { + // Decimal - preserve shape, sign, and leading zero + const sign = value < 0 ? "-" : ""; + const parts = Math.abs(value).toString().split("."); + const intPart = parts[0] === "0" ? "0" : "1".repeat(parts[0].length); + const fracPart = "1".repeat(parts[1].length); + return Number(`${sign}${intPart}.${fracPart}`); + } + } + else if (typeof value === "boolean") { + // Replace all booleans with false, this can give sensitive info away based + // on what the key was in the data + return false; + } + else { + return value; + } +} \ No newline at end of file