Skip to content

read_orders #

Functions and classes for reading in order information from the excel sheet containing responses from the Microsoft form

Classes:

  • Order

    Class to hold information about a single specific order

Functions:

  • extract_orders

    Read in the order response form as a pandas DataFrame.

  • read_order

    Obtain the order information for a specific person

Order #

Order(
    email: str,
    name: str,
    items: List[str],
    sizings: List[str],
    back_names: List[str],
    sleeve_names: List[str],
)

Class to hold information about a single specific order

Parameters#

email : str Email address of person placing the order name : str Name of the person placing the order items : list Ordered items sizings : list Sizing information (XS, S, .., 4XL) back_names : list Personalisations to be printed on the back of the item sleeve_names : list Initials to be printed on the sleeve

Returns#

None

Methods:

Source code in plkit/read_orders.py
def __init__(
    self,
    email: str,
    name: str,
    items: _List[str],
    sizings: _List[str],
    back_names: _List[str],
    sleeve_names: _List[str],
) -> None:
    """
    Initialise the Order class

    Parameters
    ----------
    email : str
        Email address of person placing the order
    name : str
        Name of the person placing the order
    items : list
        Ordered items
    sizings : list
        Sizing information (XS, S, .., 4XL)
    back_names : list
        Personalisations to be printed on the back of the item
    sleeve_names : list
        Initials to be printed on the sleeve

    Returns
    -------
    None
    """
    self.email = email
    self.name = name
    self.items = items
    self.sizings = sizings
    self.back_names = back_names
    self.sleeve_names = sleeve_names
    self.products = [
        _np.nan
    ] * 5  # Product information is empty, extract with function identify_products()
    self.price = _np.nan # Price is initially empty, calculate from products

    if not (len(items) == len(sizings) == len(back_names) == len(sleeve_names)):
        raise ValueError("Mismatch in items input!")

    # List to store number of personalisations
    n_personalisations = [_np.nan] * 5

    for n in range(len(items)):
        if isinstance(items[n], str):
            n_personalisations[n] = 0

    for n in range(len(back_names)):
        if isinstance(back_names[n], str):
            n_personalisations[n] += 1

    for n in range(len(sleeve_names)):
        if isinstance(sleeve_names[n], str):
            n_personalisations[n] += 1

    self.n_personalisations = n_personalisations

identify_products #

identify_products() -> None

Assign a product name to each item in the order

Source code in plkit/read_orders.py
def identify_products(self) -> None:
    """
    Assign a product name to each item in the order
    """

    products = [_np.nan] * len(self.items)

    for n in range(len(self.items)):
        item = self.items[n]

        if isinstance(item, str):
            # Extract how many personalisations an item has
            n_personal = self.n_personalisations[n]

            if n_personal == 0:
                products[n] = item

            if n_personal == 1:
                products[n] = item + " - 1 Personalisation"

            if n_personal == 2:
                products[n] = item + " - 2 Personalisations"

    # Replace 'Green' with 'Forest'
    products = [
        product.replace("Green", "Forest") if isinstance(product, str) else product
        for product in products
    ]

    # Move colour to the end of the product name
    for i, product in enumerate(products):
        if isinstance(product, str):
            for colour in ["Forest", "Navy"]:
                if f"({colour})" in product:
                    products[i] = (
                        product.replace(f"({colour})", "").strip() + f" ({colour})"
                    )

    # Clean up double spacing in product name
    self.products = [
        product.replace("  ", " ").strip() if isinstance(product, str) else product
        for product in products
    ]

update_pricing #

update_pricing() -> None

Calculate the total price of a single person's order

Source code in plkit/read_orders.py
def update_pricing(self) -> None:
    """
    Calculate the total price of a single person's
    order
    """
    # unit pricing including VAT
    pricing_info = {
                "Unisex EcoLayer Hoodie": 38.40,  # pounds
                "Unisex EcoLayer Hoodie - 1 Personalisation": 42.60,
                "Unisex EcoLayer Hoodie - 2 Personalisations": 46.80,
                "Unisex Shield Performance Sweatshirt": 36.0,
                "Unisex Shield Performance Sweatshirt - 1 Personalisation": 40.20,
                "Unisex Shield Performance Sweatshirt - 2 Personalisations": 44.40,
                "Men's EcoLayer Tee (Navy)": 18.60,
                "Men's EcoLayer Tee - 1 Personalisation (Navy)": 22.80,
                "Men's EcoLayer Tee - 2 Personalisations (Navy)": 27.0,
                "Men's EcoLayer Tee (Forest)": 18.60,
                "Men's EcoLayer Tee - 1 Personalisation (Forest)": 22.80,
                "Men's EcoLayer Tee - 2 Personalisations (Forest)": 27.0,
                "Women's EcoLayer Tee (Navy)": 18.60,
                "Women's EcoLayer Tee - 1 Personalisation (Navy)": 22.80,
                "Women's EcoLayer Tee - 2 Personalisations (Navy)": 27.0,
                "Women's EcoLayer Tee (Forest)": 18.60,
                "Women's EcoLayer Tee - 1 Personalisation (Forest)": 22.80,
                "Women's EcoLayer Tee - 2 Personalisations (Forest)": 27.0,
                "Men's Sublimated Tee (Navy)": 25.62,
                "Men's Sublimated Tee - 1 Personalisation (Navy)": 25.62,
                "Men's Sublimated Tee - 2 Personalisations (Navy)": 25.62,
                "Men's Sublimated Tee (Forest)": 25.62,
                "Men's Sublimated Tee - 1 Personalisation (Forest)": 25.62,
                "Men's Sublimated Tee - 2 Personalisations (Forest)": 25.62,
                "Women's Sublimated Tee (Navy)": 25.62,
                "Women's Sublimated Tee - 1 Personalisation (Navy)": 25.62,
                "Women's Sublimated Tee - 2 Personalisations (Navy)": 25.62,
                "Women's Sublimated Tee (Forest)": 25.62,
                "Women's Sublimated Tee - 1 Personalisation (Forest)": 25.62,
                "Women's Sublimated Tee - 2 Personalisations (Forest)": 25.62
            }

    price = 0

    # Update the product names
    self.identify_products()

    for product in self.products:
        if product in pricing_info.keys():
            price += pricing_info[product]

    self.price = price

extract_orders #

extract_orders(
    filename: str = "responses.xlsx",
) -> DataFrame

Read in the order response form as a pandas DataFrame.

Parameters#

filename : str, optional The name of the responses form saved from Microsoft forms

Returns#

df_orders : pd.DataFrame The order details converted to a pandas DataFrame

Source code in plkit/read_orders.py
def extract_orders(filename: str = "responses.xlsx") -> _pd.DataFrame:
    """
    Read in the order response form as a pandas DataFrame.

    Parameters
    ----------
    filename : str, optional
        The name of the responses form saved from Microsoft forms

    Returns
    -------
    df_orders : pd.DataFrame
        The order details converted to a pandas DataFrame
    """

    if not filename.endswith(".xlsx"):
        raise ValueError("Input must be an Excel File")

    if not _os.path.isfile(filename):
        raise FileNotFoundError(f"File {filename} does not exist")

    try:
        df_orders = _pd.read_excel(filename)
    except _pd.errors.EmptyDataError as e:
        raise _pd.errors.EmptyDataError(f"The file {filename} is empty") from e
    except Exception as e:
        raise Exception(f"An error occurred: {e}") from e

    # Clean hidden characters
    df_orders['Name'] = df_orders['Name'].apply(_clean_string)
    df_orders['Email'] = df_orders['Email'].apply(_clean_string)

    for number in ['First', 'Second', 'Third', 'Fourth', 'Fifth']:
        column_name = f'{number} kit item'
        df_orders[column_name] = df_orders[column_name].apply(_clean_string)
        column_name = f'{number} item - name personalisation for back (optional)'
        df_orders[column_name] = df_orders[column_name].apply(_clean_string)
        column_name = (f'{number} item - personalisation for initials (optional, '
        'max two letters)')
        df_orders[column_name] = df_orders[column_name].apply(_clean_string)

    for number in ['first', 'second', 'third', 'fourth', 'fifth']:
        column_name = (f"Sizing for {number} kit item (note that for women's tee, "
        "XS=size 6, S=size 8, ... , 4XL=20)"
        )
        df_orders[column_name] = df_orders[column_name].apply(_clean_string)

    return df_orders

read_order #

read_order(
    df_orders: DataFrame, name: str, email: str = None
)

Obtain the order information for a specific person

Parameters#

df_orders: pd.DataFrame The pandas DataFrame containing all the order information name : str The name of the person placing the order email : str, optional The email address of the person placing the order

Returns#

order : class Instance of the Order class for the specified name

Source code in plkit/read_orders.py
def read_order(df_orders: _pd.DataFrame, name: str, email: str = None):
    """
    Obtain the order information for a specific person

    Parameters
    ----------
    df_orders: pd.DataFrame
        The pandas DataFrame containing all the order information
    name : str
        The name of the person placing the order
    email : str, optional
        The email address of the person placing the order

    Returns
    -------
    order : class
        Instance of the Order class for the specified name
    """
    name = name.strip()

    # Check that names column exists
    if "Name" not in df_orders.columns:
        raise LookupError("Name column not found in input DataFrame")
    else:
        names = df_orders["Name"].to_list()

    # Check that email column exists
    if "Email" not in df_orders.columns:
        raise LookupError("Email column not found in input DataFrame")

    # Extract email if not specified
    if isinstance(email, str):
        email = email.strip()
    else:
        email = df_orders.loc[df_orders["Name"] == name, "Email"].iloc[0]

    name_count = names.count(name)

    if name_count == 0:
        raise LookupError(f"Name {name} not found!")
    elif name_count == 1:
        idx = df_orders[
            df_orders["Name"] == name
        ].index  # Only use email unless if are two identical names
    else:
        idx = df_orders[
            (df_orders["Name"] == name) & (df_orders["Email"] == email)
        ].index

    # Initialise class
    order_info = Order(
        email=email,
        name=name,
        items=_extract_items(df_orders, idx),
        sizings=_extract_sizings(df_orders, idx),
        back_names=_extract_back_names(df_orders, idx),
        sleeve_names=_extract_sleeve_names(df_orders, idx),
    )

    return order_info