diff --git a/QuickStart.md b/QuickStart.md index cd90de7..97f4c04 100755 --- a/QuickStart.md +++ b/QuickStart.md @@ -23,16 +23,25 @@ pip install -r requirements.txt ``` ## Verwendung - -* Lege die auszulesende Stundenplan-PDF in den Input-Ordner ab (`data/input`) -* ``` - python python-scripts/main.py - ``` -* Es folgt eine Konsolenabfrage zu den gewünschten Modulen. -* Formatierter Stundenplan ist in dem Output-Ordner in Form von einer `.csv`- oder `.ics`-Datei, zu finden (`data/output`) -* Importiere das gewünschte Format in den Kalender -* `.ics` = für Apple Anwendungen -* `.csv` = für Meisten anderen Kalenderanwendungen +* Windows + 1. Führe TTP.exe in release/vX-X-X/ aus. Hinweis: Du musst evtl. die Datei vorher entpacken. + 2. Füge Stundenpläne hinzu, die konvertiert werden sollen. + 3. Setze die Einstellungen, die du willst. + 4. Lese und erkläre dich mit den AGB/TOS einverstanden. Hinweis: Hierfür musst du Englisch verstehen können. + 5. Drücke auf 'Parse Timetable(s)'. + 6. Wähle die Module aus, die in den Kalendar integriert werden sollen. + 7. Drücke auf 'Create Calendar'. + 8. Der Calendar befindet sich im Verzeichnis ./data/output. + +* Andere Betriebssysteme + 1. Führe ```python python-scripts/main.py``` in dem Wurzelverzeichnis aus. + 2. Füge Stundenpläne hinzu, die konvertiert werden sollen. + 3. Setze die Einstellungen, die du willst. + 4. Lese und erkläre dich mit den AGB/TOS einverstanden. Hinweis: Hierfür musst du Englisch verstehen können. + 5. Drücke auf 'Parse Timetable(s)'. + 6. Wähle die Module aus, die in den Kalendar integriert werden sollen. + 7. Drücke auf 'Create Calendar'. + 8. Der Calendar befindet sich im Verzeichnis ./data/output. ## Ordnerstruktur ``` @@ -69,15 +78,25 @@ pip install -r requirements.txt ``` ## Usage -* Save timetable-PDF in input-folder (`data/input`) -* ```bash - python python-scripts/main.py - ``` -* A console query will ask you to choose your modules -* Formatted timetable will be created in form of a `.csv`- or `.ics`-file, depending on your choice, in the output-folder (`data/output`) -* Import your prefered file to your calendar -* `.ics` = for Apple Applications -* `.csv` = for most other Applications +* Windows + 1. Run TTP.exe in release/vX-X-X/. Hint: You might need to unzip it first. + 2. Add Timetables that are going to be parsed. + 3. Adjust settings to your liking. + 4. Read and agree to Terms of Service. + 5. Press 'Parse Timetable(s)'. + 6. Choose desired modules for the calendar. + 7. Press 'Create Calendar'. + 8. The calendar will be in ./data/output. + +* Other OS + 1. Run ```python python-scripts/main.py``` in root-directory. + 2. Add Timetables that are going to be parsed. + 3. Adjust settings to your liking. + 4. Read and agree to Terms of Service. + 5. Press 'Parse Timetable(s)'. + 6. Choose desired modules for the calendar. + 7. Press 'Create Calendar'. + 8. The calendar will be in ./data/output. ## Folder Structure ``` diff --git a/data/input/.keep b/data/input/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/python-scripts/classes/controller/gui_controller.py b/python-scripts/classes/controller/gui_controller.py new file mode 100644 index 0000000..3e4f692 --- /dev/null +++ b/python-scripts/classes/controller/gui_controller.py @@ -0,0 +1,133 @@ +from tkinter import messagebox + +from classes.controller.timetable_controller import TimetableController +from classes.file_management.loader import Loader +from classes.models.timetable import Timetable +from classes.view import gui +from helper.data_handler import get_module_names_from_data_items +from helper.data_item_from_json import data_items_from_json +from helper.data_output import create_ics, create_csv, create_json_from_data_item +from helper.folder_manager import path_leaf, get_cached_file_for +from helper.get_pdf_pages import count_pdf_pages + + +class GuiController: + + def __init__(self): + self.input_files = [] + self.selectable_modules = [] + self._parsed_data_items = [] + + def clear_inputs(self): + self.input_files.clear() + self.selectable_modules.clear() + self._parsed_data_items.clear() + + def create_calendar(self, agreed_to_tos, export_as_ics, selected_module_indices): + if not agreed_to_tos: + messagebox.showerror(gui.title, "You need to agree to the Terms Of Service to do that.") + return + + if len(self._parsed_data_items) == 0: + messagebox.showinfo(gui.title, "There are no modules to select.\nPlease parse a timetable.") + return + + if len(selected_module_indices) == 0: + messagebox.showinfo(gui.title, "There are no modules selected.\n" + "Please select modules to fill the calendar with.") + return + + selected_module_names = [] + for index in selected_module_indices: + selected_module_names.append(self.selectable_modules[index]) + + selected_modules = [] + for data_item in self._parsed_data_items: + if selected_module_names.__contains__(data_item.module): + selected_modules.append(data_item) + + if export_as_ics: + create_ics(selected_modules) + else: + create_csv(selected_modules) + + messagebox.showinfo(gui.title, "Created the calendar. Check the /data/output folder! :)") + + def parse_input_files(self, use_cache, agreed_to_tos): + if not agreed_to_tos: + messagebox.showerror(gui.title, "You need to agree to the Terms Of Service to do that.") + return + + if len(self.input_files) == 0: + messagebox.showinfo(gui.title, "There are no timetables to parse. " + "To parse a timetable add it via the 'Add Timetable'-Button.") + return + + # reset from previous parse + self.selectable_modules = [] + self._parsed_data_items = [] + + messagebox.showinfo(gui.title, "This process may take a while. Grab a something to drink! :)") + + for file in self.input_files: + try: + cached_file = get_cached_file_for(path_leaf(file)) + file_is_parsed_already = cached_file.is_file() + + data_items_in_file = get_data_items_from_file(cached_file) if file_is_parsed_already and use_cache \ + else get_data_items_from_file(file) + + for data_item in data_items_in_file: + self._parsed_data_items.append(data_item) + + modules_in_data_items = get_module_names_from_data_items(data_items_in_file) + for module in modules_in_data_items: + self.selectable_modules.append(module) + + if not (file_is_parsed_already and use_cache): + create_json_from_data_item(data_items_in_file, path_leaf(cached_file).replace('.json', '')) + print(path_leaf(file) + " was successfully parsed.") + + except Exception as e: + messagebox.showerror(gui.title, + "Couldn't parse the timetable '" + path_leaf(file) + "' because:\n" + str(e)) + + messagebox.showinfo(gui.title, "Parsing finished! :)") + + +def get_data_items_from_file(file): + """ + Summary + ------- + Retrieves the data items from the given file. + + Parameter + --------- + file : path # Absolute path of the file that's going to be parsed. + Supports *.json and *.pdf files. + timetable : timetable # The timetable to retrieve the info with. + """ + + data_items = [] + + if str(file).endswith('.pdf'): + # get data items from *.pdf file + timetable = Timetable() + + page_count = count_pdf_pages(file) + for page_index in range(page_count): + timetable_controller = TimetableController(Loader(file, path_leaf(file), page_index), timetable) + timetable_controller.create_timetable_information() + timetable_controller.send_data_to_timetable() + timetable.search_modules() + timetable.get_weeks() + timetable.find_modules(data_items) + + elif str(file).endswith('.json'): + # get data items from *.json file + data_items_from_json(data_items, file) + + else: + throw: Exception("File type not supported. Please provide only *.pdf or *.json files.") + + return data_items diff --git a/python-scripts/classes/models/timetable.py b/python-scripts/classes/models/timetable.py index ce3d204..f7562ea 100755 --- a/python-scripts/classes/models/timetable.py +++ b/python-scripts/classes/models/timetable.py @@ -1,11 +1,12 @@ -from .timetable_information import TimetableInformation -from helper.regex_parser import RegexParser -from classes.file_management.loader import Loader import datetime -from .event_meta import EventMeta + +from classes.file_management.loader import Loader from classes.timetable_parts.week import Week from helper.data_handler import create_event, string_to_datetime +from helper.regex_parser import RegexParser +from .event_meta import EventMeta from .iterable import * +from .timetable_information import TimetableInformation class Timetable: @@ -54,7 +55,7 @@ def __calculate_date_from_week_number(self, week_number): ------- String of current date in format YYYY-MM-DD """ - + total_weeks = self.__get_total_weeks_of_year() # Gets from - to - years, can differ in the winter semester years = self.__timetalbe_information.current_year @@ -82,7 +83,7 @@ def __calculate_date_from_week_number(self, week_number): def __get_total_weeks_of_year(self): gemein_jahre = [2020, 2026, 2032, 2037] total_weeks = 52 - if self.__timetalbe_information.current_year[0]-1 in gemein_jahre: + if self.__timetalbe_information.current_year[0] - 1 in gemein_jahre: total_weeks = 53 return total_weeks @@ -93,7 +94,7 @@ def __fill_weeks(self): self.__weeks = dict() for week in self.__timetalbe_information.week_numbers: self.__weeks[str(week)] = (Week(self.__calculate_date_from_week_number(week), week) - .fill_days(self.__timetalbe_information.week_days_per_week)) + .fill_days(self.__timetalbe_information.week_days_per_week)) def find_modules(self, data: list): """ @@ -104,7 +105,7 @@ def find_modules(self, data: list): # test is a list of all horizontal lines bigger than the default date (sorry for the variable naming) test = RegexParser.extract_modules_pyquery_array(self.__loader.get_file(), - (self.__timetalbe_information.single_time_stamp_width* + (self.__timetalbe_information.single_time_stamp_width * len(self.__timetalbe_information.time_stamp_information)), self.__timetalbe_information.single_time_stamp_width) @@ -146,7 +147,6 @@ def find_modules(self, data: list): # Iterate over dictionary, because one page can contain multiple weeks. Sorry for the naming for key, value in self.__weeks.items(): - # Get current date of day start_date = value.days[item.day_name].date_of_day.strftime("%d/%m/%Y") start_end_time = self.__find_module_time(item) @@ -173,13 +173,14 @@ def __find_module_time(self, item): # Return dictionary with ["end" : "end time", "start" : "start time"] return return_information - def __find_module_info(self, week_day_name : str, box: Box): + def __find_module_info(self, week_day_name: str, box: Box): module_name_box = list() # Definition of outer box, which contains strings - main_box = Box([box.x0, self.__timetalbe_information.week_day_information[week_day_name][1]-self.__word_margin, - box.x1, self.__timetalbe_information.week_day_information[week_day_name][3]]) + main_box = Box( + [box.x0, self.__timetalbe_information.week_day_information[week_day_name][1] - self.__word_margin, + box.x1, self.__timetalbe_information.week_day_information[week_day_name][3]]) m_name_finder_initializer = ModuleNameFinder(main_box) m_name_finder_initializer.min_box = main_box @@ -208,9 +209,9 @@ def __iterator(self, iterable: Iterable): """ # Iterate over PyQuery-Items for item in self.__loader.get_file().pq('LTTextLineHorizontal:in_bbox("' - + str(iterable.main_box.x0)+', '+str(iterable.main_box.y0)+',' - + str(iterable.main_box.x1)+', ' - + str(iterable.main_box.y1)+'")').items(): + + str(iterable.main_box.x0) + ', ' + str(iterable.main_box.y0) + ',' + + str(iterable.main_box.x1) + ', ' + + str(iterable.main_box.y1) + '")').items(): description_box = Box(RegexParser.extract_coordinates_from_bbox(item)) iterable.get_information(BoxContent(description_box, item.text())) diff --git a/python-scripts/classes/view/gui.py b/python-scripts/classes/view/gui.py new file mode 100644 index 0000000..26e0817 --- /dev/null +++ b/python-scripts/classes/view/gui.py @@ -0,0 +1,194 @@ +import pathlib +import tkinter as tk +from tkinter import filedialog + +from classes.controller.gui_controller import GuiController +from helper.folder_manager import path_leaf + +# constants +title = "TimeTableParser" +height = 815 +width = 500 + + +class Gui: + controller = GuiController() + root = tk.Tk() + input_section = tk.Frame(root) + input_files = tk.Frame(input_section) + input_file_list = tk.Listbox(input_files, width=40) + parse_section = tk.Frame(root) + module_chooser = tk.Frame(parse_section) + module_list = tk.Listbox(module_chooser, width=40, selectmode=tk.MULTIPLE) + + def __init__(self): + self.root.title(title) + self.root.iconbitmap(pathlib.Path.cwd().joinpath('res', 'icon.ico')) + self.center_window_on_screen() + self.use_cache = tk.BooleanVar() + self.export_as_ics = tk.BooleanVar() + self.tos = tk.BooleanVar() + + def show(self): + self.build_ui() + self.root.mainloop() + + def add_file(self): + selected_files = filedialog.askopenfilenames(title="Select Timetables", filetypes=[("Timetables", "*.pdf")]) + for file in selected_files: + if file != "" and not self.controller.input_files.__contains__(file): + self.controller.input_files.append(file) + self.refresh_inputs() + + def refresh_inputs(self): + # clear listbox + self.input_file_list.delete(0, tk.END) + + # add files + for input_file in self.controller.input_files: + self.input_file_list.insert(tk.END, path_leaf(input_file)) + + def refresh_modules(self): + # clear data_item_chooser + self.module_list.delete(0, tk.END) + + # add files + for data_item in self.controller.selectable_modules: + self.module_list.insert(tk.END, data_item) + + def select_all_modules(self): + self.module_list.select_set(0, tk.END) + + def deselect_all_modules(self): + self.module_list.selection_clear(0, tk.END) + + def center_window_on_screen(self): + screen_width = self.root.winfo_screenwidth() + screen_height = self.root.winfo_screenheight() + x = (screen_width / 2) - (width / 2) + y = (screen_height / 2) - (height / 2) + self.root.geometry('%dx%d+%d+%d' % (width, height, x, y)) + + def build_ui(self): + # region input section + + self.input_section.pack(pady=10) + self.input_files.pack() + + # label + input_file_label = tk.Label(self.input_files, text="TimeTables", font='Helvetica 10 bold') + input_file_label.pack() + + # list of input files + self.input_file_list.pack(side=tk.LEFT, fill=tk.BOTH) + input_file_list_scrollbar = tk.Scrollbar(self.input_files) + input_file_list_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + input_file_list_scrollbar.config(command=self.input_file_list.yview) + self.input_file_list.config(yscrollcommand=input_file_list_scrollbar.set) + + # input buttons + input_section_buttons = tk.Frame(self.input_section) + input_section_buttons.pack(side=tk.BOTTOM) + add_button = tk.Button(input_section_buttons, text="Add Timetables", command=self.add_file) + add_button.pack(side=tk.LEFT, padx=10) + clear_button = tk.Button(input_section_buttons, text="Clear", + command=lambda: [self.controller.clear_inputs(), + self.refresh_inputs(), + self.refresh_modules()]) + clear_button.pack(side=tk.RIGHT, padx=10) + + # endregion + + # region settings section + settings_section = tk.Frame(self.root) + settings_section.pack(pady=20) + + # label + settings_label = tk.Label(settings_section, text="Settings", font='Helvetica 10 bold') + settings_label.grid(row=0, column=0, columnspan=2) + + # checkboxes + use_cache_checkbox = tk.Checkbutton(settings_section, variable=self.use_cache) + use_cache_checkbox.toggle() + use_cache_checkbox.grid(row=1, column=0) + use_cache_label = tk.Label(settings_section, text="Use previous cache?\n" + "Speeds up process if file was parsed before.") + use_cache_label.grid(row=1, column=1) + + export_ics_checkbox = tk.Checkbutton(settings_section, variable=self.export_as_ics) + export_ics_checkbox.grid(row=2, column=0) + export_ics_label = tk.Label(settings_section, text="Export as .ics-file?\n" + "If unchecked the output type will be csv.") + export_ics_label.grid(row=2, column=1) + + crash_checkbox = tk.Checkbutton(settings_section) + crash_checkbox.grid(row=3, column=0) + crash_label = tk.Label(settings_section, text="Crash randomly?\nThis does not actually do anything.") + crash_label.grid(row=3, column=1) + + # endregion + + # region parse section + self.parse_section.pack(pady=10) + + # parse label + parse_label = tk.Label(self.parse_section, text="Create Calendar", font='Helvetica 10 bold') + parse_label.pack() + + # parse button + parse_button = tk.Button(self.parse_section, text="Parse Timetable(s)", + command=lambda: [self.controller.parse_input_files(self.use_cache.get(), self.tos.get()), + self.refresh_modules()]) + parse_button.pack(pady=5) + + self.module_chooser.pack(fill=tk.X) + module_chooser_label = tk.Label(self.module_chooser, text="Parsed Modules.") + module_chooser_label.pack(side=tk.TOP) + module_chooser_scrollbar = tk.Scrollbar(self.module_chooser) + self.module_list.pack(side=tk.LEFT) + module_chooser_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + self.module_list.config(yscrollcommand=module_chooser_scrollbar.set) + module_chooser_scrollbar.config(command=self.module_list.yview) + + modules_manual = tk.Label(self.parse_section, text="Click item in list to (de-)select it.") + modules_manual.pack() + + select_all_modules_button = tk.Button(self.parse_section, text="Select All", command=self.select_all_modules) + select_all_modules_button.pack(side=tk.LEFT) + + deselect_all_modules_button = \ + tk.Button(self.parse_section, text="Deselect All", command=self.deselect_all_modules) + deselect_all_modules_button.pack(side=tk.RIGHT) + + # Create calendar button + create_calendar_button = tk.Button(self.parse_section, text="Create Calendar", + command=lambda: self.controller.create_calendar( + self.tos.get(), + self.export_as_ics.get(), + self.module_list.curselection()) + ) + create_calendar_button.pack(pady=10) + + # endregion + + # region TOS section + + tos_section = tk.Frame(self.root) + tos_section.pack() + + # TOS checkbox + tos_checkbox = tk.Checkbutton(tos_section, text="Agree to Terms Of Service", variable=self.tos) + tos_checkbox.pack() + + tos_label = tk.Label(tos_section, text="Terms of Service / TOS", font='Helvetica 8 bold') + tos_label.pack() + + tos_label = tk.Label(tos_section, font='Helvetica 8', + text="The contents of the TimeTableParser (TTP) were compiled with the " + "greatest possible care and\nin accordance with in the best of " + "conscience. Nevertheless, the provider of this application does not" + "\nassume any liability for the topicality, completeness and" + " correctness of the content provided.") + tos_label.pack() + + # endregion diff --git a/python-scripts/helper/data_handler.py b/python-scripts/helper/data_handler.py index 023ae09..d0dae9c 100755 --- a/python-scripts/helper/data_handler.py +++ b/python-scripts/helper/data_handler.py @@ -1,7 +1,8 @@ +from datetime import datetime +from typing import List + from classes.data_item import DataItem from classes.dictionary_item import DictionaryItem -from typing import List -from datetime import datetime def create_event(data: List[DataItem], pdf_name: str, module: str, start_datetime: datetime, end_datetime: datetime, @@ -72,6 +73,29 @@ def string_to_datetime(date: str, time: str): return temp_datetime +def get_module_names_from_data_items(data: List[DataItem]): + + """ + Summary + ------- + Creates a list of the module names of given data items. + + Parameter + --------- + data : list # DataItem-list with events + + Returns + ------- + dictionary : list # DictionaryItem-list with module id, module name and pdf name + """ + + module_names = [] + dictionary = create_data_dictionary(data) + for dictionary_item in dictionary: + module_names.append(dictionary_item.module) + return module_names + + def create_data_dictionary(data: List[DataItem]): """ diff --git a/python-scripts/helper/data_item_from_json.py b/python-scripts/helper/data_item_from_json.py index 58765c7..51f3c1c 100755 --- a/python-scripts/helper/data_item_from_json.py +++ b/python-scripts/helper/data_item_from_json.py @@ -1,10 +1,11 @@ import json -from classes.data_item import DataItem from datetime import datetime + +from classes.data_item import DataItem from helper.data_handler import create_event -def decode_data_item(dct)->DataItem: +def decode_data_item(dct) -> DataItem: """ Summary ------- @@ -23,7 +24,7 @@ def decode_data_item(dct)->DataItem: return dct -def data_item_from_json(data_item_list, filepath: str): +def data_items_from_json(data_item_list, filepath: str): """ Summary ------- @@ -34,7 +35,7 @@ def data_item_from_json(data_item_list, filepath: str): filename : str # .json-filename """ - with open(filepath, 'r', encoding="utf-8") as item_data: + with open(filepath, 'r', encoding="ISO-8859-1") as item_data: data = item_data.read() z = json.loads(data, object_hook=decode_data_item) @@ -42,4 +43,3 @@ def data_item_from_json(data_item_list, filepath: str): # TODO Search better way to create list of dataitems create_event(data_item_list, data_item.pdf_name, data_item.module, data_item.start_datetime, data_item.end_datetime, data_item.lecturer, data_item.location) - diff --git a/python-scripts/helper/data_output.py b/python-scripts/helper/data_output.py index a38cd4d..229aeb1 100755 --- a/python-scripts/helper/data_output.py +++ b/python-scripts/helper/data_output.py @@ -1,17 +1,19 @@ import csv -import pytz import json -from ics import Calendar, Event +import pathlib from datetime import datetime -from classes.data_item import DataItem from typing import List -import pathlib + +import pytz +from ics import Calendar, Event + +from classes.data_item import DataItem output_folder = pathlib.Path.cwd().joinpath('data', 'output') cache_folder = pathlib.Path.cwd().joinpath('data', 'cache') -def create_csv(data: List[DataItem], filename: str = "calendar"): +def create_csv(data: List[DataItem], filename: str = "calendar"): """ Summary ------- @@ -47,7 +49,6 @@ def create_csv(data: List[DataItem], filename: str = "calendar"): def create_ics(data: List[DataItem], filename: str = "calendar"): - """ Summary ------- @@ -129,20 +130,11 @@ def create_json_from_data_item(data_item_list: List[DataItem], filename): # Create file from list for x in range(len(data_item_list)): - if data_item_list[x].pdf_name == filename: + if data_item_list[x].pdf_name == filename.replace('.json', '.pdf'): temp_list.append(data_item_list[x]) for x in range(len(temp_list)): json_file.writelines(temp_list[x].to_json()) - if x < (len(temp_list)-1): + if x < (len(temp_list) - 1): json_file.writelines(',') json_file.writelines(']') - - - - - - - - - diff --git a/python-scripts/helper/folder_manager.py b/python-scripts/helper/folder_manager.py index 5761064..61d617d 100755 --- a/python-scripts/helper/folder_manager.py +++ b/python-scripts/helper/folder_manager.py @@ -1,180 +1,68 @@ -import glob -from typing import List -from termcolor import * +import ntpath import os.path +import pathlib import time -from time import mktime from datetime import datetime -import pathlib +from time import mktime +from typing import List + +from termcolor import * input_folder = pathlib.Path.cwd().joinpath('data', 'input') cache_folder = pathlib.Path.cwd().joinpath('data', 'cache') -def read_input_folder(): - input_files = input_folder.rglob('*.pdf') - # input_files = glob.glob(input_folder + "/*.pdf") +def get_cached_file_for(filename): + return pathlib.Path().joinpath(cache_folder, filename.replace('.pdf', '.json')) + +def convert_files(input_files, file_type): files = list() for file in input_files: - temp_file = file.name.replace(".pdf", "") + file_name = path_leaf(file).replace(file_type, "") # temp_file = temp_file.replace(input_folder + "/", "") # Replace for MAC path # temp_file = temp_file.replace(input_folder + "\\", "") # Replace for Windows path - temp_dict = {"file_name": temp_file, "file_path": file, "file_edit": None} - temp_dict["file_edit"] = datetime.fromtimestamp(mktime(time.localtime(os.path.getmtime(temp_dict["file_path"])))) + temp_dict = {"file_name": file_name, "file_path": file, "file_edit": None} + temp_dict["file_edit"] = \ + datetime.fromtimestamp(mktime(time.localtime(os.path.getmtime(temp_dict["file_path"])))) files.append(temp_dict) return files -def read_cache_folder(): - cache_files = cache_folder.rglob('*.json') - # cache_files = glob.glob(cache_folder + "/*.json") - - files = list() - - for file in cache_files: - temp_file = file.name.replace(".json", "") - # temp_file = temp_file.replace(cache_folder + "/", "") # Replace for MAC path - # temp_file = temp_file.replace(cache_folder + "\\", "") # Replace for Windows path - - temp_dict = {"file_name": temp_file, "file_path": file, "file_edit": None} - temp_dict["file_edit"] = datetime.fromtimestamp(mktime(time.localtime(os.path.getmtime(temp_dict["file_path"])))) - - files.append(temp_dict) - - return files - - -def file_handler(input_files: List[dict], cache_files: List[dict]): - - if input_files.__len__() > 0: - - print("Files in input-folder:") - print("\nID\tPDF") - - for index in range(input_files.__len__()): - print(f"{colored((index + 1).__str__(), 'yellow', attrs=['bold'])} \t {input_files[index]['file_name']}") - - print("\n" + colored("Which files do you want to parse?", attrs=['reverse'])) - print("Please select file " + colored("ID", 'yellow', attrs=['bold']) + " to add") - print("To finish process press ('" + colored("D", 'green') + "')") - - temp_input_files = list() - - while True: - user_input = input() - err = 1 - - try: - if user_input == "D" or user_input == "d": - break - - for index in range(input_files.__len__()): - if index == (int(user_input) - 1): - if input_files[index] not in temp_input_files: - temp_input_files.append(input_files[index]) - err = 0 - break - else: - print(colored("File already in list.", 'red')) - err = 0 - break - if err: - print(colored("Could not find file.", 'red')) - except: - print(colored("Could not find file.", 'red')) - - files_to_parse = list() - files_to_load = list() +def read_input_folder(): + return convert_files(input_folder.rglob('*.pdf'), '.pdf') - if cache_files.__len__() > 0: - for input_file in temp_input_files: - file_added = 0 - for save_file in cache_files: - if input_file["file_name"] == save_file["file_name"]: - edit_time = save_file["file_edit"].strftime("%d.%m.%Y - %H:%M") +def read_cache_folder(): + return convert_files(cache_folder.rglob('*.json'), '.json') - print("\n" + colored("Should the file " + colored("(" + input_file["file_name"] + ")", 'white', attrs=['bold', 'reverse']), attrs=['reverse']) - + colored(" (parsed on: " + edit_time + ") be parsed again?", attrs=['reverse'])) - print("Yes ('" + colored("Y", 'green') + "') or No ('" + colored("N", 'green') + "')") - correct_input = 1 - while correct_input: - question_input = input() +def file_handler(input_files: List[dict], cache_files: List[dict], use_cache): - if question_input == 'Y' or question_input == 'y': - correct_input = 0 - files_to_parse.append(input_file) - file_added = 1 + files_to_parse = list() + files_to_load = list() - elif question_input == 'N' or question_input == 'n': - correct_input = 0 - files_to_load.append(save_file) - file_added = 1 - else: - print(colored("Incorrect input!", 'red')) + if cache_files.__len__() > 0: + for input_file in input_files: + for save_file in cache_files: + if input_file["file_name"] == save_file["file_name"]: + if use_cache: + files_to_load.append(save_file) + else: + files_to_parse.append(input_file) - if file_added == 0: - files_to_parse.append(input_file) + else: + for file in input_files: + files_to_parse.append(file) - else: - for file in temp_input_files: - files_to_parse.append(file) + temp_dict = {"files_to_parse": files_to_parse, "files_to_load": files_to_load} + return temp_dict - temp_dict = {"files_to_parse": files_to_parse, "files_to_load": files_to_load} - return temp_dict - else: - if cache_files.__len__() > 0: - - print("No files in input-folder!") - print("\nFiles in cache-folder:") - print("\nID\tPDF") - - for index in range(cache_files.__len__()): - print((index + 1).__str__() + "\t" + cache_files[index]["file_name"]) - - print("\nWhich files do you want to load?") - print("To finish process press 'D'") - - temp_load_files = list() - - while True: - user_input = input() - err = 1 - - try: - if user_input == "D" or user_input == "d": - break - - for index in range(cache_files.__len__()): - if index == (int(user_input) - 1): - if cache_files[index] not in temp_load_files: - temp_load_files.append(cache_files[index]) - err = 0 - break - else: - print("File already in list.") - err = 0 - break - if err: - print("Could not find file.") - except: - print("Could not find file.") - - files_to_parse = list() - files_to_load = list() - - for file in temp_load_files: - files_to_load.append(file) - - temp_dict = {"files_to_parse": files_to_parse, "files_to_load": files_to_load} - return temp_dict - - else: - print("No files found!") +def path_leaf(file_path): + head, tail = ntpath.split(file_path) + return tail or ntpath.basename(head) diff --git a/python-scripts/helper/get_pdf_pages.py b/python-scripts/helper/get_pdf_pages.py index e9ebf58..faa5ae3 100755 --- a/python-scripts/helper/get_pdf_pages.py +++ b/python-scripts/helper/get_pdf_pages.py @@ -1,7 +1,7 @@ from pdfquery import * -def get_pdf_pages(filepath): +def count_pdf_pages(filepath): """ Summary diff --git a/python-scripts/main.py b/python-scripts/main.py index e91cd2c..a0e9de5 100755 --- a/python-scripts/main.py +++ b/python-scripts/main.py @@ -1,66 +1,8 @@ -from classes.file_management.loader import Loader -from classes.controller.timetable_controller import TimetableController -from classes.models.timetable import Timetable -from helper.data_handler import create_data_dictionary -from helper.terminal_interface import show_application_disclaimer, user_select, format_select -from helper.data_handler import filter_data_list -from helper.progress_info import ProgressAnimation -from helper.get_pdf_pages import get_pdf_pages -from helper.folder_manager import read_input_folder, read_cache_folder, file_handler -from helper.data_output import * -from helper.data_item_from_json import data_item_from_json -from termcolor import * -import colorama +from classes.view.gui import Gui -def main(): - colorama.init() - show_application_disclaimer() - - data = list() - - files = file_handler(read_input_folder(), read_cache_folder()) - - # security question! - if files is not None: - if files["files_to_parse"].__len__() == 0 and files["files_to_load"].__len__() == 0: - print(f'\n {colored("No file selected, program shutdown!", "red")}') - return - - if files["files_to_parse"].__len__() > 0: - print('Start Parsing (This will take a moment, grab a coffee!)') - - for file_index in range(files["files_to_parse"].__len__()): - page_count = get_pdf_pages(files["files_to_parse"][file_index]["file_path"]) - animation = ProgressAnimation(files["files_to_parse"].__len__(), file_index + 1, - files["files_to_parse"][file_index]["file_name"], page_count) - - for page in range(page_count): - - animation.set_current_page(page + 1) - file_container = Loader(files["files_to_parse"][file_index]["file_path"], - files["files_to_parse"][file_index]["file_name"], page) - - timetable = Timetable() - - t_controller = TimetableController(file_container, timetable) - t_controller.create_timetable_information() - t_controller.send_data_to_timetable() - - timetable.search_modules() - timetable.get_weeks() - timetable.find_modules(data) - - create_json_from_data_item(data, files["files_to_parse"][file_index]["file_name"]) - animation.thread_progress_animation_end() - print("") # Empty line after parsing - - for file_index in range(files["files_to_load"].__len__()): - data_item_from_json(data, files["files_to_load"][file_index]["file_path"]) - - filter_data_list(data, user_select(create_data_dictionary(data))) - - format_select(data) +def main(): + Gui().show() if __name__ == "__main__": diff --git a/release/v0-0-2.zip b/release/v0-0-2.zip new file mode 100644 index 0000000..8ef3563 Binary files /dev/null and b/release/v0-0-2.zip differ diff --git a/res/icon.ico b/res/icon.ico new file mode 100644 index 0000000..f06bbea Binary files /dev/null and b/res/icon.ico differ