Skip to content
This repository has been archived by the owner on Nov 16, 2024. It is now read-only.

Raise-strings #44

Merged
merged 3 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
// Set *default* container specific settings.json values on container create.
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.enabled": false,
"python.linting.pylintEnabled": false,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
Expand All @@ -37,10 +37,6 @@
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"GitHub.codespaces",
"GitHub.remotehub",
"ms-vscode-remote.vscode-remote-extensionpack",
"ms-vscode.remote-repositories",
"redhat.vscode-yaml",
"mtxr.sqltools-driver-sqlite",
"streetsidesoftware.code-spell-checker",
Expand All @@ -62,8 +58,7 @@
"features": {
"docker-in-docker": "latest",
"git": "os-provided",
"github-cli": "latest",
"jupyterlab": "latest"
"github-cli": "latest"
},
"remoteEnv": {
"NPBC_DATABASE_DIR": "data"
Expand Down
18 changes: 18 additions & 0 deletions npbc_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ def status_print(success: bool, message: str) -> None:
colour = Fore.GREEN if success else Fore.RED
print(f"{colour}{Style.BRIGHT}{message}{Style.RESET_ALL}\n")

return


def calculate(parsed_arguments: ArgNamespace, connection: Connection) -> None:
"""calculate the cost for a given month and year
Expand Down Expand Up @@ -267,6 +269,7 @@ def calculate(parsed_arguments: ArgNamespace, connection: Connection) -> None:
# print the results
status_print(True, "Success!")
print(f"SUMMARY:\n\n{formatted}")
return


def addudl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand Down Expand Up @@ -315,6 +318,7 @@ def addudl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
return

status_print(True, "Success!")
return


def deludl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand Down Expand Up @@ -356,6 +360,7 @@ def deludl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
return

status_print(True, "Success!")
return


def getudl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand Down Expand Up @@ -399,6 +404,8 @@ def getudl(parsed_arguments: ArgNamespace, connection: Connection) -> None:
for items in undelivered_strings:
print(', '.join([str(item) for item in items]))

return


def extract_delivery_from_user_input(input_delivery: str) -> list[bool]:
"""convert the /[YN]{7}/ user input to a Boolean list"""
Expand Down Expand Up @@ -499,6 +506,7 @@ def editpaper(parsed_arguments: ArgNamespace, connection: Connection) -> None:
return

status_print(True, "Success!")
return


def addpaper(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand Down Expand Up @@ -532,6 +540,7 @@ def addpaper(parsed_arguments: ArgNamespace, connection: Connection) -> None:
return

status_print(True, "Success!")
return


def delpaper(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand All @@ -552,6 +561,7 @@ def delpaper(parsed_arguments: ArgNamespace, connection: Connection) -> None:
return

status_print(True, "Success!")
return


def getpapers(parsed_arguments: ArgNamespace, connection: Connection) -> None:
Expand Down Expand Up @@ -661,6 +671,8 @@ def getpapers(parsed_arguments: ArgNamespace, connection: Connection) -> None:

print()

return


def getlogs(parsed_arguments: ArgNamespace, connection: Connection) -> None:
"""get a list of all logs in the database
Expand Down Expand Up @@ -699,20 +711,24 @@ def getlogs(parsed_arguments: ArgNamespace, connection: Connection) -> None:
for row in data:
print(', '.join(str(item) for item in row))

return


def update(parsed_arguments: ArgNamespace, _: Connection) -> None:
"""update the application
- under normal operation, this function should never run
- if the update CLI argument is provided, this script will never run and the updater will be run instead"""

status_print(False, "Update failed.")
return


def init(parsed_arguments: ArgNamespace, _: Connection) -> None:
"""initialize the application
- this function should run only once, when the application is first installed"""

status_print(True, "Initialized successfully.")
return


def main(arguments: list[str]) -> None:
Expand Down Expand Up @@ -743,6 +759,8 @@ def main(arguments: list[str]) -> None:
finally:
connection.close() # type: ignore

return


if __name__ == "__main__":
main(argv[1:])
65 changes: 46 additions & 19 deletions npbc_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,17 @@ def get_number_of_each_weekday(month: int, year: int) -> Generator[int, None, No
number_of_weeks = len(main_calendar)

# iterate over each possible weekday
for i in range(len(WEEKDAY_NAMES)):
for weekday_index in range(len(WEEKDAY_NAMES)):

# assume that the weekday occurs once per week in the month
number_of_weekday: int = number_of_weeks

# if the first week doesn't have the weekday, decrement its count
if main_calendar[0][i] == 0:
if main_calendar[0][weekday_index] == 0:
number_of_weekday -= 1

# if the last week doesn't have the weekday, decrement its count
if main_calendar[-1][i] == 0:
if main_calendar[-1][weekday_index] == 0:
number_of_weekday -= 1

yield number_of_weekday
Expand All @@ -101,8 +101,9 @@ def validate_undelivered_string(*strings: str) -> None:
raise npbc_exceptions.InvalidUndeliveredString(f'{string} is not a valid undelivered string.')

# if we get here, all strings passed the regex check
return

def extract_number(string: str, month: int, year: int) -> date | None:
def extract_number(string: str, month: int, year: int) -> date:
"""if the date is simply a number, it's a single day. so we just identify that date"""

day = int(string)
Expand All @@ -111,6 +112,9 @@ def extract_number(string: str, month: int, year: int) -> date | None:
if 0 < day <= monthrange(year, month)[1]:
return date(year, month, day)

# if we reach here, the check failed and it's not a valid date
raise npbc_exceptions.InvalidUndeliveredString(f'{string} is not a valid date for {datetime(year=year, month=month, day=1):%B %Y}.')


def extract_range(string: str, month: int, year: int) -> Generator[date, None, None]:
"""if the date is a range of numbers, it's a range of days. we identify all the dates in that range, bounds inclusive"""
Expand All @@ -122,6 +126,10 @@ def extract_range(string: str, month: int, year: int) -> Generator[date, None, N
for day in range(start, end + 1):
yield date(year, month, day)

else:
# if we reach here, the check failed and the month doesn't have that many days
raise npbc_exceptions.InvalidUndeliveredString(f'{datetime(year=year, month=month, day=1):%B %Y} does not have days between {start} and {end}.')


def extract_weekday(string: str, month: int, year: int) -> Generator[date, None, None]:
"""if the date is the plural of a weekday name, we identify all dates in that month which are the given weekday"""
Expand All @@ -133,7 +141,7 @@ def extract_weekday(string: str, month: int, year: int) -> Generator[date, None,
yield date(year, month, day)


def extract_nth_weekday(string: str, month: int, year: int) -> date | None:
def extract_nth_weekday(string: str, month: int, year: int) -> date:
"""if the date is a number and a weekday name (singular), we identify the date that is the nth occurrence of the given weekday in the month"""

n, weekday_name = npbc_regex.HYPHEN_SPLIT_REGEX.split(string)
Expand All @@ -156,6 +164,8 @@ def extract_nth_weekday(string: str, month: int, year: int) -> date | None:
# return the date that is the nth occurrence of the given weekday in the month
return valid_dates[n - 1]

# if we reach here, the check failed and the weekday does not occur n times in the month
raise npbc_exceptions.InvalidUndeliveredString(f'{datetime(year=year, month=month, day=1):%B %Y} does not have {n} {weekday_name}s.')

def extract_all(month: int, year: int) -> Generator[date, None, None]:
"""if the text is "all", we identify all the dates in the month"""
Expand Down Expand Up @@ -210,17 +220,19 @@ def parse_undelivered_strings(month: int, year: int, *strings: str) -> set[date]

# check for each of the patterns
for string in strings:
try:
dates.update(parse_undelivered_string(month, year, string))

except npbc_exceptions.InvalidUndeliveredString:
print(
f"""Congratulations! You broke the program!
You managed to write a string that the program considers valid, but isn't actually.
Please report it to the developer.
\nThe string you wrote was: {string}
This data has not been counted."""
)
if string:
try:
dates.update(parse_undelivered_string(month, year, string))

except npbc_exceptions.InvalidUndeliveredString as e:
print(
f"""Congratulations! You broke the program!
You managed to write a string that the program considers valid, but isn't actually.
Please report it to the developer.
\nThe string you wrote was: {string}
This data has not been counted.\n
Exact error message: {e}"""
)

return dates

Expand Down Expand Up @@ -262,9 +274,9 @@ def calculate_cost_of_one_paper(
for day in undelivered_dates:
number_of_days_per_weekday_not_received[day.weekday()] += 1

return numpy.sum(
return float(numpy.sum(
delivery_data * cost_data * (number_of_each_weekday - number_of_days_per_weekday_not_received)
)
))


def calculate_cost_of_all_papers(connection: Connection, undelivered_strings: dict[int, list[str]], month: int, year: int) -> tuple[
Expand Down Expand Up @@ -364,6 +376,8 @@ def save_results(
(log_ids[paper_id], day.strftime("%Y-%m-%d"))
)

return


def format_output(connection: Connection, costs: dict[int, float], total: float, month: int, year: int) -> Generator[str, None, None]:
"""format the output of calculating the cost of all papers"""
Expand Down Expand Up @@ -405,6 +419,8 @@ def add_new_paper(connection: Connection, name: str, days_delivered: list[bool],
(paper_id, day_id, delivered, cost)
)

return


def edit_existing_paper(
connection: Connection,
Expand Down Expand Up @@ -446,6 +462,8 @@ def edit_existing_paper(
(delivered, paper_id, day_id)
)

return


def delete_existing_paper(connection: Connection, paper_id: int) -> None:
"""delete an existing paper
Expand All @@ -470,6 +488,8 @@ def delete_existing_paper(connection: Connection, paper_id: int) -> None:
(paper_id,)
)

return


def add_undelivered_string(connection: Connection, month: int, year: int, paper_id: int | None = None, *undelivered_strings: str) -> None:
"""record strings for date(s) paper(s) were not delivered
Expand Down Expand Up @@ -515,6 +535,8 @@ def add_undelivered_string(connection: Connection, month: int, year: int, paper_

connection.executemany("INSERT INTO undelivered_strings (month, year, paper_id, string) VALUES (?, ?, ?, ?);", params)

return


def delete_undelivered_string(
connection: Connection,
Expand Down Expand Up @@ -572,6 +594,8 @@ def delete_undelivered_string(

connection.execute(f"{delete_query} WHERE {conditions};", values)

return


def get_papers(connection: Connection) -> tuple[Papers]:
"""get all papers
Expand Down Expand Up @@ -606,7 +630,7 @@ def get_undelivered_strings(
"""get undelivered strings
- the user may specify as many as they want parameters
- available parameters: string_id, month, year, paper_id, string
- returns a list of tuples containing the following fields:
- returns a tuple of tuples containing the following fields:
string_id, paper_id, year, month, string"""

# initialize parameters for the WHERE clause of the SQL query
Expand Down Expand Up @@ -753,3 +777,6 @@ def validate_month_and_year(month: int | None = None, year: int | None = None) -

if isinstance(year, int) and (year <= 0):
raise npbc_exceptions.InvalidMonthYear("Year must be greater than 0.")

# if we get here, the month and year are valid
return
Loading