mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add timeSpan()
This commit is contained in:
parent
93ab3b4164
commit
8e118bbc76
6 changed files with 104 additions and 27 deletions
|
@ -1,10 +1,13 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
|
import { timeSpan, MONTH, YEAR } from "../time";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import { gatherData, buildHistogram, GraphData, AddedRange } from "./added";
|
import { gatherData, buildHistogram, GraphData, AddedRange } from "./added";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import HistogramGraph from "./HistogramGraph.svelte";
|
import HistogramGraph from "./HistogramGraph.svelte";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
|
@ -20,6 +23,10 @@
|
||||||
console.log("preparing data");
|
console.log("preparing data");
|
||||||
histogramData = buildHistogram(addedData, range);
|
histogramData = buildHistogram(addedData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -29,15 +36,15 @@
|
||||||
<div class="range-box-inner">
|
<div class="range-box-inner">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={AddedRange.Month} />
|
<input type="radio" bind:group={range} value={AddedRange.Month} />
|
||||||
Month
|
{month}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={AddedRange.Quarter} />
|
<input type="radio" bind:group={range} value={AddedRange.Quarter} />
|
||||||
3 months
|
{month3}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={AddedRange.Year} />
|
<input type="radio" bind:group={range} value={AddedRange.Year} />
|
||||||
Year
|
{year}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={AddedRange.AllTime} />
|
<input type="radio" bind:group={range} value={AddedRange.AllTime} />
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
|
import { timeSpan, MONTH, YEAR } from "../time";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import { defaultGraphBounds } from "./graphs";
|
import { defaultGraphBounds } from "./graphs";
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +14,8 @@
|
||||||
import HistogramGraph from "./HistogramGraph.svelte";
|
import HistogramGraph from "./HistogramGraph.svelte";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
let graphData = null as GraphData | null;
|
let graphData = null as GraphData | null;
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
|
|
||||||
|
@ -27,6 +31,10 @@
|
||||||
console.log("preparing data");
|
console.log("preparing data");
|
||||||
histogramData = buildHistogram(graphData, range);
|
histogramData = buildHistogram(graphData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -37,15 +45,15 @@
|
||||||
<div class="range-box-inner">
|
<div class="range-box-inner">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={FutureDueRange.Month} />
|
<input type="radio" bind:group={range} value={FutureDueRange.Month} />
|
||||||
Month
|
{month}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={FutureDueRange.Quarter} />
|
<input type="radio" bind:group={range} value={FutureDueRange.Quarter} />
|
||||||
3 months
|
{month3}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={FutureDueRange.Year} />
|
<input type="radio" bind:group={range} value={FutureDueRange.Year} />
|
||||||
Year
|
{year}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={FutureDueRange.AllTime} />
|
<input type="radio" bind:group={range} value={FutureDueRange.AllTime} />
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { assertUnreachable } from "../typing";
|
import { timeSpan, MONTH, YEAR } from "../time";
|
||||||
import { I18n } from "../i18n";
|
import { I18n } from "../i18n";
|
||||||
|
import { assertUnreachable } from "../typing";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { getGraphData, RevlogRange } from "./graphs";
|
import { getGraphData, RevlogRange } from "./graphs";
|
||||||
import IntervalsGraph from "./IntervalsGraph.svelte";
|
import IntervalsGraph from "./IntervalsGraph.svelte";
|
||||||
|
@ -81,6 +82,9 @@
|
||||||
search = displayedSearch;
|
search = displayedSearch;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="range-box">
|
<div class="range-box">
|
||||||
|
@ -113,11 +117,11 @@
|
||||||
Review history:
|
Review history:
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={revlogRange} value={RevlogRange.Month} />
|
<input type="radio" bind:group={revlogRange} value={RevlogRange.Month} />
|
||||||
Month
|
{month}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={revlogRange} value={RevlogRange.Year} />
|
<input type="radio" bind:group={revlogRange} value={RevlogRange.Year} />
|
||||||
Year
|
{year}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={revlogRange} value={RevlogRange.All} />
|
<input type="radio" bind:group={revlogRange} value={RevlogRange.All} />
|
||||||
|
@ -129,10 +133,10 @@
|
||||||
|
|
||||||
<TodayStats {sourceData} {i18n} />
|
<TodayStats {sourceData} {i18n} />
|
||||||
<CardCounts {sourceData} {i18n} />
|
<CardCounts {sourceData} {i18n} />
|
||||||
<FutureDue {sourceData} />
|
<FutureDue {sourceData} {i18n} />
|
||||||
<ReviewsGraph {sourceData} {revlogRange} />
|
<ReviewsGraph {sourceData} {revlogRange} {i18n} />
|
||||||
<IntervalsGraph {sourceData} />
|
<IntervalsGraph {sourceData} {i18n} />
|
||||||
<EaseGraph {sourceData} />
|
<EaseGraph {sourceData} />
|
||||||
<ButtonsGraph {sourceData} />
|
<ButtonsGraph {sourceData} />
|
||||||
<HourGraph {sourceData} />
|
<HourGraph {sourceData} />
|
||||||
<AddedGraph {sourceData} />
|
<AddedGraph {sourceData} {i18n} />
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
|
import { timeSpan, MONTH, YEAR } from "../time";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
import {
|
import {
|
||||||
gatherIntervalData,
|
gatherIntervalData,
|
||||||
|
@ -10,13 +12,14 @@
|
||||||
import HistogramGraph from "./HistogramGraph.svelte";
|
import HistogramGraph from "./HistogramGraph.svelte";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
let intervalData: IntervalGraphData | null = null;
|
let intervalData: IntervalGraphData | null = null;
|
||||||
let histogramData = null as HistogramData | null;
|
let histogramData = null as HistogramData | null;
|
||||||
|
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
let range = IntervalRange.Percentile95;
|
let range = IntervalRange.Percentile95;
|
||||||
|
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
console.log("gathering data");
|
||||||
intervalData = gatherIntervalData(sourceData);
|
intervalData = gatherIntervalData(sourceData);
|
||||||
|
@ -26,6 +29,8 @@
|
||||||
console.log("preparing data");
|
console.log("preparing data");
|
||||||
histogramData = prepareIntervalData(intervalData, range);
|
histogramData = prepareIntervalData(intervalData, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if histogramData}
|
{#if histogramData}
|
||||||
|
@ -35,28 +40,28 @@
|
||||||
<div class="range-box-inner">
|
<div class="range-box-inner">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={IntervalRange.Month} />
|
<input type="radio" bind:group={range} value={IntervalRange.Month} />
|
||||||
Month
|
{month}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group={range}
|
bind:group={range}
|
||||||
value={IntervalRange.Percentile50} />
|
value={IntervalRange.Percentile50} />
|
||||||
50th percentile
|
50%
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group={range}
|
bind:group={range}
|
||||||
value={IntervalRange.Percentile95} />
|
value={IntervalRange.Percentile95} />
|
||||||
95th percentile
|
95%
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group={range}
|
bind:group={range}
|
||||||
value={IntervalRange.Percentile999} />
|
value={IntervalRange.Percentile999} />
|
||||||
99.9th percentile
|
99.9%
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={IntervalRange.All} />
|
<input type="radio" bind:group={range} value={IntervalRange.All} />
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
import { defaultGraphBounds, RevlogRange } from "./graphs";
|
import { defaultGraphBounds, RevlogRange } from "./graphs";
|
||||||
import { GraphData, gatherData, renderReviews, ReviewRange } from "./reviews";
|
import { GraphData, gatherData, renderReviews, ReviewRange } from "./reviews";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
|
import { timeSpan, MONTH, YEAR } from "../time";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
export let revlogRange: RevlogRange = RevlogRange.Month;
|
export let revlogRange: RevlogRange = RevlogRange.Month;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
let graphData: GraphData | null = null;
|
let graphData: GraphData | null = null;
|
||||||
|
|
||||||
|
@ -39,6 +42,10 @@
|
||||||
$: if (graphData) {
|
$: if (graphData) {
|
||||||
renderReviews(svg as SVGElement, bounds, graphData, range, showTime);
|
renderReviews(svg as SVGElement, bounds, graphData, range, showTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const month = timeSpan(i18n, 1 * MONTH);
|
||||||
|
const month3 = timeSpan(i18n, 3 * MONTH);
|
||||||
|
const year = timeSpan(i18n, 1 * YEAR);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="graph">
|
<div class="graph">
|
||||||
|
@ -53,15 +60,15 @@
|
||||||
{#if revlogRange >= RevlogRange.Year}
|
{#if revlogRange >= RevlogRange.Year}
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={ReviewRange.Month} />
|
<input type="radio" bind:group={range} value={ReviewRange.Month} />
|
||||||
Month
|
{month}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={ReviewRange.Quarter} />
|
<input type="radio" bind:group={range} value={ReviewRange.Quarter} />
|
||||||
3 months
|
{month3}
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" bind:group={range} value={ReviewRange.Year} />
|
<input type="radio" bind:group={range} value={ReviewRange.Year} />
|
||||||
Year
|
{year}
|
||||||
</label>
|
</label>
|
||||||
{/if}
|
{/if}
|
||||||
{#if revlogRange === RevlogRange.All}
|
{#if revlogRange === RevlogRange.All}
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
import { I18n } from "./i18n";
|
import { I18n } from "./i18n";
|
||||||
|
|
||||||
const SECOND = 1.0;
|
export const SECOND = 1.0;
|
||||||
const MINUTE = 60.0 * SECOND;
|
export const MINUTE = 60.0 * SECOND;
|
||||||
const HOUR = 60.0 * MINUTE;
|
export const HOUR = 60.0 * MINUTE;
|
||||||
const DAY = 24.0 * HOUR;
|
export const DAY = 24.0 * HOUR;
|
||||||
const MONTH = 30.0 * DAY;
|
export const MONTH = 30.0 * DAY;
|
||||||
const YEAR = 12.0 * MONTH;
|
export const YEAR = 12.0 * MONTH;
|
||||||
|
|
||||||
enum TimespanUnit {
|
enum TimespanUnit {
|
||||||
Seconds,
|
Seconds,
|
||||||
|
@ -70,6 +70,15 @@ function unitAmount(unit: TimespanUnit, secs: number): number {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unitAmountRounded(unit: TimespanUnit, secs: number): number {
|
||||||
|
const value = unitAmount(unit, secs);
|
||||||
|
if (unit === TimespanUnit.Seconds || unit === TimespanUnit.Days) {
|
||||||
|
return Math.round(value);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function studiedToday(i18n: I18n, cards: number, secs: number): string {
|
export function studiedToday(i18n: I18n, cards: number, secs: number): string {
|
||||||
const unit = naturalUnit(secs);
|
const unit = naturalUnit(secs);
|
||||||
const amount = unitAmount(unit, secs);
|
const amount = unitAmount(unit, secs);
|
||||||
|
@ -86,3 +95,40 @@ export function studiedToday(i18n: I18n, cards: number, secs: number): string {
|
||||||
"secs-per-card": secsPer,
|
"secs-per-card": secsPer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describe the given seconds using the largest appropriate unit.
|
||||||
|
/// If precise is true, show to two decimal places, eg
|
||||||
|
/// eg 70 seconds -> "1.17 minutes"
|
||||||
|
/// If false, seconds and days are shown without decimals.
|
||||||
|
export function timeSpan(i18n: I18n, seconds: number, precise = true): string {
|
||||||
|
const unit = naturalUnit(seconds);
|
||||||
|
let amount: number;
|
||||||
|
if (precise) {
|
||||||
|
amount = unitAmount(unit, seconds);
|
||||||
|
} else {
|
||||||
|
amount = unitAmountRounded(unit, seconds);
|
||||||
|
}
|
||||||
|
let key: number;
|
||||||
|
switch (unit) {
|
||||||
|
case TimespanUnit.Seconds:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_SECONDS;
|
||||||
|
break;
|
||||||
|
case TimespanUnit.Minutes:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_MINUTES;
|
||||||
|
break;
|
||||||
|
case TimespanUnit.Hours:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_HOURS;
|
||||||
|
break;
|
||||||
|
case TimespanUnit.Days:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_DAYS;
|
||||||
|
break;
|
||||||
|
case TimespanUnit.Months:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_MONTHS;
|
||||||
|
break;
|
||||||
|
case TimespanUnit.Years:
|
||||||
|
key = i18n.TR.SCHEDULING_TIME_SPAN_YEARS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18n.tr(key, { amount });
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue