13 Commits

Author SHA1 Message Date
PoliEcho ab854ed5ed version 1.0.0
/ sync-to-origin (push) Has been cancelled
2025-11-08 15:51:52 +01:00
PoliEcho 0573510962 add absence page 2025-11-08 15:40:33 +01:00
PoliEcho 290b9344d2 fix password entry prompt
/ sync-to-origin (push) Has been cancelled
2025-11-07 11:49:55 +01:00
PoliEcho 24e4528b4c fix exit to komens menu 2025-11-07 11:49:13 +01:00
PoliEcho 15b0344dc8 hide cursor on marks page
/ sync-to-origin (push) Has been cancelled
2025-06-18 17:32:46 +02:00
PoliEcho 73fed3df0e version 0.8.2
/ sync-to-origin (push) Has been cancelled
2025-05-24 12:29:19 +02:00
PoliEcho 6b3d47ceed add color help for timetable
/ sync-to-origin (push) Waiting to run
2025-05-24 12:27:41 +02:00
PoliEcho 89c67035ca fix menu disappearing after exiting any page 2025-05-24 10:22:07 +02:00
PoliEcho 0486d9fb22 fix space before operator warning 2025-05-24 10:18:59 +02:00
PoliEcho e51088eb26 add download progress bar
/ sync-to-origin (push) Has been cancelled
2025-04-14 20:03:48 +02:00
PoliEcho ad6f748899 add homework checkbox to README
/ sync-to-origin (push) Waiting to run
2025-04-14 07:26:06 +00:00
PoliEcho 1d0d4e2362 update README to reflect new aditions
/ sync-to-origin (push) Waiting to run
2025-04-14 09:24:23 +02:00
PoliEcho a35242b420 fix attachment window not disapearing 2025-04-14 09:22:39 +02:00
10 changed files with 283 additions and 25 deletions
+2 -1
View File
@@ -10,5 +10,6 @@
> - [x] Login
> - [x] Marks
> - [x] Timetable
> - [ ] Komens
> - [x] Komens
> - [ ] Absence
> - [ ] Homework
+168
View File
@@ -0,0 +1,168 @@
#include "memory.h"
#include "helper_funcs.h"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <curses.h>
#include <nlohmann/json.hpp>
#include "net.h"
#include <nlohmann/json_fwd.hpp>
#include <ncurses.h>
#include <string>
using nlohmann::json;
std::vector<allocation> absence_allocated;
#define DATE_LEN 10//DD.MM.YYYY
#define SUM_FIELD_NUM 7
#define NUM_PRESSISON 3
#define FLOAT_PRESSISON 6
#define BASE_ABSENCE_WIN_SIZE 1+NUM_PRESSISON+1+NUM_PRESSISON+1+FLOAT_PRESSISON+1+1
#define ABSENCE_WIN_HIGHT 5
constexpr char sum_field_names[SUM_FIELD_NUM] = {'/', 'X','N','P','O', '-', 'D'};
constexpr chtype sum_field_colors[SUM_FIELD_NUM] = {
COLOR_PAIR(COLOR_RED),
COLOR_PAIR(COLOR_GREEN),
COLOR_PAIR(COLOR_RED),
COLOR_PAIR(COLOR_YELLOW),
COLOR_PAIR(COLOR_MAGENTA),
COLOR_PAIR(COLOR_GREEN),
COLOR_PAIR(COLOR_CYAN)
};
constexpr char* sum_field_strings[SUM_FIELD_NUM] = {"Unsolved","Ok","Missed","Late","Soon","School","DistanceTeaching"};
constexpr char date_str[] = "Date";
void absence_page() {
current_allocated = &absence_allocated;
const json resp_from_api = [&]() -> json {
const std::string endpoint = "api/3/absence/student";
return bakaapi::get_data_from_endpoint(endpoint, GET);
}();
/* Initialize curses */
setlocale(LC_ALL, "");
initscr();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
curs_set(0);
/* Initialize all the colors */
for (uint8_t i = 0; i < 8; i++) {
init_pair(i, i, COLOR_BLACK);
}
const uint16_t sum_win_height = (resp_from_api["Absences"].size()*2)+1;
const uint16_t sum_win_width = 1+DATE_LEN+1+(SUM_FIELD_NUM*4);
const float absence_threshold = resp_from_api["PercentageThreshold"].get<float>()*100.0f;
{
WINDOW*sum_win = newwin(sum_win_height,sum_win_width,0,0);
absence_allocated.push_back({WINDOW_TYPE, sum_win, 1});
box(sum_win, 0, 0);
print_in_middle(sum_win, 1, 1, DATE_LEN, date_str, COLOR_PAIR(COLOR_RED));
for (uint16_t i = 0; i < SUM_FIELD_NUM; i++) {
mvwaddch(sum_win, 0, 1+DATE_LEN+(i*4), ACS_TTEE);
mvwaddch(sum_win, 1, 1+DATE_LEN+(i*4), ACS_VLINE);
mvwaddch(sum_win, sum_win_height-1, 1+DATE_LEN+(i*4), ACS_BTEE);
wattron(sum_win, sum_field_colors[i]);
mvwaddch(sum_win, 1, 1+DATE_LEN+2+(i*4),sum_field_names[i]);
wattroff(sum_win, sum_field_colors[i]);
}
for (size_t i = 0; resp_from_api["Absences"].size()-1 > i; i++) {
mvwaddch(sum_win, 2+(i*2), 0, ACS_LTEE);
mvwhline(sum_win, 2+(i*2), 1, ACS_HLINE, DATE_LEN);
mvwaddch(sum_win, 2+(i*2), 1+DATE_LEN, ACS_PLUS);
for(size_t j=0;j<SUM_FIELD_NUM;j++) {
mvwhline(sum_win, 2+(i*2),1+DATE_LEN+1+(j*4), ACS_HLINE,3);
mvwaddch(sum_win, 2+(i*2),1+DATE_LEN+4+(j*4), ACS_PLUS);
mvwaddch(sum_win, 3+(i*2),1+DATE_LEN+(j*4), ACS_VLINE);
mvwprintw(sum_win,3+(i*2),1+DATE_LEN+1+(j*4),"%3d",resp_from_api["Absences"][i][sum_field_strings[j]].get<int>());
}
mvwaddch(sum_win, 2+(i*2), sum_win_width-1, ACS_RTEE);
const std::string date_str = resp_from_api["Absences"][i]["Date"].get<std::string>();
const char* date_cstr = date_str.c_str();
mvwprintw(sum_win, 2+(i*2)+1, 1, "%.2s.%.2s.%.4s", date_cstr+8,date_cstr+5,date_cstr);
}
wrefresh(sum_win);
}
WINDOW** subject_wins = new WINDOW* [resp_from_api["AbsencesPerSubject"].size()];
absence_allocated.push_back({WINDOW_ARRAY, subject_wins, resp_from_api["AbsencesPerSubject"].size()});
uint16_t window_y_offset = 0;
uint16_t window_x_offset = sum_win_width+1;
for (size_t i = 0;resp_from_api["AbsencesPerSubject"].size() > i; i++) {
const uint16_t subject_name_lenght = string_to_wstring(resp_from_api["AbsencesPerSubject"][i]["SubjectName"].get<std::string>()).length();
const uint16_t subject_win_width = 2 + subject_name_lenght > BASE_ABSENCE_WIN_SIZE ? subject_name_lenght+2 : BASE_ABSENCE_WIN_SIZE;
if (window_x_offset + subject_win_width > COLS) {
window_x_offset = sum_win_width+1;
window_y_offset += ABSENCE_WIN_HIGHT;
}
subject_wins[i] = newwin(ABSENCE_WIN_HIGHT,subject_win_width, window_y_offset, window_x_offset);
box(subject_wins[i],0,0);
window_x_offset +=subject_win_width;
const uint16_t absence_per_subject_base = resp_from_api["AbsencesPerSubject"][i]["Base"].get<uint16_t>();
const uint16_t absence_per_subject_lessons_count = resp_from_api["AbsencesPerSubject"][i]["LessonsCount"].get<uint16_t>();
const float absence_per_subject_percentage = ((resp_from_api["AbsencesPerSubject"][i]["Base"].get<float>()/resp_from_api["AbsencesPerSubject"][i]["LessonsCount"].get<float>())*100.0);
mvwaddch(subject_wins[i],2,0,ACS_LTEE);
mvwhline(subject_wins[i],2,1,ACS_HLINE,subject_win_width-2);
mvwaddch(subject_wins[i],2,subject_win_width-1,ACS_RTEE);
mvwaddch(subject_wins[i], 2, 1+NUM_PRESSISON, ACS_TTEE);
mvwaddch(subject_wins[i], 3, 1+NUM_PRESSISON, ACS_VLINE);
mvwaddch(subject_wins[i], 4, 1+NUM_PRESSISON, ACS_BTEE);
mvwaddch(subject_wins[i], 2, 1+(NUM_PRESSISON*2)+1, ACS_TTEE);
mvwaddch(subject_wins[i], 3, 1+(NUM_PRESSISON*2)+1, ACS_VLINE);
mvwaddch(subject_wins[i], 4, 1+(NUM_PRESSISON*2)+1, ACS_BTEE);
if (subject_win_width != BASE_ABSENCE_WIN_SIZE) {
mvwaddch(subject_wins[i], 2, subject_win_width-FLOAT_PRESSISON-3, ACS_TTEE);
mvwaddch(subject_wins[i], 3, subject_win_width-FLOAT_PRESSISON-3, ACS_VLINE);
mvwaddch(subject_wins[i], 4, subject_win_width-FLOAT_PRESSISON-3, ACS_BTEE);
}
mvwprintw(subject_wins[i], 3, 1, "%3d",absence_per_subject_base);
mvwprintw(subject_wins[i], 3, 1+NUM_PRESSISON+1, "%3d",absence_per_subject_lessons_count);
chtype text_color;
if (absence_threshold <= absence_per_subject_percentage) {
text_color = COLOR_PAIR(COLOR_RED);
} else if (absence_threshold*0.6f <= absence_per_subject_percentage) {
text_color = COLOR_PAIR(COLOR_YELLOW);
} else {
text_color = COLOR_PAIR(COLOR_GREEN);
}
wprint_in_middle(subject_wins[i],1, 1, subject_win_width-1, string_to_wstring(resp_from_api["AbsencesPerSubject"][i]["SubjectName"].get<std::string>()).c_str() , text_color);
wattron(subject_wins[i], text_color);
mvwprintw(subject_wins[i], 3, subject_win_width-FLOAT_PRESSISON-1-1, "%6.2f%%",absence_per_subject_percentage);
wattroff(subject_wins[i], text_color);
wrefresh(subject_wins[i]);
}
attron(COLOR_PAIR(4));
mvprintw(LINES-1, 0, "F1 to exit | Absence threshold: %.2f%% ", absence_threshold);
attroff(COLOR_PAIR(4));
for (uint8_t i = 0; i < SUM_FIELD_NUM; i++) {
attron(COLOR_PAIR(4));
addch('|');
attroff(COLOR_PAIR(4));
wattron(stdscr, sum_field_colors[i]);
printw( " %c:%s ", sum_field_names[i], sum_field_strings[i]);
wattroff(stdscr, sum_field_colors[i]);
}
refresh();
while (getch() != KEY_F(1));
delete_all(&absence_allocated);
clear();
}
+2
View File
@@ -0,0 +1,2 @@
#pragma once
void absence_page();
+2 -2
View File
@@ -1,7 +1,7 @@
#include <string_view>
#ifndef VERSION
#define VERSION "0.8.1"
#define VERSION "1.0.0"
#define NAME "bakatui"
inline constexpr auto hash_djb2a(const std::string_view sv) {
@@ -12,7 +12,7 @@ inline constexpr auto hash_djb2a(const std::string_view sv) {
return hash;
}
inline constexpr auto operator"" _sh(const char *str, size_t len) {
inline constexpr auto operator""_sh(const char *str, size_t len) {
return hash_djb2a(std::string_view{str, len});
}
+38 -2
View File
@@ -5,10 +5,12 @@
#include "types.h"
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <curses.h>
#include <cwchar>
#include <future>
#include <menu.h>
#include <ncurses.h>
#include <nlohmann/json.hpp>
@@ -199,12 +201,42 @@ void komens_page(koment_type type) {
clrtoeol();
refresh();
char progress_bar[20];
std::fill(progress_bar, progress_bar + 20, '.');
LimitedInt progress_index(0, 0, sizeof(progress_bar) - 1);
// Download the attachment
bakaapi::download_attachment(
auto future = std::async(
std::launch::async, bakaapi::download_attachment,
resp_from_api["Messages"][item_index(current_item(
komens_choise_menu.menu))]["Attachments"][index]["Id"]
.get<std::string>(),
path);
while (true) {
if (future.wait_for(std::chrono::seconds(1)) ==
std::future_status::ready) {
// Future has completed
int result = future.get();
if (result != 0) {
attron(COLOR_PAIR(COLOR_RED));
mvprintw(LINES - 1, 0, "Download failed with error code: %d",
result);
attroff(COLOR_PAIR(COLOR_RED));
} else {
attron(COLOR_PAIR(COLOR_GREEN));
mvprintw(LINES - 1, 0, "Download completed successfully");
attroff(COLOR_PAIR(COLOR_GREEN));
}
break;
} else {
progress_bar[progress_index] = '#';
// Future is still running
mvprintw(LINES - 1, 0, "%s", progress_bar);
progress_bar[progress_index] = '.';
progress_index++;
}
}
komens_print_usage_message();
}
}
@@ -273,7 +305,7 @@ void insert_content(WINDOW *content_win, WINDOW *attachment_win,
mvwprintw(attachment_win, j + 1, 0, "%zu>", j + 1);
wattroff(attachment_win, COLOR_PAIR(COLOR_MAGENTA));
}
{ // remove duplicating spaces
{ // remove duplicating edges
unsigned short attachment_win_top, attachment_win_left,
attachment_win_height, attachment_win_width;
getbegyx(attachment_win, attachment_win_top, attachment_win_left);
@@ -284,6 +316,10 @@ void insert_content(WINDOW *content_win, WINDOW *attachment_win,
}
refresh();
wrefresh(attachment_win);
} else {
// remove attachment window if there are no attachments
wclear(attachment_win);
wrefresh(attachment_win);
}
}
+3 -1
View File
@@ -98,9 +98,11 @@ void komens_menu() {
komens_page(
static_cast<koment_type>(item_index(current_item(komens_menu.menu))));
current_allocated = &komens_menu_allocated;
clear();
pos_menu_cursor(komens_menu.menu);
refresh();
wrefresh(komens_menu.win);
refresh();
redrawwin(komens_menu.win);
break;
}
wrefresh(komens_menu.win);
+6 -4
View File
@@ -10,17 +10,18 @@
#include <cstring>
#include <curses.h>
#include <menu.h>
#include "absence.h"
std::vector<allocation> main_menu_allocated;
void main_menu() {
current_allocated = &main_menu_allocated;
const wchar_t *choices[] = {
L"login", L"Marks", L"timetable", L"Komens",
L"Marks", L"timetable", L"Komens",
L"Homework", L"Absence", L"Exit", nullptr,
};
void (*choicesFuncs[])() = {nullptr, marks_page, timetable_page, komens_menu,
nullptr, nullptr, nullptr, nullptr};
void (*choicesFuncs[])() = {marks_page, timetable_page, komens_menu,
nullptr, absence_page, nullptr, nullptr};
complete_menu main_menu;
main_menu_allocated.push_back({COMPLETE_MENU_TYPE, &main_menu, 1});
@@ -99,8 +100,9 @@ void main_menu() {
choicesFuncs[item_index(current_item(main_menu.menu))]();
current_allocated = &main_menu_allocated;
pos_menu_cursor(main_menu.menu);
refresh();
wrefresh(main_menu.win);
refresh();
redrawwin(main_menu.win);
break;
}
wrefresh(main_menu.win);
+2
View File
@@ -38,6 +38,7 @@ void win_show(WINDOW *win, const wchar_t *label, const int label_color,
void marks_page() {
current_allocated = &marks_allocated;
curs_set(0);
// thanks to lambda i can make this const
const json resp_from_api = [&]() -> json {
@@ -132,6 +133,7 @@ void marks_page() {
endwin();
clear();
delete_all(&marks_allocated);
curs_set(1);
}
/* Put all the windows */
+6
View File
@@ -187,7 +187,13 @@ void refresh_access_token() {
if (http_code != 200) {
std::cerr << RED "[ERROR] " << RESET << http_code
<< "is non 200 response\n";
def_prog_mode();
endwin();
get_input_and_login();
reset_prog_mode();
refresh();
doupdate();
return;
}
SoRAuthFile(true, response);
+54 -15
View File
@@ -6,6 +6,7 @@
#include "net.h"
#include "types.h"
#include <bits/chrono.h>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <ctime>
@@ -26,6 +27,16 @@ using nlohmann::json;
#define DEFAULT_OFFSET 3
#define REMOVED_COLOR_PAIR COLOR_GREEN
#define ROOMCHANGED_COLOR_PAIR COLOR_MAGENTA
#define SUBSTITUTION_COLOR_PAIR COLOR_YELLOW
#define ADDED_COLOR_PAIR COLOR_BLUE
#define HELP_TEXT \
"Arrows/hjkl to select | ENTER to show info | p/n to select weeks |F1 to " \
"exit"
#define HELP_TEXT_LENGTH sizeof(HELP_TEXT) - 1
std::vector<allocation> timetable_allocated;
const wchar_t *day_abriviations[] = {nullptr, L"Mo", L"Tu", L"We",
@@ -279,9 +290,32 @@ reload_for_new_week:
}
}
attron(COLOR_PAIR(COLOR_BLUE));
mvprintw(LINES - 2, 0,
"Arrows/hjkl to select | ENTER to show info | p/n to select weeks "
"|F1 to exit");
mvprintw(LINES - 2, 0, HELP_TEXT);
attroff(COLOR_PAIR(COLOR_BLUE));
{
constexpr char *change_types[] = {"Canceled/Removed", "RoomChanged",
"Substitution", "Added"};
constexpr uint8_t change_types_colors[] = {
REMOVED_COLOR_PAIR, ROOMCHANGED_COLOR_PAIR, SUBSTITUTION_COLOR_PAIR,
ADDED_COLOR_PAIR};
for (uint8_t i = 0; i < ARRAY_SIZE(change_types); i++) {
init_pair(UCHAR_MAX - i, COLOR_BLACK, change_types_colors[i]);
}
uint8_t text_offset = 1;
for (uint8_t i = 0; i < ARRAY_SIZE(change_types); i++) {
attron(COLOR_PAIR(UCHAR_MAX - i));
mvprintw(LINES - 2, HELP_TEXT_LENGTH + text_offset, "%s",
change_types[i]);
attroff(COLOR_PAIR(UCHAR_MAX - i));
text_offset += strlen(change_types[i]) + 1;
}
}
attron(COLOR_PAIR(COLOR_BLUE));
{
std::tm end_week = local_time;
std::tm start_week = local_time;
@@ -296,13 +330,14 @@ reload_for_new_week:
.get<uint8_t>();
// Calculate days back to start day (handles week wraparound)
int days_back = (current_wday >= start_day)
? (current_wday - start_day)
: (current_wday + 7 - start_day);
uint8_t days_back = (current_wday >= start_day)
? (current_wday - start_day)
: (current_wday + 7 - start_day);
// Calculate days forward to end day (handles week wraparound)
int days_forward = (current_wday <= end_day) ? (end_day - current_wday)
: (end_day + 7 - current_wday);
uint8_t days_forward = (current_wday <= end_day)
? (end_day - current_wday)
: (end_day + 7 - current_wday);
// Adjust dates
start_week.tm_mday -= days_back;
@@ -619,20 +654,24 @@ void draw_cells(uint8_t num_of_columns, uint8_t num_of_days,
hash_djb2a(atom->at("Change")["ChangeType"].get<std::string>())) {
case "Canceled"_sh:
case "Removed"_sh:
wattron(cells[i][j], COLOR_PAIR(COLOR_GREEN));
wattron(cells[i][j], COLOR_PAIR(REMOVED_COLOR_PAIR));
box(cells[i][j], 0, 0);
wattroff(cells[i][j], COLOR_PAIR(COLOR_GREEN));
wattroff(cells[i][j], COLOR_PAIR(REMOVED_COLOR_PAIR));
break;
case "RoomChanged"_sh:
case "Substitution"_sh:
wattron(cells[i][j], COLOR_PAIR(COLOR_YELLOW));
wattron(cells[i][j], COLOR_PAIR(ROOMCHANGED_COLOR_PAIR));
box(cells[i][j], 0, 0);
wattroff(cells[i][j], COLOR_PAIR(COLOR_YELLOW));
wattroff(cells[i][j], COLOR_PAIR(ROOMCHANGED_COLOR_PAIR));
break;
case "Substitution"_sh:
wattron(cells[i][j], COLOR_PAIR(SUBSTITUTION_COLOR_PAIR));
box(cells[i][j], 0, 0);
wattroff(cells[i][j], COLOR_PAIR(SUBSTITUTION_COLOR_PAIR));
break;
case "Added"_sh:
wattron(cells[i][j], COLOR_PAIR(COLOR_BLUE));
wattron(cells[i][j], COLOR_PAIR(ADDED_COLOR_PAIR));
box(cells[i][j], 0, 0);
wattroff(cells[i][j], COLOR_PAIR(COLOR_BLUE));
wattroff(cells[i][j], COLOR_PAIR(ADDED_COLOR_PAIR));
break;
default:
// TODO add error handling