Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: thepycoach/automation
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: mvilain/python-automation
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.

Commits on Jun 20, 2022

  1. Copy the full SHA
    97df191 View commit details

Commits on Jun 21, 2022

  1. Copy the full SHA
    f7b7b7d View commit details
  2. add shebang line

    mvilain committed Jun 21, 2022
    Copy the full SHA
    7763be0 View commit details
  3. Copy the full SHA
    ddae986 View commit details
  4. Copy the full SHA
    6aa4743 View commit details

Commits on Jun 23, 2022

  1. add some documentation at the start;

    add generalized variables so it will run w/o mods
    add pretty output
    mvilain committed Jun 23, 2022
    Copy the full SHA
    ae27642 View commit details
  2. add virtualenv directory

    mvilain committed Jun 23, 2022
    Copy the full SHA
    3a16c5d View commit details
  3. Copy the full SHA
    ed3bf1c View commit details
  4. fix formatting per PEP8

    mvilain committed Jun 23, 2022
    Copy the full SHA
    e5c1f96 View commit details

Commits on Jun 26, 2022

  1. added pyinstaller

    mvilain committed Jun 26, 2022
    Copy the full SHA
    dd150ba View commit details
  2. Copy the full SHA
    0bc8663 View commit details
  3. Copy the full SHA
    a1d0161 View commit details
  4. Copy the full SHA
    ad55a3c View commit details

Commits on Jun 27, 2022

  1. Copy the full SHA
    e71ba15 View commit details
  2. Copy the full SHA
    75678ac View commit details
  3. Copy the full SHA
    cd795bc View commit details
  4. Copy the full SHA
    9e8c181 View commit details
  5. Copy the full SHA
    38cd4fb View commit details

Commits on Jun 28, 2022

  1. Copy the full SHA
    5e550c7 View commit details
  2. Copy the full SHA
    1a7e8ca View commit details

Commits on Jun 29, 2022

  1. Copy the full SHA
    b261d53 View commit details
  2. Copy the full SHA
    44b6cc1 View commit details

Commits on Jul 1, 2022

  1. Copy the full SHA
    eeaa9f2 View commit details
  2. Copy the full SHA
    477a77f View commit details
  3. finished project 3

    mvilain committed Jul 1, 2022
    Copy the full SHA
    9f978b9 View commit details

Commits on Jul 8, 2022

  1. Copy the full SHA
    035695d View commit details

Commits on Jul 9, 2022

  1. Copy the full SHA
    a449bb7 View commit details
  2. Copy the full SHA
    a33521b View commit details
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
*.tar.gz
*.swp
*.log
*.textlog
*.retry
*.sublime-*
.vscode
.idea/
*.mp4
**/venv/*
**/virtualenv/*
.hypothesis
.ipynb_checkpoints
**/build/*
**/dist/*
*.spec

# python cache files
__pycache__/
.pytest_cache

# Crash log files
crash.log
.vault_pass.txt
.DS_Store
.htaccess
htpasswd
account.json
1 change: 1 addition & 0 deletions 1.Table Extraction/Extract Tables from PDFs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#! /usr/bin/env python3
import camelot

tables = camelot.read_pdf('foo.pdf', pages='1', flavor='lattice')
25 changes: 18 additions & 7 deletions 2.Automate The News/1.news-extract-data.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
#!/usr/bin/which python3
# 1.news-extract-data.py
# extract information from a site using Xpath's chrome webdriver
# this will open a browser session on the URL and allow extraction

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import pandas as pd
import os
import sys
PROG = os.path.basename(sys.argv[0])
PATH = os.path.expanduser('~/Downloads/chromedriver') # introduce path here
OUT = 'headline.csv'
WEB = 'https://www.thesun.co.uk/sport/football/'

web = 'https://www.thesun.co.uk/sport/football/'
path = '/Users/frankandrade/Downloads/chromedriver' # introduce path here

print('{}--> {}'.format(PROG, WEB), end='', flush=True)
# Creating the driver
driver_service = Service(executable_path=path)
driver_service = Service(executable_path=PATH)
driver = webdriver.Chrome(service=driver_service)
driver.get(web)
driver.get(WEB)

# Finding Elements
containers = driver.find_elements(by='xpath', value='//div[@class="teaser__copy-container"]')
@@ -23,10 +32,12 @@
titles.append(title)
subtitles.append(subtitle)
links.append(link)
print(".", end='', flush=True)

# Exporting data to a CSV file
my_dict = {'title': titles, 'subtitle': subtitles, 'link': links}
df_headlines = pd.DataFrame(my_dict)
df_headlines.to_csv('headline.csv')

df_headlines.to_csv(OUT)
print("{} {} entries".format(OUT, len(titles)))
driver.quit()
exit(0)
24 changes: 19 additions & 5 deletions 2.Automate The News/2.news-headless.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
#!/usr/bin/which python3
# 2.news-headless.py
# extract information from a site using Xpath's chrome webdriver
# this will NOT open a browser but extract the pages from the URL anyway

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import pandas as pd
import os
import sys

web = 'https://www.thesun.co.uk/sport/football/'
path = '/Users/frankandrade/Downloads/chromedriver' # introduce path here
WEB = 'https://www.thesun.co.uk/sport/football/'
PROG = os.path.basename(sys.argv[0])
DRIVER_PATH = os.path.expanduser('~/Downloads/chromedriver') # introduce path here
OUT_FILE = 'headline.csv'

print('{}--> driver.get('.format(PROG), end='', flush=True)
# add headless mode
options = Options()
options.headless = True
driver_service = Service(executable_path=path)
driver_service = Service(executable_path=DRIVER_PATH)
driver = webdriver.Chrome(service=driver_service, options=options)
driver.get(web)
driver.get(WEB)

print('{} )'.format(WEB), end='', flush=True)
containers = driver.find_elements(by='xpath', value='//div[@class="teaser__copy-container"]')

titles = []
@@ -25,9 +36,12 @@
titles.append(title)
subtitles.append(subtitle)
links.append(link)
print(".", end='', flush=True)

my_dict = {'title': titles, 'subtitle': subtitles, 'link': links}
df_headlines = pd.DataFrame(my_dict)
df_headlines.to_csv('headline-headless.csv')
df_headlines.to_csv(OUT_FILE)
print(" {} [{} entries]".format(OUT_FILE, len(titles)))

driver.quit()
exit(0)
38 changes: 25 additions & 13 deletions 2.Automate The News/3.news-automation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#!/usr/bin/which python3
# 3.news-automation.py
# extract information from a site using Xpath's chrome webdriver
# this will NOT open a browser but extract the pages from the URL anyway
# name output file with date, so it can be run via cron regularly

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
@@ -6,41 +12,47 @@
import os
import sys

# Preparing script before we convert it to executable
application_path = os.path.dirname(sys.executable)

# get date in format MMDDYYYY
now = datetime.now()
month_day_year = now.strftime("%m%d%Y")
WEB = 'https://www.thesun.co.uk/sport/football/'
PROG = os.path.basename(sys.argv[0])
DRIVER_PATH = os.path.expanduser('~/Downloads/chromedriver')

web = 'https://www.thesun.co.uk/sport/football/'
path = '/Users/frankandrade/Downloads/chromedriver' # introduce path here
OUT_FILE = 'football_headlines_{}.csv'.format(datetime.now().strftime("%Y%m%d"))
"""
# os.path.join ensures name is not OS dependent (e.g. directory seperator MacOS="/", Win="\")
# but I don't want the file stored where python is stored so commented out
"""
# OUT_FILE = os.path.join(os.path.dirname(sys.executable), OUT_FILE)

print('{}--> driver.web('.format(PROG), end='', flush=True)
# Headless mode
options = Options()
options.headless = True
driver_service = Service(executable_path=path)
driver_service = Service(executable_path=DRIVER_PATH)
driver = webdriver.Chrome(service=driver_service, options=options)
driver.get(web)
driver.get(WEB)

print('{}) '.format(WEB), end='', flush=True)
containers = driver.find_elements(by='xpath', value='//div[@class="teaser__copy-container"]')

titles = []
subtitles = []
links = []
link_count = 0
for container in containers:
title = container.find_element(by='xpath', value='./a/h2').text
subtitle = container.find_element(by='xpath', value='./a/p').text
link = container.find_element(by='xpath', value='./a').get_attribute('href')
titles.append(title)
subtitles.append(subtitle)
links.append(link)
link_count += 1
if (link_count % 5) == 0: # print "+" when count mod 5 = 0
print(".", end='', flush=True)

# Exporting data to the same folder where the executable will be located
my_dict = {'title': titles, 'subtitle': subtitles, 'link': links}
df_headlines = pd.DataFrame(my_dict)
file_name = f'football_headlines_{month_day_year}.csv'
final_path = os.path.join(application_path, file_name)
df_headlines.to_csv(final_path)
df_headlines.to_csv(OUT_FILE)
print(" {} [{} entries]".format(OUT_FILE, len(titles)))

driver.quit()
Binary file added 2.Automate The News/3.news-automation.warning.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 20 additions & 4 deletions 3.Excel Report/1.make-pivot-table.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import pandas as pd
#!/usr/bin/which python3
# 1.make-pivot-table.py
# read the supermarket sales spreadsheet
# select 3 columns by Gender, Product, Total Sales
# create a pivot table using Gender for y-axis showing Product and Total Sales
# output to excel

import pandas as pd
import os
import sys
PROG = os.path.basename(sys.argv[0])
IN_FILE = os.path.expanduser('supermarket_sales.xlsx')
OUT_FILE = os.path.expanduser('pivot_table.xlsx')
# Read Excel File
df = pd.read_excel('supermarket_sales.xlsx')
df = pd.read_excel(IN_FILE)
print('{}--> {}'.format(PROG, IN_FILE)) # , end='', flush=True)

# Select columns: 'Gender', 'Product line', 'Total'
df = df[['Gender', 'Product line', 'Total']]
print('selecting columns')
print(df)

# Make pivot table
pivot_table = df.pivot_table(index='Gender', columns='Product line',
values='Total', aggfunc='sum').round(0)

# Export pivot table to Excel file
pivot_table.to_excel('pivot_table.xlsx', 'Report', startrow=4)
# Export table to Excel file w/ sheet='Report' starting at row 4
pivot_table.to_excel(OUT_FILE, 'Report', startrow=4)
print(pivot_table)
print(' -->{}'.format(OUT_FILE))
56 changes: 48 additions & 8 deletions 3.Excel Report/2.add-charts.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,80 @@
#!/usr/bin/which python3
# 2.add-charts.py
# loads the Excel pivot_table.xlsx 'Report' workbook created in 1.make-pivot-table.py
# select the 'Report' sheet to generate the graph
# select 3 columns by Gender, Product, Total Sales
# create a pivot table using Gender for y-axis showing Product and Total Sales
# output to excel

from openpyxl import load_workbook
from openpyxl.chart import BarChart, Reference
import os
import sys
PROG = os.path.basename(sys.argv[0])
IN_FILE = os.path.expanduser('pivot_table.xlsx')
OUT_FILE = os.path.expanduser('barchart.xlsx')

# Read workbook and select sheet
wb = load_workbook('pivot_table.xlsx')
sheet = wb['Report']
if not os.path.exists(IN_FILE):
print("{} -- file '{}' not found".format(PROG, IN_FILE))
exit(1)

# if the pivot_table.xlsx file has been modified by IntelliJ's ExcelReader,
# this will throw a 'KeyError' exception, so trap it and handle that
# NOTE: opening the file with Apple's Numbers or Excel does not cause this error
try:
wb = load_workbook(IN_FILE)
except KeyError:
print("{} -- error opening '{}'... regenerate the file".format(PROG, IN_FILE))
exit(1)

try:
sheet = wb['Report'] # not defined if Sheet not found...throws KeyError
print("{}--> '{}({})'".format(PROG, IN_FILE, sheet), end='', flush=True)
except KeyError:
print("{} -- error opening '{}' -- workbook or sheet not found".format(PROG, IN_FILE))
exit(1)

# Active rows and columns
min_column = wb.active.min_column
max_column = wb.active.max_column
min_row = wb.active.min_row
max_row = wb.active.max_row
# print(' [active cells=({},{}):({},{})] '.format(min_row,min_column,max_row,max_column))

# Instantiate a barchart
barchart = BarChart()

# Locate data and categories
# data reference omits the column with the category headers
data = Reference(sheet,
min_col=min_column+1,
max_col=max_column,
min_row=min_row,
max_row=max_row) # including headers

max_row=max_row
)
# categories being displayed are in Column A (e.g.1)
# don't include the headers in min_row
categories = Reference(sheet,
min_col=min_column,
max_col=min_column,
min_row=min_row+1,
max_row=max_row) # not including headers
max_row=max_row
)

# Make chart
# Adding data and categories
barchart.add_data(data, titles_from_data=True)
barchart.set_categories(categories)

# Make chart
sheet.add_chart(barchart, "B12")
barchart.title = 'Sales by Product line'
# chart style for Excel 16.6 on MacOS
# plain 1=BW 2=multi-color 3=blue 4=red 5=green 6=purple 7=cyan 8=orange
# outline 9=BW 10=multi-color 11=blue 12=red 13=green 14=purple 15=cyan 16=orange
# no error occurs of style > 16
barchart.style = 5 # choose the chart style
sheet.add_chart(barchart, "B12")

# Save workbook
wb.save('barchart.xlsx')
wb.save(OUT_FILE)
print(" --> '{}'".format(OUT_FILE))
38 changes: 33 additions & 5 deletions 3.Excel Report/3.apply-formulas.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
#!/usr/bin/which python3
# 3.apply-formulas.py
# loads the Excel pivot_table.xlsx 'Report' workbook created in 1.make-pivot-table.py
# select the 'Report' sheet to generate the graph
# select 3 columns by Gender, Product, Total Sales
# create a pivot table using Gender for y-axis showing Product and Total Sales
# output to excel
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
import os
import sys
PROG = os.path.basename(sys.argv[0])
IN_FILE = os.path.expanduser('barchart.xlsx')
OUT_FILE = os.path.expanduser('report.xlsx')

if not os.path.exists(IN_FILE):
print("{} -- file '{}' not found".format(PROG, IN_FILE))
exit(1)

# if the barchart.xlsx file has been modified by IntelliJ's ExcelReader,
# this will throw a 'KeyError' exception
try:
wb = load_workbook(IN_FILE)
except KeyError:
print("{} -- error opening '{}'... regenerate the file".format(PROG, IN_FILE))
exit(1)

wb = load_workbook('barchart.xlsx')
sheet = wb['Report']
print("{}--> '{}({})'".format(PROG, IN_FILE, sheet), end='', flush=True)

min_column = wb.active.min_column
max_column = wb.active.max_column
min_row = wb.active.min_row
max_row = wb.active.max_row

# this is what this code does if you did it manually
# Write an Excel formula with Python
# sheet['B8'] = '=SUM(B6:B7)'
# sheet['B8'].style = 'Currency'

# Write multiple formulas with a for loop
for i in range(min_column+1, max_column+1): # (B, G+1)
letter = get_column_letter(i)
# the range method produces an iterable from start to stop-1,
# so to ensure covering the last column, specify max_column+1
for i in range(min_column+1, max_column+1): # (B, G+1) skipping category
letter = get_column_letter(i) # conv column # to letter
sheet[f'{letter}{max_row + 1}'] = f'=SUM({letter}{min_row + 1}:{letter}{max_row})'
sheet[f'{letter}{max_row + 1}'].style = 'Currency'
sheet[f'{letter}{max_row + 1}'].style = 'Currency' # formats as "####.## $"

wb.save('report.xlsx')
wb.save(OUT_FILE)
print(" --> '{}'".format(OUT_FILE))
23 changes: 21 additions & 2 deletions 3.Excel Report/4.format-cells.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
#!/usr/bin/which python3
# 4.format-cells.py
# loads the Excel report.xlsx 'Report' workbook created in 2.add-charts.py
# select the 'Report' sheet
# annotate cells A1 and A2 with titles
# output to excel
from openpyxl import load_workbook
from openpyxl.styles import Font
import os
import sys
PROG = os.path.basename(sys.argv[0])
IN_FILE = os.path.expanduser('report.xlsx')
OUT_FILE = os.path.expanduser('report_january.xlsx')

wb = load_workbook('report.xlsx')
if not os.path.exists(IN_FILE):
print("{} -- file '{}' not found".format(PROG, IN_FILE))
exit(1)

# if the barchart.xlsx file has been modified by IntelliJ's ExcelReader,
# this will throw a 'KeyError' exception
wb = load_workbook(IN_FILE)
sheet = wb['Report']
print("{}--> '{}({})'".format(PROG, IN_FILE, sheet), end='', flush=True)

# Add format
sheet['A1'] = 'Sales Report'
sheet['A2'] = 'January'
sheet['A1'].font = Font('Arial', bold=True, size=20)
sheet['A2'].font = Font('Arial', bold=True, size=10)

wb.save('report_january.xlsx')
wb.save(OUT_FILE)
print(" --> '{}'".format(OUT_FILE))
63 changes: 55 additions & 8 deletions 3.Excel Report/5.pivot-to-report.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
#!/usr/bin/which python3
# 5.pivot-to-report.py
# loads the Excel pivot_table.xlsx 'Report' workbook created in 1.make-pivot-table.py
# select the 'Report' sheet
# create bar chart
# create total summary of categories
# annotate cells A1 and A2 with titles
# output to excel
from openpyxl import load_workbook
from openpyxl.chart import BarChart, Reference
from openpyxl.utils import get_column_letter
from openpyxl.styles import Font
import os
import sys
PROG = os.path.basename(sys.argv[0])
MONTH = 'february'
IN_FILE = os.path.expanduser('pivot_table.xlsx')
OUT_FILE = os.path.expanduser(f'report_{MONTH}.xlsx')

# Read workbook and select sheet
if not os.path.exists(IN_FILE):
print("{} -- file '{}' not found".format(PROG, IN_FILE))
exit(1)

# Putting together #2, #3, and #4 (input: pivot_table.xlsx + month , output: Report with barchart, formulas and format)
month = 'february'

# Read workbook and select sheet
wb = load_workbook('pivot_table.xlsx')
sheet = wb['Report']
# if the pivot_table.xlsx file has been modified by IntelliJ's ExcelReader,
# this will throw a 'KeyError' exception, so trap it and handle that
# NOTE: opening the file with Apple's Numbers or Excel does not cause this error
try:
wb = load_workbook(IN_FILE)
except KeyError:
print("{} -- error opening '{}'... regenerate the file".format(PROG, IN_FILE))
exit(1)

try:
sheet = wb['Report'] # not defined if Sheet not found...throws KeyError
print("{}--> '{}({})'".format(PROG, IN_FILE, sheet), end='', flush=True)
except KeyError:
print("{} -- error opening '{}' -- workbook or sheet not found".format(PROG, IN_FILE))
exit(1)

# Active rows and columns
min_column = wb.active.min_column
@@ -20,8 +50,18 @@
barchart = BarChart()

# Locate data and categories
data = Reference(sheet, min_col=min_column+1, max_col=max_column, min_row=min_row, max_row=max_row) # including headers
categories = Reference(sheet, min_col=min_column, max_col=min_column, min_row=min_row+1, max_row=max_row) # not including headers
data = Reference(sheet,
min_col=min_column+1,
max_col=max_column,
min_row=min_row,
max_row=max_row
) # including headers
categories = Reference(sheet,
min_col=min_column,
max_col=min_column,
min_row=min_row+1,
max_row=max_row
) # not including headers

# Adding data and categories
barchart.add_data(data, titles_from_data=True)
@@ -30,18 +70,25 @@
# Make chart
sheet.add_chart(barchart, "B12")
barchart.title = 'Sales by Product line'
# chart style for Excel 16.6 on MacOS
# plain 1=BW 2=multi-color 3=blue 4=red 5=green 6=purple 7=cyan 8=orange
# outline 9=BW 10=multi-color 11=blue 12=red 13=green 14=purple 15=cyan 16=orange
# no error occurs of style > 16
barchart.style = 5 # choose the chart style

# Write multiple formulas with a for loop
for i in range(min_column+1, max_column+1): # (B, G+1)
letter = get_column_letter(i)
sheet[f'{letter}{max_row + 1}'] = f'=SUM({letter}{min_row + 1}:{letter}{max_row})'
# Python's 'Currency' style appears as "##### $" which shows as Custom in Excel
# this is a bug in the openpyxl library
sheet[f'{letter}{max_row + 1}'].style = 'Currency'

# Add format
sheet['A1'] = 'Sales Report'
sheet['A2'] = month
sheet['A2'] = MONTH
sheet['A1'].font = Font('Arial', bold=True, size=20)
sheet['A2'].font = Font('Arial', bold=True, size=10)

wb.save(f'report_{month}.xlsx')
wb.save(OUT_FILE)
print(" --> '{}'".format(OUT_FILE))
272 changes: 215 additions & 57 deletions 3.Excel Report/6.py-to-exe.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,220 @@
#!/usr/bin/which python3
"""
6.py-to-exe.py
loads the Excel the sales marketing data and creates a pivot_table.xlsx 'Report' workbook
select the 'Report' sheet
create bar chart
create total summary of categories
annotate cells A1 and A2 with titles
output to excel from openpyxl import load_workbook
"""

import argparse
import os
import sys
from datetime import date

import pandas as pd
from openpyxl import load_workbook
from openpyxl.chart import BarChart, Reference
from openpyxl.utils import get_column_letter
from openpyxl.styles import Font
import os
import sys

# Preparing script before we convert it to executable
application_path = os.path.dirname(sys.executable)

# Putting together #2, #3, and #4 (input: pivot_table.xlsx + month , output: Report with barchart, formulas and format)
month = input('Introduce month: ')

# Read workbook and select sheet
input_path = os.path.join(application_path, 'pivot_table.xlsx')
wb = load_workbook(input_path)
sheet = wb['Report']

# Active rows and columns
min_column = wb.active.min_column
max_column = wb.active.max_column
min_row = wb.active.min_row
max_row = wb.active.max_row

# Instantiate a barchart
barchart = BarChart()

# Locate data and categories
data = Reference(sheet,
min_col=min_column+1,
max_col=max_column,
min_row=min_row,
max_row=max_row) # including headers
categories = Reference(sheet,
min_col=min_column,
max_col=min_column,
min_row=min_row+1,
max_row=max_row) # not including headers

# Adding data and categories
barchart.add_data(data, titles_from_data=True)
barchart.set_categories(categories)

# Make chart
sheet.add_chart(barchart, "B12")
barchart.title = 'Sales by Product line'
barchart.style = 5 # choose the chart style

# Write multiple formulas with a for loop
for i in range(min_column+1, max_column+1): # (B, G+1)
letter = get_column_letter(i)
sheet[f'{letter}{max_row + 1}'] = f'=SUM({letter}{min_row + 1}:{letter}{max_row})'
sheet[f'{letter}{max_row + 1}'].style = 'Currency'

# Add format
sheet['A1'] = 'Sales Report'
sheet['A2'] = month
sheet['A1'].font = Font('Arial', bold=True, size=20)
sheet['A2'].font = Font('Arial', bold=True, size=10)

output_path = os.path.join(application_path, f'report_{month}.xlsx')
wb.save(output_path)

def parse_arguments(default_input_file='supermarket_sales.xlsx',
default_output_file='report.xlsx',
default_month='MONTHLY'
):
"""parse the argument list, build help and usage messages
Args:
default_input_file (str): filename containing a default input Excel spreadsheet
default_output_file (str): filename containing a default output Excel spreadsheet for
the consolidation report
default_month (str): containing the current month as a string (not validated)
Returns:
namespace (ns): namespace with the arguments passed and their values
"""
parser = argparse.ArgumentParser(
description='generate Consolidation report from Excel spreadsheet of marketing data')
# if omitted, the default value is returned, so arg.input is always defined
parser.add_argument('-i', '--input',
action='store',
default=default_input_file,
help=f'Input file of Excel Marketing data [default: {default_input_file}]',
required=False,
# nargs="?", # command line arg w/o flag
)
# if omitted, the default value is returned, so arg.output is always defined
parser.add_argument('-o', '--output',
action="store",
default=default_output_file,
help=f'Consolidation Report output file [default: {default_output_file}]',
required=False
)
# if omitted, the current month is used, so arg.month will always be defined
parser.add_argument('-m', '--month',
action="store",
default=default_month,
help=f'Month to tag in output report [default: {default_month}]',
required=False
)
parser.add_argument('-v', '--verbose',
action="store_true",
help='show progress as report is being built',
required=False
)
args = parser.parse_args()

# args.input = os.path.expanduser(args.input)
# args.output = os.path.expanduser(args.output)

return args # namespace containing the argument passed on the command line


def main():
"""main program to input marketing data and output a consolidation report as Excel spreadsheet
Args:
inputs Excel spreadsheet (must be xlsx file)
assumes Excel table format with 1st line containing
- Invoice
- Branch
- City
- Customer Type
- Gender
- Product Line
- Unit Price
- Quantity
- Tax 5%
- Total
- Date (mm/dd/yy)
- Time (HH:MM)
- Payment
- cogs
- gross Margin
- gross Income
- Rating
outputs Excel spreadsheet consolidation report
Returns: 0
"""
prog = os.path.basename(sys.argv[0])
this_month = date.today().strftime('%B') # full month
args = parse_arguments(
default_input_file='supermarket_sales.xlsx',
default_output_file=f'report_{this_month}.xlsx',
default_month=this_month
)

input_expanded = os.path.expanduser(args.input)
# output_file = os.path.expanduser(os.path.exists(args.output))
if not os.path.exists(input_expanded):
print(f'{prog} -- file {input_expanded} not found')
sys.exit(1)

df = pd.read_excel(input_expanded) # open excel file as panda dataframe

if args.verbose:
print(f'{prog}--> read_excel {input_expanded}...', end='', flush=True)

# Select columns: 'Gender', 'Product line', 'Total'
df = df[['Gender', 'Product line', 'Total']]
if args.verbose:
# print(df)
print('selecting columns...', end='', flush=True)

pivot_table = df.pivot_table(index='Gender', columns='Product line',
values='Total', aggfunc='sum').round(2)

# Export pivot table to Excel file w/ sheet starting at row 4
# Preparing script before we convert it to executable
# application_path = os.path.dirname(sys.executable)
# output_file = os.path.join(os.path.dirname(sys.executable), args.output)
pivot_table.to_excel(args.output, 'Report', startrow=4)
if args.verbose:
# print(pivot_table)
print(f'excel.pivot{args.output}...')

# if the pivot_table.xlsx file has been modified by IntelliJ's ExcelReader,
# this will throw a 'KeyError' exception, so trap it and handle that
# NOTE: opening the file with Apple's Numbers or Excel does not cause this error
# since this is an intermediate step and can't be read, remove the exception trapping
# try:
wb = load_workbook(args.output)
# except KeyError:
# print(f"{PROG} -- error opening '{output_file}'... regenerate the file")
# sys.exit(1)
#
# TODO: how to reference the sheet from report_sheet as a dict w/o hard coding reference?
try:
sheet = wb['Report'] # not defined if Sheet not found...throws KeyError
if args.verbose:
print(f"{prog}--> '{args.output}(Report)'...", end='', flush=True)
except KeyError:
print(f"{prog} -- error opening '{args.output}' -- workbook or sheet not found")
sys.exit(1)

if args.verbose:
print('...loaded...', end='', flush=True)

# Active rows and columns
min_column = wb.active.min_column
max_column = wb.active.max_column
min_row = wb.active.min_row
max_row = wb.active.max_row

# Instantiate a barchart
barchart = BarChart()

# Locate data and categories
data = Reference(sheet,
min_col=min_column+1, max_col=max_column, # data starts after header column
min_row=min_row, max_row=max_row)
categories = Reference(sheet,
min_col=min_column, max_col=min_column, # omit header column
min_row=min_row+1, max_row=max_row)

# Adding data and categories
barchart.add_data(data, titles_from_data=True)
barchart.set_categories(categories)

# Make chart in cell B12
sheet.add_chart(barchart, "B12")
barchart.title = 'Sales by Product line'
# chart style for Excel 16.6 on MacOS 12.4
# plain 1=BW 2=multi-color 3=blue 4=red 5=green 6=purple 7=cyan 8=orange
# outlined 9=BW 10=multi-color 11=blue 12=red 13=green 14=purple 15=cyan 16=orange
# no error occurs of style > 16
barchart.style = 5 # choose the chart style (plain green)
if args.verbose:
print('barchart...', end='', flush=True)
# Write multiple formulas with a for loop
# note that openpyxl has a bug incorrectly setting style='Currency' (##### $ instead of $#####)
for i in range(min_column+1, max_column+1): # (B, G+1)
letter = get_column_letter(i)
sheet[f'{letter}{max_row + 1}'] = f'=SUM({letter}{min_row + 1}:{letter}{max_row})'
sheet[f'{letter}{max_row + 1}'].style = 'Currency'
if args.verbose:
print('summary...', end='', flush=True)

# Add Title and format
sheet['A1'] = 'Sales Report'
sheet['A2'] = args.month
sheet['A1'].font = Font('Arial', bold=True, size=20)
sheet['A2'].font = Font('Arial', bold=True, size=10)
if args.verbose:
print('titles...', end='', flush=True)

wb.save(args.output)
if args.verbose:
print(f" '{args.output}'")

return 0


if __name__ == '__main__':
sys.exit(main())
Binary file removed 3.Excel Report/barchart.xlsx
Binary file not shown.
Binary file removed 3.Excel Report/pivot_table.xlsx
Binary file not shown.
Binary file removed 3.Excel Report/report.xlsx
Binary file not shown.
Binary file modified 3.Excel Report/supermarket_sales.xlsx
Binary file not shown.
100 changes: 93 additions & 7 deletions 4.WhatsApp/0.whatsapp-pywhatkit.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,101 @@
#!/usr/bin/which python3
"""
0.whatsapp-pywhatkit.py
send message to single WhatsApp user or a GroupID
"""
import sys
from datetime import datetime,timedelta
import argparse
import pywhatkit

# Send message to a contact
phone_number = input("Enter phone number: ")

pywhatkit.sendwhatmsg(phone_number, "Test", 7, 21)
pywhatkit.sendwhatmsg(phone_number, "Test", 7, 25, 15, True, 2)
def parse_arguments():
"""parse the argument list, build help and usage messages
parses command line for
-p --phone (str): WhatsApp phone number to call TODO: validate as phone #
-g --group (str): WhatsApp Group ID to call
Returns:
namespace (ns): namespace with the arguments passed and their values
"""
parser = argparse.ArgumentParser(
description='contact a WhatsApp user via their phone number or Group ID')
# if omitted, the script will prompt for the phone or group id
parser.add_argument('-p', '--phone',
action='store',
help='WhatsApp phone number',
required=False,
# nargs="?", # command line arg w/o flag
)
# if neither is passed, the phone is prompted
parser.add_argument('-g', '--group',
action="store",
help='WhatsApp Group ID',
required=False
)
args = parser.parse_args()
return args # namespace containing the argument passed on the command line


def main():
"""parse WhatsApp phone number or group ID and contact that number
"""
args = parse_arguments()
if args.phone:
phone_number = args.phone
else:
phone_number = input("Enter phone number: ")

# this could just as easily been now + timedelta(seconds=10)
# but we need to pass the hours minutes and seconds
now = datetime.now()
call_hh = now.hour
call_mm = now.minute

# Send message to a group
group_id = input("Enter group id: ")
# Send message to a contact 10 from when program was run
# sendwhatmsg_instantly(phone_no: str,
# message: str,
# wait_time: int = 15,
# tab_close: bool = False,
# close_time: int = 3) -> None
# sendwhatmsg( phone_no: str,
# message: str,
# time_hour: int,
# time_min: int,
# wait_time: int = 15,
# tab_close: bool = False,
# close_time: int = 3)
pywhatkit.sendwhatmsg_instantly('+1' + phone_number, "Instant Test")
# pywhatkit.sendwhatmsg('+1' + phone_number, "Test", call_hh, call_mm+2)
pywhatkit.sendwhatmsg('+1' + phone_number,
"Test...wait 7...close....close=2",
call_hh, call_mm+1,
7, True, 2
)

pywhatkit.sendwhatmsg_to_group(group_id, "Test Group", 7, 31)
if args.group:
group_id = args.group
else:
group_id = input("Enter group id: ")

# Send message to a group
# sendwhatmsg_to_group( group_id: str,
# message: str,
# time_hour: int,
# time_min: int,
# wait_time: int = 15,
# tab_close: bool = False,
# close_time: int = 3) -> None
pywhatkit.sendwhatmsg_to_group(group_id,
"Test Group",
call_hh, call_mm+3
)
return 0


if __name__ == '__main__':
sys.exit(main())
Binary file added Automation Cheat Sheet.pdf
Binary file not shown.
379 changes: 379 additions & 0 deletions Readme.md

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## requirements.txt
## used by https://github.com/ifrankandrade/automation
## and fork https://github.com/mvilain/python-automation
## use this with a python virtual environment
## (e.g. python3 -m virtualenvironment virtualenv; source virtualenv/bin/activate; python3 -m pip install -r requirements)
##### DO NOT ALLOW PYCHARM or INTELLIJ to install these modules
##### it will install them into the system directory...use the commands above
pathlib

## https://camelot-py.readthedocs.io/en/master/user/install-deps.html
ghostscript
tk
## https://stackoverflow.com/questions/60666006/python-camelot-module-does-not-work-offline
opencv-python
camelot-py

jupyterlab
pandas
openpyxl

selenium
pyinstaller

pywhatkit
## the following are part of the standard Library install
## they don't need to be listed here but are used by this repo
#email
#ssl
#time
#wheel