Skip to content

Commit b808fe6

Browse files
author
sheepzh
committed
Support Webtime Tracker (#216)
1 parent 70ef69c commit b808fe6

File tree

2 files changed

+91
-15
lines changed

2 files changed

+91
-15
lines changed

src/app/components/data-manage/migration/import-other-button/processor.ts

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import StatDatabase from "@db/stat-database"
2+
import { AUTHOR_EMAIL } from "@src/package"
23
import { isNotZeroResult } from "@util/stat"
34

45
const statDatabase: StatDatabase = new StatDatabase(chrome.storage.local)
56

6-
export type OtherExtension = "web_activity_time_tracker"
7+
export type OtherExtension = "webtime_tracker" | "web_activity_time_tracker"
78

89
export type ActionType = 'overwrite' | 'accumulate'
910

@@ -18,6 +19,8 @@ export type ImportedData = {
1819
rows: ImportedRow[]
1920
}
2021

22+
const throwError = () => { throw new Error("Failed to parse, please check your file or contact the author via " + AUTHOR_EMAIL) }
23+
2124
/**
2225
* Parse the content to rows
2326
*
@@ -26,20 +29,18 @@ export type ImportedData = {
2629
* @returns row data
2730
*/
2831
export async function parseFile(ext: OtherExtension, file: File): Promise<ImportedData> {
29-
const text = await file.text()
32+
let rows: ImportedRow[] = []
33+
let focus = false
34+
let time = false
3035
if (ext === 'web_activity_time_tracker') {
31-
const lines = text.split('\n').map(line => line.trim()).filter(line => !!line).splice(1)
32-
const rows: ImportedRow[] = lines.map(line => {
33-
const [host, date, seconds] = line.split(',').map(cell => cell.trim())
34-
const [year, month, day] = date.split('/')
35-
const realDate = `${year}${month.length == 2 ? month : '0' + month}${day.length == 2 ? day : '0' + day}`
36-
return { host, date: realDate, focus: parseInt(seconds) * 1000 }
37-
})
38-
await doIfExist(rows, (row, exist) => row.exist = exist)
39-
return { rows, focus: true }
40-
} else {
41-
return { rows: [] }
36+
rows = await parseWebActivityTimeTracker(file)
37+
focus = true
38+
} else if (ext === 'webtime_tracker') {
39+
rows = await parseWebtimeTracker(file)
40+
focus = true
4241
}
42+
await doIfExist(rows, (row, exist) => row.exist = exist)
43+
return { rows, focus, time }
4344
}
4445

4546
async function doIfExist<T extends timer.stat.RowKey>(items: T[], processor: (item: T, existVal: timer.stat.Result) => any): Promise<void> {
@@ -50,6 +51,79 @@ async function doIfExist<T extends timer.stat.RowKey>(items: T[], processor: (it
5051
}))
5152
}
5253

54+
async function parseWebActivityTimeTracker(file: File): Promise<ImportedRow[]> {
55+
const text = await file.text()
56+
const lines = text.split('\n').map(line => line.trim()).filter(line => !!line).splice(1)
57+
const rows: ImportedRow[] = lines.map(line => {
58+
const [host, date, seconds] = line.split(',').map(cell => cell.trim())
59+
!host || !date || (!seconds && seconds !== '0') && throwError()
60+
const [year, month, day] = date.split('/')
61+
!year || !month || !day && throwError()
62+
const realDate = `${year}${month.length == 2 ? month : '0' + month}${day.length == 2 ? day : '0' + day}`
63+
return { host, date: realDate, focus: parseInt(seconds) * 1000 }
64+
})
65+
return rows
66+
}
67+
68+
type WebtimeTrackerBackup = {
69+
content: {
70+
domains: {
71+
[domain: string]: {
72+
name: string
73+
days: {
74+
// date format: 2023-07-22
75+
[date: string]: { seconds: number }
76+
}
77+
}
78+
}
79+
}
80+
}
81+
82+
const WEBTIME_TRACKER_DATE_REG = /(\d{2})-(\d{2})-\d{2}/
83+
const cvtWebtimeTrackerDate = (date: string): string => WEBTIME_TRACKER_DATE_REG.test(date) ? date.split('-').join('') : undefined
84+
85+
async function parseWebtimeTracker(file: File): Promise<ImportedRow[]> {
86+
const text = await file.text()
87+
if (isJsonFile(file)) {
88+
// JSON file by backup
89+
const data = JSON.parse(text) as WebtimeTrackerBackup
90+
const domains = data?.content?.domains || {}
91+
const rows: ImportedRow[] = Object.entries(domains)
92+
.flatMap(
93+
([host, value]) => Object.entries(value?.days || {})
94+
.map(([date, item]) => [host, cvtWebtimeTrackerDate(date), item?.seconds] as [string, string, number])
95+
)
96+
.filter(([host, date, seconds]) => host && date && seconds)
97+
.map(([host, date, seconds]) => ({
98+
host,
99+
date,
100+
focus: seconds * 1000
101+
} as ImportedRow))
102+
console.log(rows)
103+
return rows
104+
} else if (isCsvFile(file)) {
105+
const lines = text.split('\n').map(line => line.trim()).filter(line => !!line)
106+
const colHeaders = lines[0].split(',')
107+
const rows: ImportedRow[] = []
108+
lines.slice(1).forEach(line => {
109+
const cells = line.split(',')
110+
const host = cells[0]
111+
if (!host) return
112+
for (let i = 1; i < colHeaders?.length; i++) {
113+
const seconds = Number.parseInt(cells[i])
114+
const date = cvtWebtimeTrackerDate(colHeaders[i])
115+
seconds && date && rows.push({ host, date, focus: seconds * 1000 })
116+
}
117+
})
118+
return rows
119+
}
120+
throw new Error("Invalid file format")
121+
}
122+
123+
const isJsonFile = (file: File): boolean => file?.type?.startsWith('application/json')
124+
125+
const isCsvFile = (file: File): boolean => file?.type?.startsWith('text/csv')
126+
53127
/**
54128
* Import data
55129
*/

src/app/components/data-manage/migration/import-other-button/step1.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { Document, Close, Right } from "@element-plus/icons-vue"
55
import { ImportedData, OtherExtension, parseFile } from "./processor"
66

77
const OTHER_NAMES: { [ext in OtherExtension]: string } = {
8+
webtime_tracker: "Webtime Tracker",
89
web_activity_time_tracker: "Web Activity Time Tracker"
910
}
1011

1112
const OTHER_FILE_FORMAT: { [ext in OtherExtension]: string } = {
12-
web_activity_time_tracker: '.csv'
13+
webtime_tracker: '.csv,.json',
14+
web_activity_time_tracker: '.csv',
1315
}
1416

1517
const ALL_TYPES: OtherExtension[] = Object.keys(OTHER_NAMES) as OtherExtension[]
@@ -20,7 +22,7 @@ const _default = defineComponent({
2022
next: (_rows: ImportedData) => true,
2123
},
2224
setup(_, ctx) {
23-
const type: Ref<OtherExtension> = ref('web_activity_time_tracker')
25+
const type: Ref<OtherExtension> = ref('webtime_tracker')
2426
const selectedFile: Ref<File> = ref()
2527
const fileInput: Ref<HTMLInputElement> = ref()
2628
const fileParsing: Ref<boolean> = ref(false)

0 commit comments

Comments
 (0)