3
\$\begingroup\$

I wrote a simple word-counter in Python a while ago, capable of processing DOCX, XLSX, TXT, and some PDF files. Now I gave it a simple GUI - this is my first tkinter program ever. What do you think?

import openpyxl
import docx2txt
import PyPDF2
import tkinter
from tkinter import filedialog


def excel_counter(filename):
    count = 0
    wb = openpyxl.load_workbook(filename)
    for sheet in wb:
        for row in sheet:
            for cell in row:
                text = str(cell.value)
                if text != "None":
                    word_list = text.split()
                    count += len(word_list)
    return count


def pdf_counter(filename):
    pdf_word_count = 0
    pdfFileObj = open(filename, "rb")
    pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
    number_of_pages = pdfReader.getNumPages() - 1
    for page in range(0, number_of_pages + 1):
        page_contents = pdfReader.getPage(page - 1)
        raw_text = page_contents.extractText()
        text = raw_text.encode('utf-8')
        page_word_count = len(text.split())
        pdf_word_count += page_word_count
    return pdf_word_count


def uploadaction(event=None):
    root = tkinter.Tk()
    root.destroy()
    filename = filedialog.askopenfilename()
    return filename


def wordcounter(file):
    extension = file.split(".")[-1]
    if extension == "xlsx":
        current_count = excel_counter(file)
        return current_count
    if extension == "docx":
        text = docx2txt.process(file)
        current_count = len(text.split())
        return current_count
    if extension == "txt":
        f = open(file, "r")
        text = f.read()
        current_count = len(text.split())
        return current_count
    if extension == "pdf":
        pdf_word_count = pdf_counter(file)
        return pdf_word_count


window = tkinter.Tk()
wordcount = wordcounter(uploadaction())
label = tkinter.Label(text=f"Word Count: {wordcount}")
label.pack()
window.mainloop()
\$\endgroup\$
2
\$\begingroup\$

One suggestion is to simplify your top-level function by taking advantage of a library and using a data structure to eliminate conditional logic.

import pathlib

def wordcounter(file_path):
    # Let a built-in library solve this problem.
    extension = pathlib.Path(file_path).suffix

    # Hold the counter functions in a data structure.
    # You'll need to create a couple of these.
    counters = {
        'xlsx': excel_counter,
        'docx': word_counter,
        'txt': txt_counter,
        'pdf': pdf_counter,
    }

    # Then just do what you want directly rather than checking conditions.
    return counters[extension](file_path)

Another observation is that your counter functions are duplicating the word counting logic. Such duplication is often a signal that there are refactoring possibilities: more specifically, the thing being duplicated might belong in its own function. If we do that, you'll need to convert your current counter functions (which take a path and return an integer) into reader functions (take a path and yield chunks of text). Such a refactor is illustrated for excel_reader. And those changes will imply a few simple adjustments in wordcounter.

def count_words(reader, file_path):
    n = 0
    for text in reader(file_path):
        n += len(text.split())
    return n

def excel_reader(file_path):
    wb = openpyxl.load_workbook(filename)
    for sheet in wb:
        for row in sheet:
            for cell in row:
                # Your current logic.
                text = str(cell.value)
                if text != "None":
                    yield text

                # I've never used openpxl, but a quick internet
                # search suggests that this is more robust conditional
                # check to filter out empty cells.
                if cell.value is not None:
                    yield str(cell.value)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.