def course_name(self): """Get the full course name of the parsed file. Returns: [str] - - the full name of the course name. """ code = self._splitted_name[0] LOGGER.debug('course code from file: %s', code) # the code is always the first 3 strings in the name course_code, edition = regex.sub(r'([A-Z]{3})([A-Z]{1,3}|\d{1,3})', r'\1 \2', code).split(' ') LOGGER.debug('codice corso, edizione: %s, %s', course_code, edition) courses_names = util.catalog_names("corsi") course_info = courses_names[course_code] self._course_path = f"{course_info['course_path']}/{code}" course = course_info['course_name'] LOGGER.debug('get complete course name: %s', course) self.html_page['course_name'] = course return course
def get_similar_words(wrong_name: str, catalog: str) -> str: """If user writes the wrong name try suggesting valid alternative. Arguments: wrong_name {str} - the not found word that the user has written catalog {str} - the type of list in where to search: c = courses (list of courses) t = teachers (list of teachers) Returns: [str] -- return the correct word choosen by the user. """ if catalog == 'c': check_list = util.catalog_names('corsi').keys() elif catalog == 't': check_list = util.catalog_names('docenti').keys() similar_name = get_close_matches(wrong_name, check_list, cutoff=0.6) possibile_names = [i for i in similar_name] choice = f"- {wrong_name} -> {possibile_names}" return choice
def _check_course_errors(self, filename: str, line_num: int): """Check for errors in the course name. Arguments: filename {str} - filename to parse for errors. line_num {int} - the current line number in the text widget. """ course_match = regex.search(r'^[A-Z]{3}', filename, regex.I) tag = f"c{line_num}" if course_match.group() not in util.catalog_names('corsi').keys(): self._tag_errors(course_match.span(), line_num, tag) self.log_frame.display_msg( get_similar_words(course_match.group(), "c"), self._tag_color(tag)) else: self._tag_remove(tag)
def teacher_name(self): """Get the full name of the teacher. Returns: [str] - - full name of the teacher abbreviation code. """ short_name = '_'.join(self._splitted_name[2:4]) LOGGER.debug('teacher name from file: %s', short_name) teacher_names = util.catalog_names("docenti") full_name = teacher_names[short_name] LOGGER.debug('teacher full name: %s', full_name) self.html_page['teacher_name'] = full_name return full_name
def _check_teacher_errors(self, filename, line_num): """Check for errors in the teacher name. Arguments: filename {str} - filename to parse for errors. line_num {int} - the current line number in the text widget. """ teacher_match = regex.search(r'(?<=_)[A-Z]_[A-Za-z]+', filename) tag = f"t{line_num}" if teacher_match.group() not in util.catalog_names('docenti').keys(): self._tag_errors(teacher_match.span(), line_num, tag) self.log_frame.display_msg( get_similar_words(teacher_match.group(), "t"), self._tag_color(tag)) else: self._tag_remove(tag)
def _split_raw_podcast(): watermark = util.catalog_names()["watermark"] LOGGER.debug("watermark audio: %s", watermark) podcast = pydub.AudioSegment.from_wav(self.abspath) cuts = (util.calculate_cuts(len(podcast)) if not num_cuts else num_cuts) LOGGER.debug("splitting podcast in: [%s] parts", cuts) cut_each = math.ceil(len(podcast) / cuts) podcast_parts = podcast[::cut_each] n_parts = len(self._audio_intro) for part in podcast_parts: export_name = f"{tmp_dir}/{n_parts}-podcast-segment.wav" part.export(export_name, parameters=["-ar", sample_rate], format="wav", bitrate=bitrate) self._audio_intro.append("podcast_segment") self._audio_intro.append(watermark) n_parts += 2
class AudioIntro(tk.Frame): """Audio intro modification section of the gui.""" _catalog = util.catalog_names() _new_audio = [] list_len = len(_catalog["intro"]) def __init__(self, parent, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.audio_catalog = self._catalog self._audio_frame = ttk.Frame(self,) self._audio_frame.grid(column=0, row=0) self._audio_list_frame = ttk.LabelFrame( self._audio_frame, text="Audio intro", padding=1) self._audio_list_frame.grid(column=0, row=0) self.create_combobox() ttk.Button(self._audio_list_frame, text="aggiungi casella", command=self.add_combobox).grid(column=1, row=20) ttk.Button(self._audio_list_frame, text="Salva nuova lista", command=self.new_intro).grid(column=1, row=20, sticky=tk.E, pady=5, padx=5) water_frame = ttk.LabelFrame(self._audio_frame, text="Watermark", padding=1) water_frame.grid(column=0, row=3, sticky=tk.W) self.watermark = ttk.Combobox(water_frame, width=42) self.watermark.grid(column=0, row=4, sticky=tk.E) self.watermark.set(self._catalog["watermark"]) save_watermark = ttk.Button(water_frame, text="Salva nuovo watermark", command=self.new_watermark) save_watermark.grid(column=0, row=5, sticky=tk.E, pady=5, padx=5) create_frame = ttk.LabelFrame( self._audio_frame, text="Crea text audio", padding=1) create_frame.grid(column=0, row=6, sticky=tk.W) ttk.Label(create_frame, text="text:").grid() self.text_entry = ttk.Entry(create_frame, width=40) self.text_entry.grid(column=1, row=0) ttk.Button(create_frame, text="Crea audio", command=self.text_to_audio).grid(column=1, row=1, sticky=tk.E, pady=5, padx=5) ttk.Label(create_frame, text="lang:").grid(column=0, row=1, sticky=tk.W) self._lang_select = ttk.Combobox(create_frame, value=["it", "en"], state="readonly", width=2) self._lang_select.grid(column=1, row=1, sticky=tk.W) self._lang_select.current(0) def text_to_audio(self): """Generate text to audio files.""" ask_user = filedialog.asksaveasfilename() if ask_user: path, filename = os.path.split(ask_user) util.generate_audio(text=self.text_entry.get(), filename=filename, path=path, lang=self._lang_select.get()) def add_combobox(self): """Add new combobox widget if user wants.""" ttk.Combobox(self._audio_list_frame, value=self.const_vars(), width=40).grid(column=1, row=self.list_len) self.list_len += 1 def create_combobox(self): """Create combobox widget from the audio intro list.""" for index, element in enumerate(self.audio_catalog["intro"]): ttk.Label(self._audio_list_frame, text=index + 1).grid(column=0, row=index) combo = ttk.Combobox(self._audio_list_frame, width=40, value=self.const_vars()) combo.grid(column=1, row=index, sticky=tk.W) if element not in self.const_vars(): element = element.replace("_", " ") combo.set(element) def parse_frame(self): """Parse audio list frame for combobox widgets and get their values.""" for widget in self._audio_list_frame.winfo_children(): if "combobox" in widget.winfo_name(): widget_data = widget.get() if widget_data: yield widget_data def new_audio(self, current_intro): """Get the elements in the audio. Get the audio list frame and check if there are new names. Arguments: current_intro [list] - the current audio intro list. """ for name in self.parse_frame(): if name not in current_intro: yield name def generate_new_audio(self, audio_list): """Gnerate new audio files for the intro. Arguments: audio_list {list/tuple} - iterable variable to generate new audio """ path = util.get_path("include/audio/new_audio") for index, name in enumerate(audio_list, 10): msg = f"Generating audio for: {name}" ttk.Label(self._audio_frame, text=msg).grid(column=0, row=index) self.update() util.generate_audio(text=name, path=path) def new_intro(self): """Check if audio intro was modified.""" current_catalog = self._catalog["intro"] new_catalog = self.audio_catalog["intro"] = list(self.parse_frame()) new_intro_list = self.new_audio(current_catalog) self.compare_current(current_catalog, new_catalog, new_intro_list) def new_watermark(self): """Check if current watermark was modified.""" current_watermark = self._catalog["watermark"] new_watermark = self.audio_catalog["watermark"] = self.watermark.get() self.compare_current(current_watermark, new_watermark, [new_watermark]) def compare_current(self, current_value, new_value, audio_list): """Compare if current_value catalog was modified. If yes then create new audio from list and save into catalog. Arguments: current_value - value of catalog dictionary to be compared new_value - value of catalog dictionary to be compared audio_list {list} - a list from where to generate the new audio """ if current_value != new_value: self.generate_new_audio(audio_list) self.save_new() else: messagebox.showinfo(message="Nessuna modifica?", icon="question") def save_new(self): """Save modifications in catalog json file.""" with open(util.catalog_file(), "w") as json_file: json.dump(self.audio_catalog, json_file, indent=True) messagebox.showinfo(title="Done", message="Done!", icon="info") @staticmethod def const_vars(): """Return a list of string placeholders for variables names that can be used in the audio intro. """ return ["$VAR{course_name}", "$VAR{teacher_name}", "$VAR{lesson_number}", "$VAR{part_number}", "$VAR{day}", "$VAR{month}", "$VAR{year}"]
def set_audio_intro(self): """Set the intro audio from the list in the catalog_names json file.""" self.audio_intro = util.catalog_names()["intro"] LOGGER.debug("audio intro files: %s", self.audio_intro)
class CatalogFrame(tk.Frame): """Catalog page of the gui.""" _catalog_list = util.catalog_names() _updated_names = {"docenti": [], "corsi": []} def __init__(self, parent, *args, **kwargs): super().__init__(parent, *args, **kwargs) _catalog_frame = ttk.Frame(self, width=420, height=800) _catalog_frame.grid(column=0, row=0, rowspan=2) self._options_frame = ttk.Frame(self, width=300, height=500) self._options_frame.grid(column=1, row=0, sticky=tk.N) self._options_frame.grid_propagate(False) self._tree_list = ttk.Treeview(_catalog_frame, height=24, columns=("names_short", "names_long")) self._tree_list.grid(column=0, row=0, columnspan=3, rowspan=3) self._generate_tree_columns() # load list frame _label_lista = ttk.LabelFrame(self._options_frame, text="Liste nomi") _label_lista.grid(column=3, row=0, sticky=tk.NW) self._selected_catalog = ttk.Combobox(_label_lista, value=["docenti", "corsi"], width=10, state="readonly") self._selected_catalog.grid(column=0, row=0, pady=10, sticky=tk.E) ttk.Button(_label_lista, text="Carica lista", command=self._load_catalog).grid(column=1, row=0, padx=10) # insert new frame self._insert_frame = ttk.LabelFrame(self._options_frame, text="Aggiungi nuovo nome") self._insert_frame.grid(column=3, row=1, sticky=tk.N) ttk.Label(self._insert_frame, text="abbr.").grid(column=0, row=1, sticky=tk.W) self._short_name = ttk.Entry(self._insert_frame) self._short_name.grid(column=1, row=1) ttk.Label(self._insert_frame, text="intero").grid(column=0, row=2, sticky=tk.W) self._long_name = ttk.Entry(self._insert_frame) self._long_name.grid(column=1, row=2) self._course = None ttk.Label(self._insert_frame, text="lang").grid(column=0, row=4, sticky=tk.W) self._lang_select = ttk.Combobox(self._insert_frame, state="readonly", value=["it", "en"], width=5) self._lang_select.grid(column=1, row=4, sticky=tk.W) self._lang_select.current(0) ttk.Button(self._insert_frame, text="Aggiungi", command=self._insert_to_catalog).grid(column=1, row=4, sticky=tk.E) self._btn_save = ttk.Button(self._options_frame, text="Salva modifiche", state="disabled", command=self._save_new_catalog) self._btn_save.grid(column=3, row=2, pady=15) ttk.Button(self._options_frame, text="Cancella nome selezionato", command=self._delete_selected).grid(column=3, row=3) def _course_path(self): """Add course path to new courses.""" value = ["ALP", "ELM", "EMP", "TTS"] if self._selected_catalog.get() == "corsi": ttk.Label(self._insert_frame, text="corso", name="corse_l").grid(column=0, row=3) self._course = ttk.Combobox(self._insert_frame, value=value, state="readonly", width=5, name="corse_p") self._course.grid(column=1, row=3, sticky=tk.E) # self._course = course.get() if self._selected_catalog.get() == "docenti": for widget in self._insert_frame.winfo_children(): if "corse" in widget.winfo_name(): widget.destroy() @property def _language(self): """Return the language selected: it or eng.""" return self._lang_select.get() def _check_name_size(self, course_name): """Check if name course is 3 letters long otherwise raise error.""" if self.get_catalog == "corsi" and not len(course_name) == 3: raise ValueError("Course name has to be 3 letters long") def _generate_tree_columns(self): """Generate columns for the treeview widget.""" self._tree_list["show"] = "headings" self._tree_list.heading('names_short', text='Nome abbreviato') self._tree_list.column('names_short', width=150) self._tree_list.heading('names_long', text='Nome intero') self._tree_list.column('names_long', width=300) self._tree_list.tag_configure("oddrow", background='gray90') self._tree_list.tag_configure("evenrow", background='gray99') @property def get_catalog(self): """Return the catalog name selected from the combobox widget.""" return self._selected_catalog.get() def _refresh_list(self): """Refresh (delete) list in treeview widget when loading other list.""" self._tree_list.delete(*self._tree_list.get_children()) def _reset_list(self): """When loading catalog, reset modification that have not be saved.""" self._updated_names = {"docenti": [], "corsi": []} self._catalog_list = util.catalog_names() def _load_catalog(self): """Load name list into treeview widget.""" self._reset_list() self._refresh_list() self._course_path() # self._btn_insert["state"] = "active" row_colors = ["oddrow", "evenrow"] catalog_names = sorted(self._catalog_list[self.get_catalog].items()) for index, names in enumerate(catalog_names): if index % 2 == 0: self._tree_list.insert('', index, names[0], tags=(row_colors[index - index])) else: self._tree_list.insert('', index, names[0], tags=(row_colors[index - index + 1])) self._tree_list.set(names[0], 'names_short', names[0]) try: self._tree_list.set(names[0], 'names_long', names[1]["course_name"]) except TypeError: self._tree_list.set(names[0], 'names_long', names[1]) def _delete_selected(self): """Delete selected element from treeview widget.""" try: selected_item = self._tree_list.selection()[0] except IndexError: messagebox.showinfo(title="Cancella", message="Nessun nome selezionato") return confirm = messagebox.askyesno(title="Cancella selezione", message=(f"Cancellare: {selected_item}?") ) if confirm: self._tree_list.delete(selected_item) self._delete_from_catalog(selected_item) self._btn_save["state"] = "active" def _delete_from_catalog(self, delete_key): """Delete key from class catalog list.""" # TODO: currently not deleting the file on disk? self._catalog_list[self.get_catalog].pop(delete_key) def _get_new_names(self): """Return a tuple with new names to insert taken from Entry widget. Return: tuple - short_name and long_name variables. """ if not self._short_name.get() or not self._long_name.get(): messagebox.showinfo(title="Errore", message="Nome invalido") return None _ = self._short_name.get().strip() # TODO: should do better checking of typos example: # if there are two spaces then it will insert 2 trailing if self.get_catalog == "docenti": short_name = _.replace(" ", "_").title() else: short_name = _.replace(" ", "_").upper() del _ long_name = self._long_name.get().strip().title() return (short_name, long_name) def _insert_to_catalog(self): """Insert new name into treeview widget.""" try: short_name, long_name = self._get_new_names() self._check_name_size(short_name) except TypeError: messagebox.showinfo(title="Errore", message="Nessun nome inserito") except tk._tkinter.TclError: messagebox.showerror(title="Errore", message="Nome esiste già!") except ValueError: messagebox.showerror( title="nome corso", message="Nome del corso dovrebbe avere solo 3 lettere") else: if self.get_catalog == "docenti": self._catalog_list[self.get_catalog].update( {short_name: long_name}) else: course_path = self._course.get() if not course_path: messagebox.showerror(message="manca codice corso") return self._catalog_list[self.get_catalog].update( {short_name: {"course_name": long_name, "course_path": course_path}}) self._tree_list.insert("", 0, short_name) self._tree_list.set(short_name, 'names_short', short_name) self._tree_list.set(short_name, 'names_long', long_name) self._updated_names[self.get_catalog].append( [long_name, self._language]) self._btn_save["state"] = "active" def _save_new_catalog(self): """Save new verison of catalog after delete or added new names.""" modification = [_ for _ in self._updated_names.values() if _] if modification: with open(util.catalog_file(), "w") as json_file: json.dump(self._catalog_list, json_file, indent=True) self.update() self._update_audio_library() messagebox.showinfo(message="Modifiche salvate!") else: messagebox.showinfo(message="Nessuna modifica?") def _update_audio_library(self): """Update audio library with deleting or adding the modifications.""" for _, new_names in self._updated_names.items(): for index, new_name in enumerate(new_names, 20): name, lang = new_name msg = f"Generating audio for: \n[{name}]" ttk.Label(self._options_frame, text=msg).grid( column=3, row=index) path = util.get_path("include/audio/new_audio") util.generate_audio(text=name, path=path, lang=lang)
def _reset_list(self): """When loading catalog, reset modification that have not be saved.""" self._updated_names = {"docenti": [], "corsi": []} self._catalog_list = util.catalog_names()