Skip to content

Commit

Permalink
Merge pull request #32 from Piyuuussshhh/Python-scripts
Browse files Browse the repository at this point in the history
Python scripts
  • Loading branch information
Piyuuussshhh authored Jul 12, 2024
2 parents d9f9790 + 564638e commit 2b6bbf9
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 12 deletions.
28 changes: 28 additions & 0 deletions scripts/task_view_to_pdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json
import sys
import tkinter as tk
from tkinter import filedialog
from weasyprint import HTML, CSS

def create_detailed_pdf(file_path, pdf_html, pdf_css):
html = HTML(string=pdf_html)
css = CSS(string=pdf_css)
html.write_pdf(file_path, stylesheets=[css])

# Create the main window (it will not be displayed)
root = tk.Tk()
root.withdraw() # Hide the main window

# Open the file dialog to choose the save location and filename
file_path = filedialog.asksaveasfilename(
defaultextension=".pdf",
filetypes=[("PDF files", "*.pdf")],
title="Choose location to save the PDF"
)

# Check if the user provided a file path
if file_path:
pdf_html = sys.argv[1]
pdf_css = sys.argv[2]

create_detailed_pdf(file_path, pdf_html, pdf_css)
49 changes: 47 additions & 2 deletions src-tauri/src/db/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::Mutex;
const ROOT_GROUP: &str = "/";
const TASK: &str = "Task";
const TASK_GROUP: &str = "TaskGroup";
const TODAY: &str = "today";

// Singleton because we need a single point of access to the db across the app.
// Mutex because only one process should be able to use the singleton at a time,
Expand All @@ -20,7 +21,7 @@ use rusqlite::{params, Connection, Result};
use serde::Serialize;
use std::{collections::HashMap, path::PathBuf};

#[derive(Serialize, Debug, Clone, Copy)]
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq)]
#[serde(tag = "type")]
pub enum Type {
Task,
Expand Down Expand Up @@ -365,12 +366,14 @@ impl Db {
}

pub mod crud_commands {
use core::panic;
use std::sync::MutexGuard;

use rusqlite::{params, Connection, Result};
use serde::{Deserialize, Serialize};

use super::{FetchBasis, Type, DB_SINGLETON, TASK, TASK_GROUP};

use super::{FetchBasis, Type, DB_SINGLETON, TASK, TASK_GROUP, TODAY};

#[tauri::command]
/// C(R)UD - Reads the database and sends appropriate structure to the frontend.
Expand Down Expand Up @@ -575,4 +578,46 @@ pub mod crud_commands {
),
}
}

/// C(R)UD - Reads database and gets all tasks' names and their parent_group_ids.
pub fn get_all_tasks(fetch_basis: FetchBasis) -> Vec<(u64, String)> {
let db = DB_SINGLETON.lock().unwrap();
let mut all_tasks: Vec<(u64, String)> = Vec::new();

match db.fetch_records(TODAY, fetch_basis) {
Ok(records) => {
all_tasks.extend(
records
.into_iter()
.filter(|(_, _, record_type, _, _)| *record_type != Type::TaskGroup)
.map(|(_, name, _, _, parent_id)| {
if let Some(id) = parent_id {
(id, name)
} else {
panic!("[ERROR] While fetching task names, the task: [{name}] had no parent.");
}
}),
);
}
Err(err) => panic!("{err}"),
}

all_tasks
}

pub fn get_item(id: u64) -> String {
let db = DB_SINGLETON.lock().unwrap();
let mut name = String::new();

if let Some(conn) = &db.db_conn {
let res: Option<String> = match conn.query_row("SELECT name FROM today WHERE id=(?1)", params![id], |row| row.get(0)) {
Ok(name) => name,
Err(err) => panic!("{err}"),
};

name = res.unwrap();
}

name
}
}
147 changes: 147 additions & 0 deletions src-tauri/src/export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use std::{collections::HashMap, process::Command};

use crate::db::ops::{
crud_commands::{get_all_tasks, get_item},
FetchBasis,
};

fn get_python_input(tasks: &[(u64, String)]) -> Vec<(String, String)> {
let mut res: Vec<(String, String)> = Vec::new();
for (parent_id, name) in tasks.iter() {
let parent_name = get_item(*parent_id);
res.push((parent_name, name.clone()));
}
res
}

fn map_parent_to_tasks(task_list: Vec<(String, String)>) -> HashMap<String, Vec<String>> {
let mut res: HashMap<String, Vec<String>> = HashMap::new();

for (parent, task) in task_list {
res.entry(parent).or_insert(Vec::new()).push(task);
}

res
}

fn generate_ordered_list(map: HashMap<String, Vec<String>>, is_completed: bool) -> String {
let mut html = String::from("<ul>");

let checked = match is_completed {
true => "checked",
false => "",
};

for (parent, tasks) in map.into_iter() {
match parent.as_str() {
"/" => {
html.push_str(&format!(
"{}",
tasks.into_iter()
.map(|task| format!(
"<li><div class=\"sub-task\"><input type=\"checkbox\" {checked}/><span>{task}</span></div></li>"
))
.collect::<Vec<String>>()
.concat()
));
}
_ => {
html.push_str(&format!(
"<li><h3>{parent}</h3><ul>{}</ul></li>",
tasks.into_iter()
.map(|task| format!("<li><div class=\"sub-task\"><input type=\"checkbox\" {checked}/><span>{task}</span></div></li>"))
.collect::<Vec<String>>()
.concat()
));
}
}
}

html.push_str("</ul>");
html
}

#[tauri::command]
pub fn export_to_pdf() {
let active_tasks = get_all_tasks(FetchBasis::Active);
let completed_tasks = get_all_tasks(FetchBasis::Completed);

let active_tasks_inp: Vec<(String, String)> = get_python_input(&active_tasks);
let completed_tasks_inp: Vec<(String, String)> = get_python_input(&completed_tasks);

let pdf_html = format!(
"<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\" />
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
<title>Tasks for Today</title>
<body>
<h1>Active Tasks</h1>
{}
<div class=\"page-break\"></div>
<h1>Completed Tasks</h1>
{}
</body>
</head>",
generate_ordered_list(map_parent_to_tasks(active_tasks_inp), false),
generate_ordered_list(map_parent_to_tasks(completed_tasks_inp), true)
);

let pdf_css = format!(
"
@import url(\"https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap\");
body {{
font-family: 'Inter', sans-serif;
}}
h1 {{
text-align: center;
}}
ul {{
list-style-type: none;
}}
li {{
margin-top: 20px;
margin-bottom: 20px;
}}
p {{
font-weight: bold;
}}
input:checked:after {{
color: black;
content: '✔';
}}
.sub-task {{
display: flex;
flex-direction: row;
justify-items: space-between;
align-items: center;
}}
span {{
margin-left: 10px;
}}
.page-break {{
page-break-after: always;
}}
"
);

let output = Command::new("python3")
.arg("../scripts/task_view_to_pdf.py")
.args([pdf_html, pdf_css])
.output()
.expect("ok the python script idea didn't work");

if !output.status.success() {
eprintln!("[ERROR]: {}", String::from_utf8_lossy(&output.stderr));
}
}
3 changes: 2 additions & 1 deletion src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod db;
pub mod window;
pub mod window;
pub mod export;
3 changes: 2 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command

use habtrack::{db::{init::DbInitializer, ops}, window};
use habtrack::{db::{init::DbInitializer, ops}, window, export};

// Start work on database.
// TODO: Once done, merge with main, and pull changes into FEATURE-add-delete-task.
Expand All @@ -27,6 +27,7 @@ fn main() {
ops::crud_commands::update_item,
window::open_tomorrow_window,
window::close_tomorrow_window,
export::export_to_pdf,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
6 changes: 6 additions & 0 deletions src/Constants.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const TAURI_UPDATE_ITEM = "update_item";
const TAURI_OPEN_TOMORROW_WINDOW = "open_tomorrow_window";
const TAURI_CLOSE_TOMORROW_WINDOW = "close_tomorrow_window";

// Export.
const TAURI_EXPORT_TO_PDF = "export_to_pdf";

export {
// Table names
TODAY,
Expand Down Expand Up @@ -53,4 +56,7 @@ export {
// New Window,
TAURI_OPEN_TOMORROW_WINDOW,
TAURI_CLOSE_TOMORROW_WINDOW,

// Export
TAURI_EXPORT_TO_PDF,
};
8 changes: 7 additions & 1 deletion src/views/TasksView/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import React from "react";
import PlaylistAddCheckIcon from "@mui/icons-material/PlaylistAddCheck";
import FastForwardIcon from "@mui/icons-material/FastForward";
import AddIcon from "@mui/icons-material/Add";
import { Download } from "@mui/icons-material";
import { invoke } from "@tauri-apps/api";
import { ROOT, TAURI_OPEN_TOMORROW_WINDOW } from "../../Constants";

// TODO: style this component.

const Navbar = ({ isSidebarOpen, onAdd, toggleCompleted }) => {
const Navbar = ({ isSidebarOpen, onExport, onAdd, toggleCompleted }) => {
const seeTomorrow = async () => {
try {
await invoke(TAURI_OPEN_TOMORROW_WINDOW);
Expand All @@ -29,6 +30,11 @@ const Navbar = ({ isSidebarOpen, onAdd, toggleCompleted }) => {
Tasks
</p>
<ul>
<li>
<button className="btn" title="Export to PDF" onClick={onExport}>
<Download />
</button>
</li>
<li>
<button className="btn" title="Set Tasks for Tomorrow" onClick={seeTomorrow}>
<FastForwardIcon />
Expand Down
Loading

0 comments on commit 2b6bbf9

Please sign in to comment.