For those who like to tinker with these types of things, I hope you find this fun. Feel free to pinch anything you see below!

Python Stuff

To the (Marine) Rescue

A mate of mine who is involved with Marine Rescue asked if I might be able to cook up a little something to help automate the gathering and distribution of certain weather forecast data that the marine rescue crew use on a daily basis to navigate the waters (literally). Naturally I obliged and can only assume that this piece of code is saving lives on a daily basis, or something equally amazing.

This project involved wrestling with the BOM’s (I refuse to call it The Bureau) antiquated systems and pulling together various disparate pieces of data in order to automate the process of producing a word and pdf documents in line with a predetermined style. Gotta give the water hero’s their data how they like it!

This is now sitting on a server and automatically firing and sending emails at predetermined intervals. Nice.

All potentially sensitive info below has been replaced by highly imaginative placeholder text.

“Hey Dan, can you build me a thing?”

Is there something in your professional or personal life that you would like to supercharge with a cheeky little behind the scenes script, desktop app, or even website? If so, I’d be very keen to work with you!

Just click here and you’ll be whisked away to the right place to start the conversation.

import os
from ftplib import FTP
from bs4 import BeautifulSoup
from datetime import datetime
import docx
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

ftp_server = FTP('ftp2.bom.gov.au')
ftp_server.login(user='legit_user', passwd='legit_password')

ftp_server.cwd('path_to_folder')

#Broken Bay to Port Hacking
coastal_forecast = 'file_name.xml'

localfile = open(coastal_forecast, 'wb')

ftp_server.retrbinary('RETR ' + coastal_forecast, localfile.write, 1024)

ftp_server.quit()
localfile.close()

with open(coastal_forecast) as fp:
    soup = BeautifulSoup(fp, 'html.parser')

issue_time = soup.find('issue-time-local').get_text().strip()
issue_time_dt = datetime.strptime(issue_time, '%Y-%m-%dT%H:%M:%S%z')
issue_time_words = issue_time_dt.strftime('%H:%M%p %A %B %d')
forecast_issued_message = f'Forecast issued at {issue_time_words}'

#date to use with doc title
title_date = issue_time_words = issue_time_dt.strftime('%A %B %d %Y')

synoptic = soup.find('text', {'type': 'synoptic_situation'}).get_text().strip()

winds = soup.find_all('text', {'type': 'forecast_winds'})
winds1 = winds[0].get_text().strip()
winds2 = winds[1].get_text().strip()

seas = soup.find_all('text', {'type': 'forecast_seas'})
seas1 = seas[0].get_text().strip()
seas2 = seas[1].get_text().strip()

swell = soup.find_all('text', {'type': 'forecast_swell1'})
swell1 = swell[0].get_text().strip()
swell2 = swell[1].get_text().strip()

weather = soup.find_all('text', {'type': 'forecast_weather'})
weather1 = weather[0].get_text().strip()
weather2 = weather[1].get_text().strip()

#Get first forecast period for coastal

forecast_period_tag = soup.find('forecast-period', {'index': '0'})

start_time_local = forecast_period_tag.get('start-time-local')

end_time_local = forecast_period_tag.get('end-time-local')

start_time_local_dt = datetime.strptime(start_time_local, '%Y-%m-%dT%H:%M:%S%z')
start_time_local_words = start_time_local_dt.strftime('%A %B %d')

end_time_local_dt = datetime.strptime(end_time_local, '%Y-%m-%dT%H:%M:%S%z')
end_time_local_words = end_time_local_dt.strftime('%A %B %d, %I:%M %p')

coastal_forecast_heading = f'Forecast for {start_time_local_words} until {end_time_local_words}'

#Get second forecast period for coastal

forecast_period_tag2 = soup.find('forecast-period', {'index': '1'})

start_time_local2 = forecast_period_tag2.get('start-time-local')

end_time_local2 = forecast_period_tag2.get('end-time-local')

start_time_local_dt2 = datetime.strptime(start_time_local2, '%Y-%m-%dT%H:%M:%S%z')
start_time_local_words2 = start_time_local_dt2.strftime('%A %B %d')

end_time_local_dt2 = datetime.strptime(end_time_local2, '%Y-%m-%dT%H:%M:%S%z')
end_time_local_words2 = end_time_local_dt2.strftime('%A %B %d, %I:%M %p')

coastal_forecast_heading2 = f'Forecast for {start_time_local_words2}'
coastal_title = "Sydney Coastal Waters Forecast: Broken Bay to Port Hacking"

#-----------------------------

ftp_server = FTP('ftp2.bom.gov.au')
ftp_server.login(user='legit_user', passwd='legit_password')

ftp_server.cwd('path_to_folder')

enclosed_forecast = 'file_name2.xml'

localfile2 = open(enclosed_forecast, 'wb')
ftp_server.retrbinary('RETR ' + enclosed_forecast, localfile2.write, 1024)

ftp_server.quit()
localfile2.close()

with open(enclosed_forecast) as fp:
    soup2 = BeautifulSoup(fp, 'html.parser')

enclosed_issue_time = soup2.find('issue-time-local').get_text().strip()
enclosed_issue_time_dt = datetime.strptime(enclosed_issue_time, '%Y-%m-%dT%H:%M:%S%z')
enclosed_issue_time_words = enclosed_issue_time_dt.strftime('%H:%M%p %A %B %d')
enclosed_forecast_issued_message = f'Forecast issued at {enclosed_issue_time_words}'

enclosed_winds = soup2.find_all('text', {'type': 'forecast_winds'})
enclosed_winds1 = enclosed_winds[0].get_text().strip()
enclosed_winds2 = enclosed_winds[1].get_text().strip()

enclosed_seas = soup2.find_all('text', {'type': 'forecast_seas'})
enclosed_seas1 = enclosed_seas[0].get_text().strip()
enclosed_seas2 = enclosed_seas[1].get_text().strip()

enclosed_weather = soup2.find_all('text', {'type': 'forecast_weather'})
enclosed_weather1 = enclosed_weather[0].get_text().strip()
enclosed_weather2 = enclosed_weather[1].get_text().strip()

#Get first forecast period for coastal

enclosed_forecast_period_tag = soup2.find('forecast-period', {'index': '0'})

enclosed_start_time_local = enclosed_forecast_period_tag.get('start-time-local')

enclosed_end_time_local = enclosed_forecast_period_tag.get('end-time-local')

enclosed_start_time_local_dt = datetime.strptime(enclosed_start_time_local, '%Y-%m-%dT%H:%M:%S%z')
enclosed_start_time_local_words = enclosed_start_time_local_dt.strftime('%A %B %d')

enclosed_end_time_local_dt = datetime.strptime(enclosed_end_time_local, '%Y-%m-%dT%H:%M:%S%z')
enclosed_end_time_local_words = enclosed_end_time_local_dt.strftime('%A %B %d, %I:%M %p')

enclosed_coastal_forecast_heading = f'Forecast for {enclosed_start_time_local_words} until {enclosed_end_time_local_words}'

#Get second forecast period for coastal

enclosed_forecast_period_tag2 = soup.find('forecast-period', {'index': '1'})

enclosed_start_time_local2 = enclosed_forecast_period_tag2.get('start-time-local')

enclosed_end_time_local2 = enclosed_forecast_period_tag2.get('end-time-local')

enclosed_start_time_local_dt2 = datetime.strptime(enclosed_start_time_local2, '%Y-%m-%dT%H:%M:%S%z')
enclosed_start_time_local_words2 = enclosed_start_time_local_dt2.strftime('%A %B %d')

enclosed_end_time_local_dt2 = datetime.strptime(enclosed_end_time_local2, '%Y-%m-%dT%H:%M:%S%z')
enclosed_end_time_local_words2 = enclosed_end_time_local_dt2.strftime('%A %B %d, %I:%M %p')

enclosed_coastal_forecast_heading2 = f'Forecast for {enclosed_start_time_local_words2}'
enclosed_title = 'Sydney Enclosed Waters Forecast: Sydney Harbour, Pittwater and Botany Bay'

#CREATE AND FORMAT DOC

document = docx.Document()

document.styles['Normal'].font.size = docx.shared.Pt(10)

pic_section = document.add_paragraph()
pic_section.alignment = 1
run = pic_section.add_run()
run.add_picture('path_to_image.png')

issue_date = document.add_paragraph()
issue_date.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.CENTER
run = issue_date.add_run(title_date)
run.bold = True
run.font.size = docx.shared.Pt(14)

heading = document.add_paragraph()
heading.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.CENTER
run = heading.add_run(coastal_title)
run.bold = True
run.font.size = docx.shared.Pt(13)

forcaset_issued = document.add_paragraph()
run = forcaset_issued.add_run(forecast_issued_message)
run.font.size = docx.shared.Pt(6)

weather_situation = document.add_paragraph()
run = weather_situation.add_run("Weather Situation")
run.bold = True

synoptic_description = document.add_paragraph(synoptic)

forecast1_heading = document.add_heading(coastal_forecast_heading, level=2)

winds1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = winds1_paragraph.add_run('Winds: ')
bold_run.bold = True
# Add the second part of the text as a regular run
winds1_paragraph.add_run(winds1)

seas1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = seas1_paragraph.add_run('Seas: ')
bold_run.bold = True
# Add the second part of the text as a regular run
seas1_paragraph.add_run(seas1)

swell1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = swell1_paragraph.add_run('Swell: ')
bold_run.bold = True
# Add the second part of the text as a regular run
swell1_paragraph.add_run(swell1)

weather1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = weather1_paragraph.add_run('Weather: ')
bold_run.bold = True
# Add the second part of the text as a regular run
weather1_paragraph.add_run(weather1)

forecast2_heading = document.add_heading(coastal_forecast_heading2, level=2)

winds2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = winds2_paragraph.add_run('Winds: ')
bold_run.bold = True
# Add the second part of the text as a regular run
winds2_paragraph.add_run(winds2)

seas2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = seas2_paragraph.add_run('Seas: ')
bold_run.bold = True
# Add the second part of the text as a regular run
seas2_paragraph.add_run(seas2)

weather2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = weather2_paragraph.add_run('Weather: ')
bold_run.bold = True
# Add the second part of the text as a regular run
weather2_paragraph.add_run(weather2)

heading2 = document.add_paragraph()
heading2.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.CENTER
run = heading2.add_run(enclosed_title)
run.bold = True
run.font.size = docx.shared.Pt(13)

forcaset_issued2 = document.add_paragraph()
run = forcaset_issued2.add_run(enclosed_forecast_issued_message)
run.font.size = docx.shared.Pt(6)

enclosed_forecast1_heading = document.add_heading(enclosed_coastal_forecast_heading, level=2)

enclosed_winds1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_winds1_paragraph.add_run('Winds: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_winds1_paragraph.add_run(enclosed_winds1)

enclosed_seas1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_seas1_paragraph.add_run('Seas: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_seas1_paragraph.add_run(enclosed_seas1)

enclosed_weather1_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_weather1_paragraph.add_run('Weather: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_weather1_paragraph.add_run(enclosed_weather1)

enclosed_forecast2_heading = document.add_heading(enclosed_coastal_forecast_heading2, level=2)

enclosed_winds2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_winds2_paragraph.add_run('Winds: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_winds2_paragraph.add_run(enclosed_winds2)

enclosed_seas2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_seas2_paragraph.add_run('Seas: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_seas2_paragraph.add_run(enclosed_seas2)

enclosed_weather2_paragraph = document.add_paragraph()
# Add the first part of the text as a bold run
bold_run = enclosed_weather2_paragraph.add_run('Weather: ')
bold_run.bold = True
# Add the second part of the text as a regular run
enclosed_weather2_paragraph.add_run(enclosed_weather2)

# Save the document
document.save('marinerescueforecast.docx')

def convert_to_pdf(input_file, output_file):
    try:
        subprocess.check_call(['soffice', '--headless', '--convert-to', 'pdf', '--outdir', '.', input_file])
        subprocess.check_call(['mv', f'{input_file[:-5]}.pdf', output_file])
    except subprocess.CalledProcessError as e:
        print(f'Conversion failed with error code {e.returncode} and output: {e.output}')

input_file = 'marinerescueforecast.docx'
output_file = 'marinerescueforecast.pdf'

convert_to_pdf(input_file, output_file)

#Automate sending the email

# set up the SMTP server
smtp_server = 'smtp.gmail.com'
smtp_port = 587
smtp_username = os.environ.get('smtp_username')
smtp_password = os.environ.get('smtp_password')
smtp_connection = smtplib.SMTP(smtp_server, smtp_port)
smtp_connection.starttls()
smtp_connection.login(smtp_username, smtp_password)

# create the message
from_email = '[email protected]'
to_email = [os.environ.get('recipient1'),os.environ.get('recipient2')]
subject = 'Waters Forecast'
body = ''
message = MIMEMultipart()
message['From'] = from_email
message['To'] = ', '.join(to_email)
message['Subject'] = subject
message.attach(MIMEText(body, 'plain'))

#add attachments

filenames = ['marinerescueforecast.docx', 'marinerescueforecast.pdf','path_to_image.pdf']
for filename in filenames:
    attachment = open(filename, 'rb')
    part = MIMEBase('application', 'octet-stream')
    part.set_payload((attachment).read())
    encoders.encode_base64(part)
    part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
    message.attach(part)
    
# send the message
smtp_connection.sendmail(from_email, to_email, message.as_string())
smtp_connection.quit()

Kids Bank

Here is a little terminal application which essentially functions as a little bank account for the kids. Both kids have an account number they can input to access their “account.” From here they can make a deposit, withdrawal, and even check their balance. When they are done and choose to exit the program, they can check a little text file (ledger, if you will) to get a summary of their transactions and balance.

It’s fairly rad. One of these days, I must show it to the kids.

class User:
	def __init__(self, name):
		self.name = name

	def getName(self):
		return self.name

class Account(User):
	def __init__(self, name, id):
		super().__init__(name)
		self.id = id
		self.balance = 0 #start account with $0

	def welcome(self):
		print("Welcome " + str(self.name.getName()) + "!")

	def getID(self):
		return self.id

	def deposit(self, amount):
		self.balance = self.balance + amount

	def withdrawal(self, amount):
		self.balance = self.balance - amount

	def showBalance(self):
		return self.balance

	def display(self):
		print("Account holder: " + self.name.getName() + "\n" +
				"Account ID: " + str(self.id) + "\n" +
				"Current balance: " + str(self.balance))

	def withdrawalToLedger(self, amount):
		return("Account holder: " + str(self.name.getName()) + "\n" + 
			"Money withdrawn: " + str(amount) + "\n" +
			"Running Balance: " + str(self.balance) + "\n" + "\n")

	def depositToLedger(self, amount):
		return("Account holder: " + str(self.name.getName()) + "\n" + 
			"Money deposited: " + str(amount) + "\n" +
			"Running Balance: " + str(self.balance) + "\n" + "\n")


accounts = []

accounts.append(Account(User("Ri"), 123))
accounts.append(Account(User("Em"), 124))

selectUser = input("Welcome to KidsBank! \nEnter your account id\n" )
selectUser = int(selectUser)

for i in accounts:

	if i.getID() == selectUser:
		
		i.welcome()
		
		def showMenu():
			print("Select 1 to make a withdrawal")
			print("Select 2 to make a deposit")
			print("Select 3 to make a show balance")
			print("Select 4 to exit")

		def banking(menuSelection):
			while(menuSelection != 4):

				if(menuSelection == 1):
					withdrawAmount = input("how much money would you like to withdraw? \n")
					withdrawAmount = int(withdrawAmount)
					i.withdrawal(withdrawAmount)
					balance = i.showBalance()
					print("Your balance is: " + str(balance))
					f = open("Ledger.txt", "a")
					f.write(i.withdrawalToLedger(withdrawAmount))
					f.close()
					break;

				elif(menuSelection == 2):
					depositAmount = input("how much money would you like to deposit? \n")
					depositAmount = int(depositAmount)
					i.deposit(depositAmount)
					print("Your balance is: " + str(i.showBalance()))
					f = open("Ledger.txt", "a")
					f.write(i.depositToLedger(depositAmount))
					f.close()
					break;

				elif(menuSelection == 3):
					print("Your balance is: " + str(i.showBalance()))
					break;

				elif(menuSelection == 4):
					print("bye bye")

				else:
					print("Not one of the options. Exiting menu")
					break

		proceedOption = input("Ready to do some banking? Select: " + "\n" +
								"1 for yes" + "\n" +
								"2 for no" + "\n")
		
		proceedOption = int(proceedOption)

		while(proceedOption != 2):
			selection = input(showMenu())
			selection = int(selection)
			banking(selection)
			proceedOption = input("Got more to do? \nSelect 1 for yes or 2 for no \n")
			proceedOption = int(proceedOption)

		print("bye bye")
		break;

	else:
		print("That is not a valid account number")







Danbot

Without a doubt, the coolest thing I’ve ever deployed.

The problem: So I built a portal at work to manage incoming and outgoing repairs. However, I ended up building a version 2 of the portal and needed to bring across hundreds of entries from the first portal into the second. Now rather than simply getting the original database to talk to the new database nicely (read: I couldn’t do it), AND RATHER THAN manually copying across the data like a schmo, I opted to over engineer the solution.

Welcome, Danbot.

Here’s a little video of Danbot in all its glory:

Here is the code. It’s broken into two sections. Firstly, scraping that data from the original portal and saving it to a list. Secondly, reading from that list and entering into the new portal.

Note, I’ve changed ip addresses and credentials for obvious reasons.

Should you deploy Danbot in your own schemes, use it for good, not evil! I envision Danbot exists with a digital heart of gold.

#Get data from original site

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
driver = webdriver.Safari()
import time


#get destination
driver.get("http://xxx.xxx.xxx.xx/xxxx/xxxx/xxxx)

time.sleep(.5)

#enter log in details
text_username = driver.find_element(By.ID,'id_username')
text_username.send_keys("xxxx")
text_password = driver.find_element(By.ID,'id_password')
text_password.send_keys("xxxx")

#click log in button
type(driver.find_element(By.XPATH,'//*[@id="login-form"]/div[3]/input').click())

time.sleep(2)

#this gets inner text of form field
scrapeList = []
i = 1
while i < 101:
    
    repairData = []
    driver.find_element(By.XPATH,f'//*[@id="result_list"]/tbody/tr[{i}]/th/a').click()
    time.sleep(2)

    #ticket
    ticket = driver.find_element(By.XPATH, '//*[@id="id_ticket"]').get_property("value")
    repairData.append(ticket)
    
    #staff
    staff = driver.find_element(By.XPATH, "//*[@id='id_staff']").get_property("value")
    repairData.append(staff)
    
    #customer_name
    customer_name = driver.find_element(By.XPATH, "//*[@id='id_customer_name']").get_property("value")
    repairData.append(customer_name)
    
    #customer_number
    customer_number = driver.find_element(By.XPATH, "//*[@id='id_customer_number']").get_property("value")
    repairData.append(customer_number)
    
    #email
    email = driver.find_element(By.XPATH, "//*[@id='id_email']").get_property("value")
    repairData.append(email)
    
    #booked_in
    booked_in = driver.find_element(By.XPATH, "//*[@id='id_booked_in']").get_property("value")
    repairData.append(booked_in)
    
    #brand
    brand = driver.find_element(By.XPATH, "//*[@id='id_brand']").get_property("value")
    repairData.append(brand)
    
    #item
    item = driver.find_element(By.XPATH, "//*[@id='id_item']").get_property("value")
    repairData.append(item)
    
    #accessories_with_item
    accessories_with_item = driver.find_element(By.XPATH, "//*[@id='id_accessories_with_item']").get_property("value")
    repairData.append(accessories_with_item)
    
    #serial
    serial = driver.find_element(By.XPATH, "//*[@id='id_serial']").get_property("value")
    repairData.append(serial)
    
    #date_purchased
    date_purchased = driver.find_element(By.XPATH, "//*[@id='id_date_purchased']").get_property("value")
    repairData.append(date_purchased)
    
    #invoice_number
    invoice_number = driver.find_element(By.XPATH, "//*[@id='id_invoice_number']").get_property("value")
    repairData.append(invoice_number)
    
    #description
    description = driver.find_element(By.XPATH, "//*[@id='id_description']").get_property("value")
    repairData.append(description)
    
    #additional_comments
    additional_comments = driver.find_element(By.XPATH, "//*[@id='id_additional_comments']").get_property("value")
    repairData.append(additional_comments)
    
    #repairer
    repairer = driver.find_element(By.XPATH, "//*[@id='id_repairer']").get_property("value")
    repairData.append(repairer)
    
    #shop_stock_erp_sku
    shop_stock_erp_sku = driver.find_element(By.XPATH, "//*[@id='id_shop_stock_erp_sku']").get_property("value")
    repairData.append(shop_stock_erp_sku)
    
    #shop_stock_internal_ra_number
    shop_stock_internal_ra_number = driver.find_element(By.XPATH, "//*[@id='id_shop_stock_internal_ra_number']").get_property("value")
    repairData.append(shop_stock_internal_ra_number)
    
    #ra_number
    ra_number = driver.find_element(By.XPATH, "//*[@id='id_ra_number']").get_property("value")
    repairData.append(ra_number)
    
    #date_sent
    date_sent = driver.find_element(By.XPATH, "//*[@id='id_date_sent']").get_property("value")
    repairData.append(date_sent)
    
    #date_received
    date_received = driver.find_element(By.XPATH, "//*[@id='id_date_received']").get_property("value")
    repairData.append(date_received)
    
    #date_customer_contacted
    date_customer_contacted = driver.find_element(By.XPATH, "//*[@id='id_date_customer_contacted']").get_property("value")
    repairData.append(date_customer_contacted)
    
    #price
    price = driver.find_element(By.XPATH, "//*[@id='id_price']").get_property("value")
    repairData.append(price)
    
    #repair_notes
    repair_notes = driver.find_element(By.XPATH, "//*[@id='id_repair_notes']").get_property("value")
    repairData.append(repair_notes)
    
    #date_collected
    date_collected = driver.find_element(By.XPATH, "//*[@id='id_date_collected']").get_property("value")
    repairData.append(date_collected)
    
    #internal_comments
    internal_comments = driver.find_element(By.XPATH, "//*[@id='id_internal_comments']").get_property("value")
    repairData.append(internal_comments)
    
    #click save button
    driver.find_element(By.XPATH,'//*[@id="repairbooking_form"]/div/div/input[1]').click()
    time.sleep(2)
    i += 1
    
    scrapeList.append(repairData)

print("Done!")


#INSERT VALUES INTO PORTAL FROM SCRAPED LIST

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
driver = webdriver.Safari()
import time


#get destination
driver.get("http://xxx.xxx.xxx.xx/xxx/xxx/xxxxxxx/")

time.sleep(.5)

#enter log in details
text_username = driver.find_element(By.ID,'id_username')
text_username.send_keys("xxxxxx")
text_password = driver.find_element(By.ID,'id_password')
text_password.send_keys("xxxxx")

#click log in button
type(driver.find_element(By.XPATH,'//*[@id="login-form"]/div[3]/input').click())

time.sleep(1)

        #INPUT VALUES INTO FORM

i = 0    
counter = 0
while i < len(scrapeList):
    for list in scrapeList:

        #click add repair button
        driver.find_element(By.XPATH,'//*[@id="content-main"]/ul/li/a').click()
        time.sleep(1)

        #MAPPING INPUT FIELDS
        #ticket
        text_field_ticket = driver.find_element(By.ID, "id_ticket")
        text_field_ticket.send_keys(list[counter])
        counter += 1

        #staff
        text_field_staff = driver.find_element(By.ID, "id_staff")
        text_field_staff.send_keys(list[counter])
        counter += 1

        #customer_name
        text_field_customer_name = driver.find_element(By.ID, "id_customer_name")
        text_field_customer_name.send_keys(list[counter])
        counter += 1

        #customer_number
        text_field_customer_number = driver.find_element(By.ID, "id_customer_number")
        text_field_customer_number.send_keys(list[counter])
        counter += 1

        #email
        text_field_email= driver.find_element(By.ID, "id_email")
        text_field_email.send_keys(list[counter])
        counter += 1

        #booked_in
        text_field_booked_in = driver.find_element(By.ID, "id_booked_in")
        text_field_booked_in.send_keys(list[counter])
        counter += 1

        #brand
        text_field_brand = driver.find_element(By.ID, "id_brand")
        text_field_brand.send_keys(list[counter])
        counter += 1

        #item
        text_field_item = driver.find_element(By.ID, "id_item")
        text_field_item.send_keys(list[counter])
        counter += 1

        #accessories_with_item
        text_field_accessories_with_item = driver.find_element(By.ID, "id_accessories_with_item")
        text_field_accessories_with_item.send_keys(list[counter])
        counter += 1

        #serial
        text_field_serial = driver.find_element(By.ID, "id_serial")
        text_field_serial.send_keys(list[counter])
        counter += 1

        #date_purchased
        text_field_date_purchased = driver.find_element(By.ID, "id_date_purchased")
        text_field_date_purchased.send_keys(list[counter])
        counter += 1

        #invoice_number
        text_field_invoice_number = driver.find_element(By.ID, "id_invoice_number")
        text_field_invoice_number.send_keys(list[counter])
        counter += 1

        #description
        text_field_description = driver.find_element(By.ID, "id_description")
        text_field_description.send_keys(list[counter])
        counter += 1

        #additional_comments
        text_field_additional_comments = driver.find_element(By.ID, "id_additional_comments")
        text_field_additional_comments.send_keys(list[counter])
        counter += 1

        #repairer
        text_field_repairer = driver.find_element(By.ID, "id_repairer")
        text_field_repairer.send_keys(list[counter])
        counter += 1
        
        #shop_stock_internal_erp_sku
        text_field_shop_stock_erp_sku = driver.find_element(By.ID, "id_shop_stock_erp_sku")
        text_field_shop_stock_erp_sku.send_keys(list[counter])
        counter += 1

        #shop_stock_internal_ra_number
        text_field_shop_stock_internal_ra_number = driver.find_element(By.ID, "id_shop_stock_internal_ra_number")
        text_field_shop_stock_internal_ra_number.send_keys(list[counter])
        counter += 1

        #ra_number
        text_field_ra_number = driver.find_element(By.ID, "id_ra_number")
        text_field_ra_number.send_keys(list[counter])
        counter += 1

        #date_sent
        text_field_date_sent = driver.find_element(By.ID, "id_date_sent")
        text_field_date_sent.send_keys(list[counter])
        counter += 1

        #date_received
        text_field_date_received = driver.find_element(By.ID, "id_date_received")
        text_field_date_received.send_keys(list[counter])
        counter += 1

        #date_customer_contacted
        text_field_date_customer_contacted = driver.find_element(By.ID, "id_date_customer_contacted")
        text_field_date_customer_contacted.send_keys(list[counter])
        counter += 1

        #price
        text_field_price = driver.find_element(By.ID, "id_price")
        text_field_price.send_keys(list[counter])
        counter += 1

        #repair_notes
        text_field_repair_notes = driver.find_element(By.ID, "id_repair_notes")
        text_field_repair_notes.send_keys(list[counter])
        counter += 1

        #date_collected
        text_field_date_collected = driver.find_element(By.ID, "id_date_collected")
        text_field_date_collected.send_keys(list[counter])
        counter += 1

        #internal_comments
        text_field_internal_comments = driver.find_element(By.ID, "id_internal_comments")
        text_field_internal_comments.send_keys(list[counter])

        #click save button
        driver.find_element(By.XPATH,'//*[@id="repairbooking_form"]/div/div/input[1]').click()
        time.sleep(2)
        
        counter = 0
        i += 1
        print(i)

print('Finished!')

Obligatory Crypto Tracking v1

If you know a little bit of code, you are obliged to see if you can successfully cook up a way to track cryptocurrency. I offer you two slightly different versions of the same thing!

For v1, when you run the program it asks for the coin you are wanting to track. It then speaks to Coinspot in this instance, and prints the latest price in 5 second increments. You know, for all you day traders out there.

import requests
import time

f = open("coinspot.key")
full_key = f.read()
f.close()

f = open("fullsecret.key")
full_secret = f.read()
f.close()

r = open("readonly.key")
read_key = r.read()
r.close()

r = open("readonlysecret.key")
read_secret = r.read()
r.close()

headers = {'key':full_key, 'secret': full_secret}
target = 'orders'
url_base = "https://www.coinspot.com.au/pubapi/v2/latest"
url_request = url_base + target

coin = input("Enter coin code you are tracking ")

i = True
while i:
    response = requests.get(url_base, headers=headers)
 	data = response.json()


 	coin_ask = data['prices'][coin]['ask']
 	coin_ask = float(coin_ask)

 	print(coin_ask)
 	time.sleep(5)
 	i += 1

Obligatory Crypto Tracking v2

For v2, rather than asking which coin you want to track, it’s hardcoded with whatever coin you like. It checks for the latest price every 5 seconds, just like v1, however this time, IT PLAYS A TOTALLY AWESOME LITTLE GUITAR LICK WHEN THE PRICE DIPS TO YOUR PREDETERMINED THRESHOLD FOR PURCHASING THAT SWEET* CRYPTO GOODNESS.

To make this more rad, you’d make v3 prompt you for all the relevant inputs, including providing a list of alternate sound options to choose from.

*sweetness may vary

import requests
import time
from pygame import mixer

mixer.init()
sound = mixer.Sound("riff.mp3")

f = open("coinspot.key")
full_key = f.read()
f.close()

f = open("fullsecret.key")
full_secret = f.read()
f.close()

r = open("readonly.key")
read_key = r.read()
r.close()

r = open("readonlysecret.key")
read_secret = r.read()
r.close()

headers = {'key':full_key, 'secret': full_secret}
target = 'orders'
url_base = "https://www.coinspot.com.au/pubapi/v2/latest"
url_request = url_base + target

counter = 0
while(True):
	response = requests.get(url_base, headers=headers)
	data = response.json()
	eth_ask = data['prices']['eth']['ask']
	eth_ask = float(eth_ask)
	print(eth_ask)

	if eth_ask < 4018:
		print("The price is " + str(eth_ask) +". Buy!")
		sound.play()

	else:
		print("The price is " + str(eth_ask) +". Don't buy")

	time.sleep(5)