-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
434 additions
and
48 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
export default class BaseDataExport { | ||
constructor() { | ||
if (new.target == BaseDataExport) { | ||
throw new Error("Cannot instantiate an abstract class."); | ||
} | ||
this.loading = false; | ||
this.header = []; | ||
this.rows = []; | ||
} | ||
|
||
startLoading() { | ||
this.loading = false; | ||
} | ||
|
||
stopLoading() { | ||
this.loading = false; | ||
} | ||
|
||
isLoading() { | ||
return this.loading; | ||
} | ||
|
||
getFileName() { | ||
return "dataExport.csv"; | ||
} | ||
|
||
// Abstract method to retrieve CSV rows | ||
async getCSVRows() { | ||
throw new Error("getCSVRows() must be implemented by subclasses."); | ||
} | ||
|
||
// Method to export rows to a CSV file | ||
exportToCSV() { | ||
const filename = this.getFileName(); | ||
if (!this.rows || this.rows.length === 0) { | ||
console.error("No data available for CSV export."); | ||
return; | ||
} | ||
|
||
const csvRows = [this.header].concat(this.rows); | ||
console.log(this.header, this.rows, csvRows); | ||
|
||
// Convert rows array to CSV format | ||
const csvContent = csvRows.map((row) => row.join(",")).join("\n"); | ||
|
||
// Create a Blob with CSV content and make it downloadable | ||
const blob = new Blob([csvContent], { type: "text/csv" }); | ||
const url = URL.createObjectURL(blob); | ||
|
||
// Create a link element to trigger the download | ||
const link = document.createElement("a"); | ||
link.href = url; | ||
link.setAttribute("download", filename); | ||
document.body.appendChild(link); | ||
link.click(); | ||
|
||
// Clean up and revoke the object URL | ||
document.body.removeChild(link); | ||
URL.revokeObjectURL(url); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { db } from "src/firebase"; | ||
import { DefaultDict } from "../generalFunctions"; | ||
|
||
import BaseDataExport from "./BaseDataExport"; | ||
|
||
export default class UserDataExport extends BaseDataExport { | ||
constructor() { | ||
super(); | ||
this.config = { | ||
label: "Signups Per Day", | ||
value: "signups", | ||
fields: [ | ||
{ name: "Date Range", type: "daterange", value: { from: undefined, to: undefined } }, | ||
], | ||
}; | ||
} | ||
|
||
date2timestamp(date_str) { | ||
const date = new Date(date_str); | ||
return date.getTime(); | ||
} | ||
|
||
timestamp2date(ts) { | ||
const date = new Date(ts); | ||
return date.toLocaleDateString("en-GB"); | ||
} | ||
|
||
getFileName() { | ||
const range = this.config.fields[0].value; | ||
return `signups_in_range_${range.from}-${range.to}.csv`; | ||
} | ||
|
||
// Implementing the abstract getCSVRows method | ||
async getCSVRows() { | ||
this.startLoading(); | ||
|
||
const from = this.date2timestamp(this.config.fields[0].value.from); | ||
const to = this.date2timestamp(this.config.fields[0].value.to); | ||
|
||
this.header = ["date", "count"]; | ||
try { | ||
const users = await this.fetchUsersInRange(from, to); | ||
this.rows = await this.aggregateSignups(users); | ||
return this.rows; | ||
} catch (error) { | ||
console.error("Error fetching user data for CSV export:", error); | ||
throw error; | ||
} finally { | ||
this.stopLoading(); | ||
} | ||
} | ||
|
||
async fetchUsersInRange(from, to) { | ||
const user_ref = db.ref("users").orderByChild("created").startAt(from).endAt(to); | ||
const payload = await user_ref.once("value"); | ||
if (!payload.exists()) { | ||
throw new Error("No users found"); | ||
} | ||
return Object.values(payload.val()); | ||
} | ||
|
||
async aggregateSignups(users) { | ||
const aggregate = await users.reduce((counts, user) => { | ||
counts[this.timestamp2date(user.created)] += 1; | ||
return counts; | ||
}, new DefaultDict(0)); | ||
return Object.entries(aggregate); | ||
} | ||
} |
Oops, something went wrong.