Skip to content

Commit 4d5f58b

Browse files
committed
Fixed calendar statistic calculations / json file locations adjusted
1 parent 8ce483d commit 4d5f58b

File tree

8 files changed

+278
-82
lines changed

8 files changed

+278
-82
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "koshelf"
3-
version = "1.0.12"
3+
version = "1.0.14"
44
description = "Transform your KOReader library into a beautiful reading dashboard with statistics."
55
repository = "https://github.com/paviro/KOShelf"
66
license = "EUPL-1.2 license"

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,19 @@ site/
209209
│ ├── calendar.js # Calendar functionality
210210
│ ├── heatmap.js # Activity heatmap visualization
211211
│ └── event-calendar.min.js # Event calendar library
212-
└── json/ # Statistics data (when available)
213-
├── week_0.json # Weekly statistics data
214-
├── week_1.json
215-
├── ...
216-
├── calendar_data.json # Calendar events and book data
217-
├── daily_activity_2023.json # Daily activity data for heatmap
218-
├── daily_activity_2024.json
219-
└── ...
212+
└── json/ # Data files (when available)
213+
├── calendar/ # Calendar data split by month
214+
│ ├── available_months.json # List of months with calendar data
215+
│ ├── 2024-01.json # January 2024 events and book data
216+
│ ├── 2024-02.json # February 2024 events and book data
217+
│ └── ... # Additional monthly files
218+
└── statistics/ # Statistics data
219+
├── week_0.json # Weekly statistics data
220+
├── week_1.json
221+
├── ...
222+
├── daily_activity_2023.json # Daily activity data for heatmap
223+
├── daily_activity_2024.json
224+
└── ...
220225
```
221226

222227
## Credits

assets/calendar.js

Lines changed: 147 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,143 @@
77
let calendar;
88
let currentEvents = [];
99
let currentBooks = {};
10+
let monthlyDataCache = new Map(); // Cache for monthly data: month -> {events, books}
11+
let availableMonths = []; // List of months that have data
12+
let currentDisplayedMonth = null;
1013

1114
// Exported entry point
1215
export function initializeCalendar() {
13-
// Load calendar data from JSON
14-
loadCalendarData().then(calendarData => {
15-
if (calendarData) {
16-
currentEvents = calendarData.events || [];
17-
currentBooks = calendarData.books || {};
16+
// First, load the list of available months
17+
loadAvailableMonths().then(() => {
18+
// Load calendar data for current month
19+
const now = new Date();
20+
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
21+
22+
loadCalendarData(currentMonth).then(calendarData => {
23+
if (calendarData) {
24+
currentEvents = calendarData.events || [];
25+
currentBooks = calendarData.books || {};
26+
currentDisplayedMonth = currentMonth;
1827

19-
initializeEventCalendar(currentEvents);
28+
initializeEventCalendar(currentEvents);
2029

21-
// Populate statistics widgets
22-
updateCalendarStats(currentEvents);
30+
// Populate statistics widgets
31+
updateCalendarStats(currentEvents);
2332

24-
// Wire up DOM interaction handlers (today / prev / next / modal)
25-
setupEventHandlers();
26-
}
33+
// Wire up DOM interaction handlers (today / prev / next / modal)
34+
setupEventHandlers();
35+
}
36+
});
2737
});
2838
}
2939

30-
// Load calendar data from JSON file
31-
async function loadCalendarData() {
40+
// Load the list of available months
41+
async function loadAvailableMonths() {
42+
try {
43+
const response = await fetch('/assets/json/calendar/available_months.json');
44+
if (response.ok) {
45+
availableMonths = await response.json();
46+
} else {
47+
console.warn('Could not load available months list');
48+
availableMonths = [];
49+
}
50+
} catch (error) {
51+
console.warn('Error loading available months:', error);
52+
availableMonths = [];
53+
}
54+
}
55+
56+
// Load and update calendar for a specific month
57+
async function loadAndUpdateCalendarForMonth(targetMonth) {
58+
// Skip if this month is already displayed
59+
if (currentDisplayedMonth === targetMonth) {
60+
return;
61+
}
62+
63+
try {
64+
// Load the target month's data (will use cache if available)
65+
await loadCalendarData(targetMonth);
66+
currentDisplayedMonth = targetMonth;
67+
68+
// Combine all cached events and books to show events from adjacent months
69+
currentEvents = [];
70+
currentBooks = {};
71+
72+
for (const [month, monthData] of monthlyDataCache) {
73+
// Add all events from all cached months
74+
currentEvents.push(...(monthData.events || []));
75+
76+
// Merge all books from all cached months
77+
Object.assign(currentBooks, monthData.books || {});
78+
}
79+
80+
// Update calendar events with all cached data
81+
if (calendar) {
82+
const mapEvents = evts => evts.map(ev => {
83+
const book = currentBooks[ev.book_id] || {};
84+
return {
85+
id: ev.book_id,
86+
title: book.title || 'Unknown Book',
87+
start: ev.start,
88+
end: ev.end || ev.start,
89+
allDay: true,
90+
backgroundColor: book.color || getEventColor(ev),
91+
borderColor: book.color || getEventColor(ev),
92+
textColor: '#ffffff',
93+
extendedProps: {
94+
...ev,
95+
book_title: book.title || 'Unknown Book',
96+
authors: book.authors || [],
97+
book_path: book.book_path,
98+
book_cover: book.book_cover,
99+
color: book.color,
100+
md5: ev.book_id
101+
}
102+
};
103+
});
104+
105+
calendar.setOption('events', mapEvents(currentEvents));
106+
}
107+
} catch (error) {
108+
console.error(`Failed to load calendar data for ${targetMonth}:`, error);
109+
}
110+
}
111+
112+
// Load calendar data from monthly JSON files
113+
async function loadCalendarData(targetMonth = null) {
32114
try {
33-
const response = await fetch('/assets/json/calendar_data.json');
115+
// If no target month specified, use current month
116+
if (!targetMonth) {
117+
const now = new Date();
118+
targetMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
119+
}
120+
121+
// Check if month data is already cached
122+
if (monthlyDataCache.has(targetMonth)) {
123+
return monthlyDataCache.get(targetMonth);
124+
}
125+
126+
// Check if this month has data available
127+
if (availableMonths.length > 0 && !availableMonths.includes(targetMonth)) {
128+
console.info(`No calendar data available for ${targetMonth}`);
129+
return { events: [], books: {} }; // Return empty data instead of null
130+
}
131+
132+
const response = await fetch(`/assets/json/calendar/${targetMonth}.json`);
34133
if (!response.ok) {
35-
console.error('Failed to load calendar data:', response.status);
36-
return null;
134+
console.error(`Failed to load calendar data for ${targetMonth}:`, response.status);
135+
return { events: [], books: {} };
37136
}
38-
return await response.json();
137+
138+
const calendarData = await response.json();
139+
140+
// Cache the loaded data
141+
monthlyDataCache.set(targetMonth, calendarData);
142+
143+
return calendarData;
39144
} catch (error) {
40-
console.error('Error loading calendar data:', error);
41-
return null;
145+
console.error(`Error loading calendar data for ${targetMonth}:`, error);
146+
return { events: [], books: {} };
42147
}
43148
}
44149

@@ -104,6 +209,10 @@ function initializeEventCalendar(events) {
104209
updateCalendarTitleDirect(viewTitle);
105210
updateMonthlyStats(currentMonthDate);
106211

212+
// Load data for the new month if it's different from current data
213+
const newMonth = `${currentMonthDate.getFullYear()}-${String(currentMonthDate.getMonth() + 1).padStart(2, '0')}`;
214+
loadAndUpdateCalendarForMonth(newMonth);
215+
107216
// Scroll current day into view if needed
108217
setTimeout(() => scrollCurrentDayIntoView(), 100);
109218
}
@@ -302,10 +411,25 @@ function updateMonthlyStats(currentDate) {
302411
// Count unique books (using book_id)
303412
uniqueBooks.add(event.book_id);
304413

305-
// Count unique dates (format as YYYY-MM-DD)
306-
const eventDate = new Date(event.start);
307-
const dateString = eventDate.toISOString().split('T')[0];
308-
uniqueDates.add(dateString);
414+
// Count all dates within the event's date range
415+
const startDate = new Date(event.start);
416+
const endDate = event.end ? new Date(event.end) : new Date(event.start);
417+
418+
// If end date exists, it's exclusive, so we subtract 1 day
419+
if (event.end) {
420+
endDate.setDate(endDate.getDate() - 1);
421+
}
422+
423+
// Iterate through all dates from start to end (inclusive)
424+
const currentDate = new Date(startDate);
425+
while (currentDate <= endDate) {
426+
// Check if this date is in the target month/year
427+
if (currentDate.getMonth() === targetMonth && currentDate.getFullYear() === targetYear) {
428+
const dateString = currentDate.toISOString().split('T')[0];
429+
uniqueDates.add(dateString);
430+
}
431+
currentDate.setDate(currentDate.getDate() + 1);
432+
}
309433

310434
// Sum pages and time
311435
totalPages += event.total_pages_read || 0;

assets/heatmap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class ActivityHeatmap {
5353
// Load activity data for a specific year
5454
async loadYearData(year) {
5555
try {
56-
const response = await fetch(`/assets/json/daily_activity_${year}.json`);
56+
const response = await fetch(`/assets/json/statistics/daily_activity_${year}.json`);
5757
if (!response.ok) {
5858
throw new Error(`Failed to load activity data for ${year}`);
5959
}

assets/input.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192

193193
/* Style the calendar body */
194194
.calendar-container .ec .ec-body {
195-
@apply bg-gray-50 dark:bg-dark-850/50 rounded-lg mt-1 border border-gray-200 dark:border-dark-700 shadow-sm;
195+
@apply bg-gray-50 dark:bg-dark-850/50 mt-1 border border-gray-200 dark:border-dark-700 shadow-sm;
196196
border-top: none;
197197
}
198198

@@ -208,8 +208,8 @@
208208

209209
/* Event styling */
210210
.calendar-container .ec-event {
211-
@apply shadow-lg rounded-lg px-1 md:py-1 md:px-2 text-2xs md:text-xs lg:text-sm;
212-
@apply mb-1;
211+
@apply shadow-lg rounded-md md:rounded-lg px-1 md:py-1 md:px-2 text-2xs md:text-xs lg:text-sm;
212+
@apply mb-1 mx-0.5;
213213
}
214214

215215
/* Scrollbar styling */

assets/statistics.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class StatisticsManager {
166166
// Wait for transition out to complete before fetching
167167
await new Promise(resolve => setTimeout(resolve, 200));
168168

169-
const response = await fetch(`/assets/json/week_${weekIndex}.json`);
169+
const response = await fetch(`/assets/json/statistics/week_${weekIndex}.json`);
170170
if (!response.ok) {
171171
throw new Error(`HTTP error! status: ${response.status}`);
172172
}

0 commit comments

Comments
 (0)