diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c5b92b6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 Gerry Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index bef7c17..c4e5d67 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# python \ No newline at end of file +### Python + +This repo will house python code that can be used freely (sanitized). Also, many of the functions have articles that go with them from my blog at [https://automationadmin.com](https://automationadmin.com). + +### DISCLAIMER + +Please do not use these scripts in a production environment without reading them over first. Please see the MIT [license](./LICENSE) for more information. \ No newline at end of file diff --git a/learning/.gitignore b/learning/.gitignore new file mode 100644 index 0000000..dfe709f --- /dev/null +++ b/learning/.gitignore @@ -0,0 +1,13 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ \ No newline at end of file diff --git a/learning/base-language/_template.py b/learning/base-language/_template.py new file mode 100644 index 0000000..3026687 --- /dev/null +++ b/learning/base-language/_template.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +# +################################################################ + + + +def main(): + print("called main function") + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/learning/base-language/calling-python-scripts/args-is-unique.py b/learning/base-language/calling-python-scripts/args-is-unique.py new file mode 100644 index 0000000..a916e30 --- /dev/null +++ b/learning/base-language/calling-python-scripts/args-is-unique.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +################################################################ +# When ran, it will return 1 if all values passed in are unique and 0 if any repeat +# Maybe a good example of testing that each argument passed is unique? + +# example: one repeates +# 5, 6, 6, 7 +# 0 + +# Example all unique +# 6 +# 1 + +# example: repeats +# 5,5 +# 0 + +# example: unique +# 6,7 +# 1 + +################################################################ + +def is_unique(s): + s = list(s) + s.sort() + + for i in range(len(s) - 1): + if s[i] == s[i + 1]: + return 0 + else: + return 1 + +if __name__ == "__main__": + print(is_unique(input())) \ No newline at end of file diff --git a/learning/base-language/calling-python-scripts/args.py b/learning/base-language/calling-python-scripts/args.py new file mode 100644 index 0000000..aa227ce --- /dev/null +++ b/learning/base-language/calling-python-scripts/args.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +################################################################ +# Script Arguments +# sys.argv is list of arguments passed to the script +# sys.argv[0] is by default the path of script +# and user defined arguments start with index 1 +################################################################ + +import sys + +# Print total number of arguments +print('Total number of arguments:', format(len(sys.argv))) + +# Print all arguments +print('Argument List:', str(sys.argv)) + +# Print arguments one by one +print('First argument:', str(sys.argv[0])) +print('Second argument:', str(sys.argv[1])) +print('Third argument:', str(sys.argv[2])) + +bob = str(sys.argv[0]) +print("magically, the filename for this script is: " + bob) +# save and exit +# now type `chmod +x ./max.py` and `python3 ./max.py 13 23 57` diff --git a/learning/base-language/classes/car.py b/learning/base-language/classes/car.py new file mode 100644 index 0000000..67c4f0d --- /dev/null +++ b/learning/base-language/classes/car.py @@ -0,0 +1,43 @@ +class Car: + """A simple attempt to represent a car.""" + + def __init__(self, make, model, year): + """Initialize attributes to describe a car.""" + self.make = make + self.model = model + self.year = year + self.odometer_reading = 0 + + def get_descriptive_name(self): + """Return a neatly formatted descriptive name.""" + long_name = f"{self.year} {self.make} {self.model}" + return long_name.title() + + def read_odometer(self): + """Print a statement showing the car's mileage.""" + print(f"This car has {self.odometer_reading} miles on it.") + + def update_odometer(self, mileage): + """ + Set the odometer reading to the given value. + Reject the change if it attempts to roll the odometer back. + """ + if mileage >= self.odometer_reading: + self.odometer_reading = mileage + else: + print("You can't roll back an odometer!") + + def increment_odometer(self, miles): + """Add the given amount to the odometer reading.""" + self.odometer_reading += miles + +my_used_car = Car('subaru', 'outback', 2015) +print(my_used_car.get_descriptive_name()) + +my_used_car.update_odometer(23_500) +my_used_car.read_odometer() + +my_used_car.increment_odometer(100) +my_used_car.read_odometer() + + diff --git a/learning/base-language/classes/dog.py b/learning/base-language/classes/dog.py new file mode 100644 index 0000000..e933561 --- /dev/null +++ b/learning/base-language/classes/dog.py @@ -0,0 +1,26 @@ +class Dog: + """A simple attempt to model a dog.""" + + def __init__(self, name, age): + """Initialize name and age attributes.""" + self.name = name + self.age = age + + def sit(self): + """Simulate a dog sitting in response to a command.""" + print(f"{self.name} is now sitting.") + + def roll_over(self): + """Simulate rolling over in response to a command.""" + print(f"{self.name} rolled over!") + +my_dog = Dog('Willie', 6) +your_dog = Dog('Lucy', 3) + +print(f"My dog's name is {my_dog.name}.") +print(f"My dog is {my_dog.age} years old.") +my_dog.sit() + +print(f"\nYour dog's name is {your_dog.name}.") +print(f"Your dog is {your_dog.age} years old.") +your_dog.sit() diff --git a/learning/base-language/classes/electric_car.py b/learning/base-language/classes/electric_car.py new file mode 100644 index 0000000..01c7d96 --- /dev/null +++ b/learning/base-language/classes/electric_car.py @@ -0,0 +1,43 @@ +class Car: + """A simple attempt to represent a car.""" + + def __init__(self, make, model, year): + self.make = make + self.model = model + self.year = year + self.odometer_reading = 0 + + def get_descriptive_name(self): + long_name = f"{self.year} {self.make} {self.model}" + return long_name.title() + + def read_odometer(self): + print(f"This car has {self.odometer_reading} miles on it.") + + def update_odometer(self, mileage): + if mileage >= self.odometer_reading: + self.odometer_reading = mileage + else: + print("You can't roll back an odometer!") + + def increment_odometer(self, miles): + self.odometer_reading += miles + +class ElectricCar(Car): + """Represent aspects of a car, specific to electric vehicles.""" + + def __init__(self, make, model, year): + """ + Initialize attributes of the parent class. + Then initialize attributes specific to an electric car. + """ + super().__init__(make, model, year) + self.battery_size = 75 + + def describe_battery(self): + """Print a statement describing the battery size.""" + print(f"This car has a {self.battery_size}-kWh battery.") + +my_tesla = ElectricCar('tesla', 'model s', 2019) +print(my_tesla.get_descriptive_name()) +my_tesla.describe_battery() \ No newline at end of file diff --git a/learning/base-language/classes/electric_car_with_battery.py b/learning/base-language/classes/electric_car_with_battery.py new file mode 100644 index 0000000..2165c9b --- /dev/null +++ b/learning/base-language/classes/electric_car_with_battery.py @@ -0,0 +1,65 @@ +class Car: + """A simple attempt to represent a car.""" + + def __init__(self, make, model, year): + self.make = make + self.model = model + self.year = year + self.odometer_reading = 0 + + def get_descriptive_name(self): + long_name = f"{self.year} {self.make} {self.model}" + return long_name.title() + + def read_odometer(self): + print(f"This car has {self.odometer_reading} miles on it.") + + def update_odometer(self, mileage): + if mileage >= self.odometer_reading: + self.odometer_reading = mileage + else: + print("You can't roll back an odometer!") + + def increment_odometer(self, miles): + self.odometer_reading += miles + +class Battery: + """A simple attempt to model a battery for an electric car.""" + + def __init__(self, battery_size=75): + """Initialize the battery's attributes.""" + self.battery_size = battery_size + + def describe_battery(self): + """Print a statement describing the battery size.""" + print(f"This car has a {self.battery_size}-kWh battery.") + + def get_range(self): + """Print a statement about the range this battery provides.""" + if self.battery_size == 75: + range = 260 + elif self.battery_size == 100: + range = 315 + + print(f"This car can go about {range} miles on a full charge.") + + +class ElectricCar(Car): + """Represent aspects of a car, specific to electric vehicles.""" + + def __init__(self, make, model, year): + """ + Initialize attributes of the parent class. + Then initialize attributes specific to an electric car. + """ + super().__init__(make, model, year) + self.battery = Battery() + + def describe_battery(self): + """Print a statement describing the battery size.""" + print(f"This car has a {self.battery_size}-kWh battery.") + +my_tesla = ElectricCar('tesla', 'model s', 2019) +print(my_tesla.get_descriptive_name()) +my_tesla.battery.describe_battery() +my_tesla.battery.get_range() \ No newline at end of file diff --git a/learning/base-language/classes/importing_classes/car.py b/learning/base-language/classes/importing_classes/car.py new file mode 100644 index 0000000..d6f470c --- /dev/null +++ b/learning/base-language/classes/importing_classes/car.py @@ -0,0 +1,65 @@ +"""A class that can be used to represent a car.""" + +class Car: + """A simple attempt to represent a car.""" + + def __init__(self, make, model, year): + """Initialize attributes to describe a car.""" + self.make = make + self.model = model + self.year = year + self.odometer_reading = 0 + + def get_descriptive_name(self): + """Return a neatly formatted descriptive name.""" + long_name = f"{self.year} {self.make} {self.model}" + return long_name.title() + + def read_odometer(self): + """Print a statement showing the car's mileage.""" + print(f"This car has {self.odometer_reading} miles on it.") + + def update_odometer(self, mileage): + """ + Set the odometer reading to the given value. + Reject the change if it attempts to roll the odometer back. + """ + if mileage >= self.odometer_reading: + self.odometer_reading = mileage + else: + print("You can't roll back an odometer!") + + def increment_odometer(self, miles): + """Add the given amount to the odometer reading.""" + self.odometer_reading += miles + +class Battery: + """A simple attempt to model a battery for an electric car.""" + + def __init__(self, battery_size=75): + """Initialize the battery's attributes.""" + self.battery_size = battery_size + + def describe_battery(self): + """Print a statement describing the battery size.""" + print(f"This car has a {self.battery_size}-kWh battery.") + + def get_range(self): + """Print a statement about the range this battery provides.""" + if self.battery_size == 75: + range = 260 + elif self.battery_size == 100: + range = 315 + + print(f"This car can go about {range} miles on a full charge.") + +class ElectricCar(Car): + """Models aspects of a car, specific to electric vehicles.""" + + def __init__(self, make, model, year): + """ + Initialize attributes of the parent class. + Then initialize attributes specific to an electric car. + """ + super().__init__(make, model, year) + self.battery = Battery() diff --git a/learning/base-language/classes/importing_classes/my_car.py b/learning/base-language/classes/importing_classes/my_car.py new file mode 100644 index 0000000..57583c6 --- /dev/null +++ b/learning/base-language/classes/importing_classes/my_car.py @@ -0,0 +1,7 @@ +from car import Car + +my_new_car = Car('audi', 'a4', 2019) +print(my_new_car.get_descriptive_name()) + +my_new_car.odometer_reading = 23 +my_new_car.read_odometer() diff --git a/learning/base-language/classes/importing_classes/my_cars.py b/learning/base-language/classes/importing_classes/my_cars.py new file mode 100644 index 0000000..54b1be2 --- /dev/null +++ b/learning/base-language/classes/importing_classes/my_cars.py @@ -0,0 +1,7 @@ +import car + +my_beetle = car.Car('volkswagen', 'beetle', 2019) +print(my_beetle.get_descriptive_name()) + +my_tesla = car.ElectricCar('tesla', 'roadster', 2019) +print(my_tesla.get_descriptive_name()) diff --git a/learning/base-language/classes/importing_classes/my_electric_car.py b/learning/base-language/classes/importing_classes/my_electric_car.py new file mode 100644 index 0000000..eaa2610 --- /dev/null +++ b/learning/base-language/classes/importing_classes/my_electric_car.py @@ -0,0 +1,7 @@ +from car import ElectricCar + +my_tesla = ElectricCar('tesla', 'model s', 2019) + +print(my_tesla.get_descriptive_name()) +my_tesla.battery.describe_battery() +my_tesla.battery.get_range() diff --git a/learning/base-language/custom-errors/custom-error-2.py b/learning/base-language/custom-errors/custom-error-2.py new file mode 100644 index 0000000..8c2ffad --- /dev/null +++ b/learning/base-language/custom-errors/custom-error-2.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +################################################################ +# Check for 'myfile.csv' in a certain directory +# Since this has more than two lines, it will output the files contents + +# You should also try renaming to 'myfile3.csv' and re-running to ensure it catches it +################################################################ + +class Error(Exception): + ''' Base class for other exceptions''' + pass + +# here we define our own error +class EmptyFileError(Error): + pass + +try: + filename = 'C:\\_gwill\\repo-home\\h1python\\learning\\base-language\\custom-errors\\myfile2.csv' + + #Use `with open(filename, encoding='utf-8') as thefile:` if the file has special chars + with open(filename) as thefile: + + #if the file has less than 2 lines, throw our own error + file_content = thefile.readlines() + + # At this point, file_content should contain a list like ['FirstName,LastName\n', 'Darth,Vader'] + line_count = len(file_content) + if line_count < 2: + raise EmptyFileError +except FileNotFoundError: + # catch if file doesn't exist + print("there is no myfile2.csv") +except EmptyFileError: + # catch our custom error + print('your file has less than two lines, exiting...') + thefile.close() +except Exception as e: + # catch any other exception + print('Failed: Exception was ' + str(e)) + thefile.close() +else: + # yay! we made it without errors, let's read the file! + + # since we did readlines(), it is a list object so we loop through and print + # If we instead did read() then you would just print + for one_line in file_content: + # and end='' in order for there not to be line breaks for each line + #print(one_line) + print(one_line, end='') + thefile.close() + + #print('Success!') diff --git a/learning/base-language/custom-errors/custom-error.py b/learning/base-language/custom-errors/custom-error.py new file mode 100644 index 0000000..520ada7 --- /dev/null +++ b/learning/base-language/custom-errors/custom-error.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +################################################################ +# Check for 'myfile.csv' in a certain directory +# Since this has less than two lines, it will output as such and exit +################################################################ + +class Error(Exception): + ''' Base class for other exceptions''' + pass + +# here we define our own error +class EmptyFileError(Error): + pass + +try: + filename = 'C:\\_gwill\\repo-home\\h1python\\learning\\base-language\\custom-errors\\myfile.csv' + + #with open(filename, encoding='utf-8') as thefile: + with open(filename) as thefile: + + #if the file has less than 2 lines, throw our own error + line_count = len(thefile.readlines()) + if line_count < 2: + raise EmptyFileError +except FileNotFoundError: + # catch if file doesn't exist + print("there is no myfile.csv") +except EmptyFileError: + # catch our custom error + print('your file has less than two lines, exiting...') + thefile.close() +except Exception as e: + # catch any other exception + print('Failed: Exception was ' + str(e)) + thefile.close() +else: + # yay! we made it without errors, let's read the file! + for one_line in thefile.readlines(): + # and end='' in order for there not to be spaces for each line + #print(one_line,end='') + print(one_line,end='') + thefile.close() + + print('Success!') diff --git a/learning/base-language/custom-errors/myfile.csv b/learning/base-language/custom-errors/myfile.csv new file mode 100644 index 0000000..97e1218 --- /dev/null +++ b/learning/base-language/custom-errors/myfile.csv @@ -0,0 +1 @@ +FirstName,LastName \ No newline at end of file diff --git a/learning/base-language/custom-errors/myfile2.csv b/learning/base-language/custom-errors/myfile2.csv new file mode 100644 index 0000000..3461a0e --- /dev/null +++ b/learning/base-language/custom-errors/myfile2.csv @@ -0,0 +1,2 @@ +FirstName,LastName +Darth,Vader \ No newline at end of file diff --git a/learning/base-language/date/date-time.py b/learning/base-language/date/date-time.py new file mode 100644 index 0000000..cd95521 --- /dev/null +++ b/learning/base-language/date/date-time.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +################################################################ +# Examples of date time +################################################################ + +import time +from datetime import datetime, timedelta + +# Example 0: My normal date format +date = '{:%Y-%m-%d %I:%M:%S %p}'.format(datetime.now()) +print(f"Example 0 - My normal date format: {date}") + +# Example 1: print local time in format `Fri Jan 1 08:39:49 2021` +# local time +date = time.localtime(time.time()) +# date currently holds an object `time.struct_time(tm_year=2021, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=38, tm_sec=31, tm_wday=4, tm_yday=1, tm_isdst=0)` +date = time.asctime(date) +print(f"Example 1: {date}") +# Returns `Fri Jan 1 08:39:49 2021` + +# Using example 1, you could store each of the values in integers +# uncomment and run to test: +# time.localtime() +# print('Day:',time.localtime().tm_mday) # output: Day: 6 +# print('Hour:',time.localtime().tm_hour) # output: Hour: 21 +# print('Minute:',time.localtime().tm_min) # output: Minute: 13 +# print('Second:',time.localtime().tm_sec) # output: Second: 11 + +# Example 2: print UTC time in format `Fri Jan 1 14:48:36 2021` +date = time.gmtime(time.time()) +date = time.asctime(date) +print(f"Example 2: {date}") + +# Example 3: print local time in format `2021-01-01 08:41:34.168579` +date = str(datetime.now()) +print(f"Example 3: {date}") + +# Example 4: Formating date time like '01-Jan-2021 08:54:27 AM' +date = '{:%d-%b-%Y %I:%M:%S %p}'.format(datetime.now()) +print(f"Example 4: {date}") + +# Example 5: Formating date time like '01/01/2021 08:55:36' +date = '{:%m/%d/%Y %H:%M:%S}'.format(datetime.now()) +print(f"Example 5: {date}") + +# Example 6: Using time delta (make sure to put from datetime import datetime, timedelta) +# Returns: +# One year in future `Example 6 - Future Date: 01-Jan-2022 01:01:17 PM` +# One year in the past `Example 6 - Past Date: 02-Jan-2020 01:01:17 PM` +futuredate = datetime.now() + timedelta(days=365, hours=4, minutes=2) +date = '{:%d-%b-%Y %I:%M:%S %p}'.format(futuredate) +print ( f"Example 6 - Future Date: {date}") + +pastdate = datetime.now() + timedelta(days=-365, hours=4, minutes=2) +date = '{:%d-%b-%Y %I:%M:%S %p}'.format(pastdate) +print ( f"Example 6 - Past Date: {date}") \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/alien.py b/learning/base-language/dictionaries-and-json/alien.py new file mode 100644 index 0000000..e3152ae --- /dev/null +++ b/learning/base-language/dictionaries-and-json/alien.py @@ -0,0 +1,18 @@ +alien_0 = {'x_position': 10, 'y_position': 25, 'speed': 'medium'} +print(f"Original position: {alien_0['x_position']}") + +# Move the alien to the right. +# Determine how far to move the alien based on its current speed. +if alien_0['speed'] == 'slow': + x_increment = 1 +elif alien_0['speed'] == 'medium': + x_increment = 2 +else: + # This must be a fast alien. + x_increment = 3 + +# The new position is the old position plus the increment. +alien_0['x_position'] = alien_0['x_position'] + x_increment +# This works because it is an int data type +print(f"New position: {alien_0['x_position']}") + diff --git a/learning/base-language/dictionaries-and-json/aliens.py b/learning/base-language/dictionaries-and-json/aliens.py new file mode 100644 index 0000000..bcfbd4f --- /dev/null +++ b/learning/base-language/dictionaries-and-json/aliens.py @@ -0,0 +1,18 @@ +# Make an empty list for storing aliens. +aliens = [] + +# Make 30 green aliens. +for alien_number in range(30): + new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'} + aliens.append(new_alien) + +for alien in aliens[:3]: + if alien['color'] == 'green': + alien['color'] = 'yellow' + alien['speed'] = 'medium' + alien['points'] = 10 + +# Show the first 5 aliens. +for alien in aliens[:5]: + print(alien) +print("...") diff --git a/learning/base-language/dictionaries-and-json/dict.py b/learning/base-language/dictionaries-and-json/dict.py new file mode 100644 index 0000000..a4415c6 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/dict.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +# hastables/dictionary +# note a comma (,) after each key-value pair +# and colon (:) between the Key-value pair instead of 'equals to' (=) used in PowerShell +table = {} # empty hashtable +table = dict() # alternative way to create empty hastable + +# hashtable with integer keys +table = { +1 : 'one', +2 : 'two', +3 : 'three' +} + +# hashtable is a data stucture that has key-value pair as a element +table = { +'firstname' : 'prateek', # in powershell single/double quotes around 'string keys' are optional, but not in python +'lastname' : 'singh' +} + +# adding key-value pairs or replace any existing key +age = {} +age['prateek']=27 +age['sam']=31 +age['susan']=25 + +# get dictionary keys or values +age.keys() +age.values() + +# returns a boolean true/false if key matches +age.__contains__('sam') + +# iterating through key-value pairs +for key, value in age.items(): + print("key: {0}, value: {1}".format(key, value)) + + +# created nested dictionary +employee = { +'name': { + 'firstname': 'prateek', + 'lastname': 'singh' + }, +'dateofjoining': { + 'day': 1, + 'month': 5, + 'year': 2017 + } +} + +age +{ 'gerry': 31, 'sam': 29, 'billy':10, 'sally': 18} +# Deleting a key-value pair +age.__delitem__('gerry') + +# accessing nested dictionary items +print(table['name']['firstname']) +print(table['dateofjoining']['year']) + +# >>> age = {} +# >>> age['gerry'] = 31 +# >>> age['sam'] = 29 +# >>> age['billy']=10 +# >>> age['sally']=18 +# >>> age +# {'gerry': 31, 'sam': 29, 'billy': 10, 'sally': 18} +# >>> age.__delitem__('gerry') +# >>> age +# {'sam': 29, 'billy': 10, 'sally': 18} +# >>> age.pop('sam') # deletes, the key-value pair but returns the value +# 29 +# >>> age +# {'billy': 10, 'sally': 18} +# >>> age.popitem() +# ('sally', 18) +# >>> age +# {'billy': 10} +# >>> + + +# sorting a dictionary +numbers = { +5 : 'five', +3 : 'three', +1 : 'one', +2 : 'two', +4 : 'four' +} + +dict(sorted(numbers.items())) # ascending order +dict(sorted(numbers.items(),reverse=True)) # descending order diff --git a/learning/base-language/dictionaries-and-json/dict2-.py b/learning/base-language/dictionaries-and-json/dict2-.py new file mode 100644 index 0000000..cb74888 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/dict2-.py @@ -0,0 +1,28 @@ +# { +# "id": "file", +# "value": "File", +# "popup": { +# "menuitem": [ +# {"value": "New", "onclick": "CreateNewDoc()"}, +# {"value": "Open", "onclick": "OpenDoc()"}, +# {"value": "Close", "onclick": "CloseDoc()"} +# ] +# } +# } + +import json +f = open('data.json') +data = json.load(f) +f.close() + +for (k, v) in data.items(): + print("Key: " + k) + print("Value: " + str(v)) + +# prints: +# Key: id +# Value: file +# Key: value +# Value: File +# Key: popup +# Value: {'menuitem': [{'value': 'New', 'onclick': 'CreateNewDoc()'}, {'value': 'Open', 'onclick': 'OpenDoc()'}, {'value': 'Close', 'onclick': 'CloseDoc()'}]} \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/favorite_languages.py b/learning/base-language/dictionaries-and-json/favorite_languages.py new file mode 100644 index 0000000..13ebaeb --- /dev/null +++ b/learning/base-language/dictionaries-and-json/favorite_languages.py @@ -0,0 +1,14 @@ +favorite_languages = { + 'jen': 'python', + 'sarah': 'c', + 'edward': 'ruby', + 'phil': 'python', + } + +friends = ['phil', 'sarah'] +for name in favorite_languages.keys(): + print(name.title()) + + if name in friends: + language = favorite_languages[name].title() + print(f"\t{name.title()}, I see you love {language}!") diff --git a/learning/base-language/dictionaries-and-json/many_users.py b/learning/base-language/dictionaries-and-json/many_users.py new file mode 100644 index 0000000..6914c04 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/many_users.py @@ -0,0 +1,22 @@ +users = { + 'aeinstein': { + 'first': 'albert', + 'last': 'einstein', + 'location': 'princeton', + }, + + 'mcurie': { + 'first': 'marie', + 'last': 'curie', + 'location': 'paris', + }, + + } + +for username, user_info in users.items(): + print(f"\nUsername: {username}") + full_name = f"{user_info['first']} {user_info['last']}" + location = user_info['location'] + + print(f"\tFull name: {full_name.title()}") + print(f"\tLocation: {location.title()}") diff --git a/learning/base-language/dictionaries-and-json/pizza.py b/learning/base-language/dictionaries-and-json/pizza.py new file mode 100644 index 0000000..5d321ea --- /dev/null +++ b/learning/base-language/dictionaries-and-json/pizza.py @@ -0,0 +1,12 @@ + # Store information about a pizza being ordered. +pizza = { + 'crust': 'thick', + 'toppings': ['mushrooms', 'extra cheese'], + } + +# Summarize the order. +print(f"You ordered a {pizza['crust']}-crust pizza " + "with the following toppings:") + +for topping in pizza['toppings']: + print("\t" + topping) diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts.json b/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts.json new file mode 100644 index 0000000..fc51816 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts.json @@ -0,0 +1,12 @@ +{ + "1": { + "count": 9071, + "lastreferrer": "https://something.com", + "page": "/downloads.html" + }, + "2": { + "count": 9074, + "lastreferrer": "https://somethingelse.com", + "page": "/downloads2.html" + } +} \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts_new.json b/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts_new.json new file mode 100644 index 0000000..bb47581 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/hitcounts_new.json @@ -0,0 +1,8 @@ +{ + "1": { + "data1": "data" + }, + "2": { + "data1": "data" + } +} \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dump.py b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dump.py new file mode 100644 index 0000000..0c44370 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dump.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +import json + +hits = { + '1': { + 'data1' : 'data' + }, + '2': { + 'data1' : 'data' + } +} +with open('hitcounts_new.json', 'w', encoding='utf-8') as out_file: + json.dump(hits, out_file, ensure_ascii=False, sort_keys=True) \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dumps.py b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dumps.py new file mode 100644 index 0000000..2a49119 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-dumps.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +import json + +json_string = ''' + { + "1": { + "count": 9071, + "lastreferrer": "https://something.com", + "page": "/downloads.html" + }, + "2": { + "count": 9074, + "lastreferrer": "https://somethingelse.com", + "page": "/downloads2.html" + } +} +''' + +# Load json from string +hits_data = json.loads(json_string) + +new_dict = json.dumps(hits_data, indent=2) + +print(new_dict) \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-load.py b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-load.py new file mode 100644 index 0000000..59b9e57 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-load.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +import json + +filename = './hitcounts.json' + +with open(filename, 'r', encoding='utf-8', newline='') as f: + hits = json.load(f) + +print(type(hits)) +# + +for p in hits: + print(p) + # prints 1 and 2 which are the keys + +for k,v in hits.items(): + print(k,v) + # prints dictionary + + +for k,v in hits.items(): + print(v['count']) + # prints one part of dictionary - 9071 and 9074 + +for k,v in hits.items(): + if k == "1": + print(v['count']) + # prints only 9071 since its key is equal to "1" \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-keyed-data.py b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-keyed-data.py new file mode 100644 index 0000000..8337ae2 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-keyed-data.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +#pg 315 + + +import json + +json_string = ''' + { + "1": { + "count": 9071, + "lastreferrer": "https://something.com", + "page": "/downloads.html" + }, + "2": { + "count": 9074, + "lastreferrer": "https://somethingelse.com", + "page": "/downloads2.html" + } +} +''' + +# Load json from string +hits_data = json.loads(json_string) + +# now you can loop through collection +for k,v in hits_data.items(): + print( f"{k}. {v['count']:>5} - {v['page']}" ) + +# Using positionals +for k,v in hits_data.items(): + print( '{}. {:>5} - {}'.format(k, v['count'], v['page'])) \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-unkeyed-data.py b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-unkeyed-data.py new file mode 100644 index 0000000..6b1d028 --- /dev/null +++ b/learning/base-language/dictionaries-and-json/standard-lib--json/json/json-loads-unkeyed-data.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +#pg 314 + +import json + +json_string = ''' + { + "website": [ + { + "count": 9071, + "lastreferrer": "https://something.com", + "page": "/downloads.html" + }, + { + "count": 9074, + "lastreferrer": "https://somethingelse.com", + "page": "/downloads2.html" + } + ] +} +''' + +# Load json from string +website_data = json.loads(json_string) + +# now you can loop through collection +for w in website_data['website']: + print(w['count'], w['page']) + +# Using my preferred method +for w in website_data['website']: + print('{} {}'.format(w['count'], w['page'])) \ No newline at end of file diff --git a/learning/base-language/dictionaries-and-json/user.py b/learning/base-language/dictionaries-and-json/user.py new file mode 100644 index 0000000..4b2c36f --- /dev/null +++ b/learning/base-language/dictionaries-and-json/user.py @@ -0,0 +1,9 @@ +user_0 = { + 'username': 'efermi', + 'first': 'enrico', + 'last': 'fermi', + } + +for key, value in user_0.items(): + print(f"\nKey: {key}") + print(f"Value: {value}") diff --git a/learning/base-language/error-handling-unittests/language_survey.py b/learning/base-language/error-handling-unittests/language_survey.py new file mode 100644 index 0000000..a461e93 --- /dev/null +++ b/learning/base-language/error-handling-unittests/language_survey.py @@ -0,0 +1,18 @@ +from survey import AnonymousSurvey + +# Define a question, and make a survey. +question = "What language did you first learn to speak?" +my_survey = AnonymousSurvey(question) + +# Show the question, and store responses to the question. +my_survey.show_question() +print("Enter 'q' at any time to quit.\n") +while True: + response = input("Language: ") + if response == 'q': + break + my_survey.store_response(response) + +# Show the survey results. +print("\nThank you to everyone who participated in the survey!") +my_survey.show_results() diff --git a/learning/base-language/error-handling-unittests/name_function.py b/learning/base-language/error-handling-unittests/name_function.py new file mode 100644 index 0000000..02c65d0 --- /dev/null +++ b/learning/base-language/error-handling-unittests/name_function.py @@ -0,0 +1,7 @@ +def get_formatted_name(first, last, middle=''): + """Generate a neatly formatted full name.""" + if middle: + full_name = f"{first} {middle} {last}" + else: + full_name = f"{first} {last}" + return full_name.title() diff --git a/learning/base-language/error-handling-unittests/names.py b/learning/base-language/error-handling-unittests/names.py new file mode 100644 index 0000000..34e1561 --- /dev/null +++ b/learning/base-language/error-handling-unittests/names.py @@ -0,0 +1,13 @@ +from name_function import get_formatted_name + +print("Enter 'q' at any time to quit.") +while True: + first = input("\nPlease give me a first name: ") + if first == 'q': + break + last = input("Please give me a last name: ") + if last == 'q': + break + + formatted_name = get_formatted_name(first, last) + print(f"\tNeatly formatted name: {formatted_name}.") diff --git a/learning/base-language/error-handling-unittests/survey.py b/learning/base-language/error-handling-unittests/survey.py new file mode 100644 index 0000000..78adb31 --- /dev/null +++ b/learning/base-language/error-handling-unittests/survey.py @@ -0,0 +1,21 @@ +class AnonymousSurvey: + """Collect anonymous answers to a survey question.""" + + def __init__(self, question): + """Store a question, and prepare to store responses.""" + self.question = question + self.responses = [] + + def show_question(self): + """Show the survey question.""" + print(self.question) + + def store_response(self, new_response): + """Store a single response to the survey.""" + self.responses.append(new_response) + + def show_results(self): + """Show all the responses that have been given.""" + print("Survey results:") + for response in self.responses: + print(f"- {response}") diff --git a/learning/base-language/error-handling-unittests/test_name_function.py b/learning/base-language/error-handling-unittests/test_name_function.py new file mode 100644 index 0000000..a32cfe8 --- /dev/null +++ b/learning/base-language/error-handling-unittests/test_name_function.py @@ -0,0 +1,20 @@ +import unittest + +from name_function import get_formatted_name + +class NamesTestCase(unittest.TestCase): + """Tests for 'name_function.py'.""" + + def test_first_last_name(self): + """Do names like 'Janis Joplin' work?""" + formatted_name = get_formatted_name('janis', 'joplin') + self.assertEqual(formatted_name, 'Janis Joplin') + + def test_first_last_middle_name(self): + """Do names like 'Wolfgang Amadeus Mozart' work?""" + formatted_name = get_formatted_name( + 'wolfgang', 'mozart', 'amadeus') + self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart') + +if __name__ == '__main__': + unittest.main() diff --git a/learning/base-language/error-handling-unittests/test_survey.py b/learning/base-language/error-handling-unittests/test_survey.py new file mode 100644 index 0000000..f7611b3 --- /dev/null +++ b/learning/base-language/error-handling-unittests/test_survey.py @@ -0,0 +1,28 @@ +import unittest +from survey import AnonymousSurvey + +class TestAnonymousSurvey(unittest.TestCase): + """Tests for the class AnonymousSurvey""" + + def setUp(self): + """ + Create a survey and a set of responses for use in all test methods. + """ + question = "What language did you first learn to speak?" + self.my_survey = AnonymousSurvey(question) + self.responses = ['English', 'Spanish', 'Mandarin'] + + def test_store_single_response(self): + """Test that a single response is stored properly.""" + self.my_survey.store_response(self.responses[0]) + self.assertIn(self.responses[0], self.my_survey.responses) + + def test_store_three_responses(self): + """Test that three individual responses are stored properly.""" + for response in self.responses: + self.my_survey.store_response(response) + for response in self.responses: + self.assertIn(response, self.my_survey.responses) + +if __name__ == '__main__': + unittest.main() diff --git a/learning/base-language/functions/formatted_name.py b/learning/base-language/functions/formatted_name.py new file mode 100644 index 0000000..f86f49a --- /dev/null +++ b/learning/base-language/functions/formatted_name.py @@ -0,0 +1,13 @@ +def get_formatted_name(first_name, last_name, middle_name=''): + """Return a full name, neatly formatted.""" + if middle_name: + full_name = f"{first_name} {middle_name} {last_name}" + else: + full_name = f"{first_name} {last_name}" + return full_name.title() + +musician = get_formatted_name('jimi', 'hendrix') +print(musician) + +musician = get_formatted_name('john', 'hooker', 'lee') +print(musician) diff --git a/learning/base-language/functions/greet_users.py b/learning/base-language/functions/greet_users.py new file mode 100644 index 0000000..9f8da87 --- /dev/null +++ b/learning/base-language/functions/greet_users.py @@ -0,0 +1,8 @@ +def greet_users(names): + """Print a simple greeting to each user in the list.""" + for name in names: + msg = f"Hello, {name.title()}!" + print(msg) + +usernames = ['hannah', 'ty', 'margot'] +greet_users(usernames) diff --git a/learning/base-language/functions/greeter.py b/learning/base-language/functions/greeter.py new file mode 100644 index 0000000..dc938ab --- /dev/null +++ b/learning/base-language/functions/greeter.py @@ -0,0 +1,20 @@ +def get_formatted_name(first_name, last_name): + """Return a full name, neatly formatted.""" + full_name = f"{first_name} {last_name}" + return full_name.title() + +# This is an infinite loop! +while True: + print("\nPlease tell me your name:") + print("(enter 'q' at any time to quit)") + + f_name = input("First name: ") + if f_name == 'q': + break + + l_name = input("Last name: ") + if l_name == 'q': + break + + formatted_name = get_formatted_name(f_name, l_name) + print(f"\nHello, {formatted_name}!") diff --git a/learning/base-language/functions/making_pizzas.py b/learning/base-language/functions/making_pizzas.py new file mode 100644 index 0000000..27876fb --- /dev/null +++ b/learning/base-language/functions/making_pizzas.py @@ -0,0 +1,4 @@ +import pizza + +pizza.make_pizza(16, 'pepperoni') +pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese') diff --git a/learning/base-language/functions/person.py b/learning/base-language/functions/person.py new file mode 100644 index 0000000..82e0b56 --- /dev/null +++ b/learning/base-language/functions/person.py @@ -0,0 +1,9 @@ +def build_person(first_name, last_name, age=None): + """Return a dictionary of information about a person.""" + person = {'first': first_name, 'last': last_name} + if age: + person['age'] = age + return person + +musician = build_person('jimi', 'hendrix', age=27) +print(musician) diff --git a/learning/base-language/functions/pets.py b/learning/base-language/functions/pets.py new file mode 100644 index 0000000..2530264 --- /dev/null +++ b/learning/base-language/functions/pets.py @@ -0,0 +1,6 @@ +def describe_pet(pet_name, animal_type='dog'): + """Display information about a pet.""" + print(f"\nI have a {animal_type}.") + print(f"My {animal_type}'s name is {pet_name.title()}.") + +describe_pet(pet_name='willie') \ No newline at end of file diff --git a/learning/base-language/functions/pizza.py b/learning/base-language/functions/pizza.py new file mode 100644 index 0000000..7ed5a90 --- /dev/null +++ b/learning/base-language/functions/pizza.py @@ -0,0 +1,5 @@ +def make_pizza(size, *toppings): + """Summarize the pizza we are about to make.""" + print(f"\nMaking a {size}-inch pizza with the following toppings:") + for topping in toppings: + print(f"- {topping}") diff --git a/learning/base-language/functions/printing_models.py b/learning/base-language/functions/printing_models.py new file mode 100644 index 0000000..926cfa7 --- /dev/null +++ b/learning/base-language/functions/printing_models.py @@ -0,0 +1,21 @@ +def print_models(unprinted_designs, completed_models): + """ + Simulate printing each design, until none are left. + Move each design to completed_models after printing. + """ + while unprinted_designs: + current_design = unprinted_designs.pop() + print(f"Printing model: {current_design}") + completed_models.append(current_design) + +def show_completed_models(completed_models): + """Show all the models that were printed.""" + print("\nThe following models have been printed:") + for completed_model in completed_models: + print(completed_model) + +unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron'] +completed_models = [] + +print_models(unprinted_designs, completed_models) +show_completed_models(completed_models) diff --git a/learning/base-language/functions/user_profile.py b/learning/base-language/functions/user_profile.py new file mode 100644 index 0000000..d57b220 --- /dev/null +++ b/learning/base-language/functions/user_profile.py @@ -0,0 +1,10 @@ +def build_profile(first, last, **user_info): + """Build a dictionary containing everything we know about a user.""" + user_info['first_name'] = first + user_info['last_name'] = last + return user_info + +user_profile = build_profile('albert', 'einstein', + location='princeton', + field='physics') +print(user_profile) diff --git a/learning/base-language/learn-x-in-y.py b/learning/base-language/learn-x-in-y.py new file mode 100644 index 0000000..f911067 --- /dev/null +++ b/learning/base-language/learn-x-in-y.py @@ -0,0 +1,1005 @@ +# https://learnxinyminutes.com/docs/python/ + +# Single line comments start with a number symbol. + +""" Multiline strings can be written + using three "s, and are often used + as documentation. +""" + +#################################################### +## 1. Primitive Datatypes and Operators +#################################################### + +# You have numbers +3 # => 3 + +# Math is what you would expect +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7.0 + +# Integer division rounds down for both positive and negative numbers. +5 // 3 # => 1 +-5 // 3 # => -2 +5.0 // 3.0 # => 1.0 # works on floats too +-5.0 // 3.0 # => -2.0 + +# The result of division is always a float +10.0 / 3 # => 3.3333333333333335 + +# Modulo operation +7 % 3 # => 1 + +# Exponentiation (x**y, x to the yth power) +2**3 # => 8 + +# Enforce precedence with parentheses +1 + 3 * 2 # => 7 +(1 + 3) * 2 # => 8 + +# Boolean values are primitives (Note: the capitalization) +True # => True +False # => False + +# negate with not +not True # => False +not False # => True + +# Boolean Operators +# Note "and" and "or" are case-sensitive +True and False # => False +False or True # => True + +# True and False are actually 1 and 0 but with different keywords +True + True # => 2 +True * 8 # => 8 +False - 5 # => -5 + +# Comparison operators look at the numerical value of True and False +0 == False # => True +1 == True # => True +2 == True # => False +-5 != False # => True + +# Using boolean logical operators on ints casts them to booleans for evaluation, but their non-cast value is returned +# Don't mix up with bool(ints) and bitwise and/or (&,|) +bool(0) # => False +bool(4) # => True +bool(-6) # => True +0 and 2 # => 0 +-5 or 0 # => -5 + +# Equality is == +1 == 1 # => True +2 == 1 # => False + +# Inequality is != +1 != 1 # => False +2 != 1 # => True + +# More comparisons +1 < 10 # => True +1 > 10 # => False +2 <= 2 # => True +2 >= 2 # => True + +# Seeing whether a value is in a range +1 < 2 and 2 < 3 # => True +2 < 3 and 3 < 2 # => False +# Chaining makes this look nicer +1 < 2 < 3 # => True +2 < 3 < 2 # => False + +# (is vs. ==) is checks if two variables refer to the same object, but == checks +# if the objects pointed to have the same values. +a = [1, 2, 3, 4] # Point a at a new list, [1, 2, 3, 4] +b = a # Point b at what a is pointing to +b is a # => True, a and b refer to the same object +b == a # => True, a's and b's objects are equal +b = [1, 2, 3, 4] # Point b at a new list, [1, 2, 3, 4] +b is a # => False, a and b do not refer to the same object +b == a # => True, a's and b's objects are equal + +# Strings are created with " or ' +"This is a string." +'This is also a string.' + +# Strings can be added too! But try not to do this. +"Hello " + "world!" # => "Hello world!" +# String literals (but not variables) can be concatenated without using '+' +"Hello " "world!" # => "Hello world!" + +# A string can be treated like a list of characters +"Hello world!"[0] # => 'H' + +# You can find the length of a string +len("This is a string") # => 16 + +# You can also format using f-strings or formatted string literals (in Python 3.6+) +name = "Reiko" +f"She said her name is {name}." # => "She said her name is Reiko" +# You can basically put any Python statement inside the braces and it will be output in the string. +f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long." + + +# None is an object +None # => None + +# Don't use the equality "==" symbol to compare objects to None +# Use "is" instead. This checks for equality of object identity. +"etc" is None # => False +None is None # => True + +# None, 0, and empty strings/lists/dicts/tuples all evaluate to False. +# All other values are True +bool(0) # => False +bool("") # => False +bool([]) # => False +bool({}) # => False +bool(()) # => False + +#################################################### +## 2. Variables and Collections +#################################################### + +# Python has a print function +print("I'm Python. Nice to meet you!") # => I'm Python. Nice to meet you! + +# By default the print function also prints out a newline at the end. +# Use the optional argument end to change the end string. +print("Hello, World", end="!") # => Hello, World! + +# Simple way to get input data from console +input_string_var = input("Enter some data: ") # Returns the data as a string +# Note: In earlier versions of Python, input() method was named as raw_input() + +# There are no declarations, only assignments. +# Convention is to use lower_case_with_underscores +some_var = 5 +some_var # => 5 + +# Accessing a previously unassigned variable is an exception. +# See Control Flow to learn more about exception handling. +some_unknown_var # Raises a NameError + +# if can be used as an expression +# Equivalent of C's '?:' ternary operator +"yay!" if 0 > 1 else "nay!" # => "nay!" + +# Lists store sequences +li = [] +# You can start with a prefilled list +other_li = [4, 5, 6] + +# Add stuff to the end of a list with append +li.append(1) # li is now [1] +li.append(2) # li is now [1, 2] +li.append(4) # li is now [1, 2, 4] +li.append(3) # li is now [1, 2, 4, 3] +# Remove from the end with pop +li.pop() # => 3 and li is now [1, 2, 4] +# Let's put it back +li.append(3) # li is now [1, 2, 4, 3] again. + +# Access a list like you would any array +li[0] # => 1 +# Look at the last element +li[-1] # => 3 + +# Looking out of bounds is an IndexError +li[4] # Raises an IndexError + +# You can look at ranges with slice syntax. +# The start index is included, the end index is not +# (It's a closed/open range for you mathy types.) +li[1:3] # Return list from index 1 to 3 => [2, 4] +li[2:] # Return list starting from index 2 => [4, 3] +li[:3] # Return list from beginning until index 3 => [1, 2, 4] +li[::2] # Return list selecting every second entry => [1, 4] +li[::-1] # Return list in reverse order => [3, 4, 2, 1] +# Use any combination of these to make advanced slices +# li[start:end:step] + +# Make a one layer deep copy using slices +li2 = li[:] # => li2 = [1, 2, 4, 3] but (li2 is li) will result in false. + +# Remove arbitrary elements from a list with "del" +del li[2] # li is now [1, 2, 3] + +# Remove first occurrence of a value +li.remove(2) # li is now [1, 3] +li.remove(2) # Raises a ValueError as 2 is not in the list + +# Insert an element at a specific index +li.insert(1, 2) # li is now [1, 2, 3] again + +# Get the index of the first item found matching the argument +li.index(2) # => 1 +li.index(4) # Raises a ValueError as 4 is not in the list + +# You can add lists +# Note: values for li and for other_li are not modified. +li + other_li # => [1, 2, 3, 4, 5, 6] + +# Concatenate lists with "extend()" +li.extend(other_li) # Now li is [1, 2, 3, 4, 5, 6] + +# Check for existence in a list with "in" +1 in li # => True + +# Examine the length with "len()" +len(li) # => 6 + + +# Tuples are like lists but are immutable. +tup = (1, 2, 3) +tup[0] # => 1 +tup[0] = 3 # Raises a TypeError + +# Note that a tuple of length one has to have a comma after the last element but +# tuples of other lengths, even zero, do not. +type((1)) # => +type((1,)) # => +type(()) # => + +# You can do most of the list operations on tuples too +len(tup) # => 3 +tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) +tup[:2] # => (1, 2) +2 in tup # => True + +# You can unpack tuples (or lists) into variables +a, b, c = (1, 2, 3) # a is now 1, b is now 2 and c is now 3 +# You can also do extended unpacking +a, *b, c = (1, 2, 3, 4) # a is now 1, b is now [2, 3] and c is now 4 +# Tuples are created by default if you leave out the parentheses +d, e, f = 4, 5, 6 # tuple 4, 5, 6 is unpacked into variables d, e and f +# respectively such that d = 4, e = 5 and f = 6 +# Now look how easy it is to swap two values +e, d = d, e # d is now 5 and e is now 4 + + +# Dictionaries store mappings from keys to values +empty_dict = {} +# Here is a prefilled dictionary +filled_dict = {"one": 1, "two": 2, "three": 3} + +# Note keys for dictionaries have to be immutable types. This is to ensure that +# the key can be converted to a constant hash value for quick look-ups. +# Immutable types include ints, floats, strings, tuples. +invalid_dict = {[1,2,3]: "123"} # => Raises a TypeError: unhashable type: 'list' +valid_dict = {(1,2,3):[1,2,3]} # Values can be of any type, however. + +# Look up values with [] +filled_dict["one"] # => 1 + +# Get all keys as an iterable with "keys()". We need to wrap the call in list() +# to turn it into a list. We'll talk about those later. Note - for Python +# versions <3.7, dictionary key ordering is not guaranteed. Your results might +# not match the example below exactly. However, as of Python 3.7, dictionary +# items maintain the order at which they are inserted into the dictionary. +list(filled_dict.keys()) # => ["three", "two", "one"] in Python <3.7 +list(filled_dict.keys()) # => ["one", "two", "three"] in Python 3.7+ + + +# Get all values as an iterable with "values()". Once again we need to wrap it +# in list() to get it out of the iterable. Note - Same as above regarding key +# ordering. +list(filled_dict.values()) # => [3, 2, 1] in Python <3.7 +list(filled_dict.values()) # => [1, 2, 3] in Python 3.7+ + +# Check for existence of keys in a dictionary with "in" +"one" in filled_dict # => True +1 in filled_dict # => False + +# Looking up a non-existing key is a KeyError +filled_dict["four"] # KeyError + +# Use "get()" method to avoid the KeyError +filled_dict.get("one") # => 1 +filled_dict.get("four") # => None +# The get method supports a default argument when the value is missing +filled_dict.get("one", 4) # => 1 +filled_dict.get("four", 4) # => 4 + +# "setdefault()" inserts into a dictionary only if the given key isn't present +filled_dict.setdefault("five", 5) # filled_dict["five"] is set to 5 +filled_dict.setdefault("five", 6) # filled_dict["five"] is still 5 + +# Adding to a dictionary +filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} +filled_dict["four"] = 4 # another way to add to dict + +# Remove keys from a dictionary with del +del filled_dict["one"] # Removes the key "one" from filled dict + +# From Python 3.5 you can also use the additional unpacking options +{'a': 1, **{'b': 2}} # => {'a': 1, 'b': 2} +{'a': 1, **{'a': 2}} # => {'a': 2} + + + +# Sets store ... well sets +empty_set = set() +# Initialize a set with a bunch of values. Yeah, it looks a bit like a dict. Sorry. +some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4} + +# Similar to keys of a dictionary, elements of a set have to be immutable. +invalid_set = {[1], 1} # => Raises a TypeError: unhashable type: 'list' +valid_set = {(1,), 1} + +# Add one more item to the set +filled_set = some_set +filled_set.add(5) # filled_set is now {1, 2, 3, 4, 5} +# Sets do not have duplicate elements +filled_set.add(5) # it remains as before {1, 2, 3, 4, 5} + +# Do set intersection with & +other_set = {3, 4, 5, 6} +filled_set & other_set # => {3, 4, 5} + +# Do set union with | +filled_set | other_set # => {1, 2, 3, 4, 5, 6} + +# Do set difference with - +{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} + +# Do set symmetric difference with ^ +{1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5} + +# Check if set on the left is a superset of set on the right +{1, 2} >= {1, 2, 3} # => False + +# Check if set on the left is a subset of set on the right +{1, 2} <= {1, 2, 3} # => True + +# Check for existence in a set with in +2 in filled_set # => True +10 in filled_set # => False + +# Make a one layer deep copy +filled_set = some_set.copy() # filled_set is {1, 2, 3, 4, 5} +filled_set is some_set # => False + + +#################################################### +## 3. Control Flow and Iterables +#################################################### + +# Let's just make a variable +some_var = 5 + +# Here is an if statement. Indentation is significant in Python! +# Convention is to use four spaces, not tabs. +# This prints "some_var is smaller than 10" +if some_var > 10: + print("some_var is totally bigger than 10.") +elif some_var < 10: # This elif clause is optional. + print("some_var is smaller than 10.") +else: # This is optional too. + print("some_var is indeed 10.") + + +""" +For loops iterate over lists +prints: + dog is a mammal + cat is a mammal + mouse is a mammal +""" +for animal in ["dog", "cat", "mouse"]: + # You can use format() to interpolate formatted strings + print("{} is a mammal".format(animal)) + +""" +"range(number)" returns an iterable of numbers +from zero to the given number +prints: + 0 + 1 + 2 + 3 +""" +for i in range(4): + print(i) + +""" +"range(lower, upper)" returns an iterable of numbers +from the lower number to the upper number +prints: + 4 + 5 + 6 + 7 +""" +for i in range(4, 8): + print(i) + +""" +"range(lower, upper, step)" returns an iterable of numbers +from the lower number to the upper number, while incrementing +by step. If step is not indicated, the default value is 1. +prints: + 4 + 6 +""" +for i in range(4, 8, 2): + print(i) + +""" +To loop over a list, and retrieve both the index and the value of each item in the list +prints: + 0 dog + 1 cat + 2 mouse +""" +animals = ["dog", "cat", "mouse"] +for i, value in enumerate(animals): + print(i, value) + +""" +While loops go until a condition is no longer met. +prints: + 0 + 1 + 2 + 3 +""" +x = 0 +while x < 4: + print(x) + x += 1 # Shorthand for x = x + 1 + +# Handle exceptions with a try/except block +try: + # Use "raise" to raise an error + raise IndexError("This is an index error") +except IndexError as e: + pass # Pass is just a no-op. Usually you would do recovery here. +except (TypeError, NameError): + pass # Multiple exceptions can be handled together, if required. +else: # Optional clause to the try/except block. Must follow all except blocks + print("All good!") # Runs only if the code in try raises no exceptions +finally: # Execute under all circumstances + print("We can clean up resources here") + +# Instead of try/finally to cleanup resources you can use a with statement +with open("myfile.txt") as f: + for line in f: + print(line) + +# Writing to a file +contents = {"aa": 12, "bb": 21} +with open("myfile1.txt", "w+") as file: + file.write(str(contents)) # writes a string to a file + +with open("myfile2.txt", "w+") as file: + file.write(json.dumps(contents)) # writes an object to a file + +# Reading from a file +with open('myfile1.txt', "r+") as file: + contents = file.read() # reads a string from a file +print(contents) +# print: {"aa": 12, "bb": 21} + +with open('myfile2.txt', "r+") as file: + contents = json.load(file) # reads a json object from a file +print(contents) +# print: {"aa": 12, "bb": 21} + + +# Python offers a fundamental abstraction called the Iterable. +# An iterable is an object that can be treated as a sequence. +# The object returned by the range function, is an iterable. + +filled_dict = {"one": 1, "two": 2, "three": 3} +our_iterable = filled_dict.keys() +print(our_iterable) # => dict_keys(['one', 'two', 'three']). This is an object that implements our Iterable interface. + +# We can loop over it. +for i in our_iterable: + print(i) # Prints one, two, three + +# However we cannot address elements by index. +our_iterable[1] # Raises a TypeError + +# An iterable is an object that knows how to create an iterator. +our_iterator = iter(our_iterable) + +# Our iterator is an object that can remember the state as we traverse through it. +# We get the next object with "next()". +next(our_iterator) # => "one" + +# It maintains state as we iterate. +next(our_iterator) # => "two" +next(our_iterator) # => "three" + +# After the iterator has returned all of its data, it raises a StopIteration exception +next(our_iterator) # Raises StopIteration + +# We can also loop over it, in fact, "for" does this implicitly! +our_iterator = iter(our_iterable) +for i in our_iterator: + print(i) # Prints one, two, three + +# You can grab all the elements of an iterable or iterator by calling list() on it. +list(our_iterable) # => Returns ["one", "two", "three"] +list(our_iterator) # => Returns [] because state is saved + + +#################################################### +## 4. Functions +#################################################### + +# Use "def" to create new functions +def add(x, y): + print("x is {} and y is {}".format(x, y)) + return x + y # Return values with a return statement + +# Calling functions with parameters +add(5, 6) # => prints out "x is 5 and y is 6" and returns 11 + +# Another way to call functions is with keyword arguments +add(y=6, x=5) # Keyword arguments can arrive in any order. + +# You can define functions that take a variable number of +# positional arguments +def varargs(*args): + return args + +varargs(1, 2, 3) # => (1, 2, 3) + +# You can define functions that take a variable number of +# keyword arguments, as well +def keyword_args(**kwargs): + return kwargs + +# Let's call it to see what happens +keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} + + +# You can do both at once, if you like +def all_the_args(*args, **kwargs): + print(args) + print(kwargs) +""" +all_the_args(1, 2, a=3, b=4) prints: + (1, 2) + {"a": 3, "b": 4} +""" + +# When calling functions, you can do the opposite of args/kwargs! +# Use * to expand tuples and use ** to expand kwargs. +args = (1, 2, 3, 4) +kwargs = {"a": 3, "b": 4} +all_the_args(*args) # equivalent to all_the_args(1, 2, 3, 4) +all_the_args(**kwargs) # equivalent to all_the_args(a=3, b=4) +all_the_args(*args, **kwargs) # equivalent to all_the_args(1, 2, 3, 4, a=3, b=4) + +# Returning multiple values (with tuple assignments) +def swap(x, y): + return y, x # Return multiple values as a tuple without the parenthesis. + # (Note: parenthesis have been excluded but can be included) + +x = 1 +y = 2 +x, y = swap(x, y) # => x = 2, y = 1 +# (x, y) = swap(x,y) # Again parenthesis have been excluded but can be included. + +# Function Scope +x = 5 + +def set_x(num): + # Local var x not the same as global variable x + x = num # => 43 + print(x) # => 43 + +def set_global_x(num): + global x + print(x) # => 5 + x = num # global var x is now set to 6 + print(x) # => 6 + +set_x(43) +set_global_x(6) + + +# Python has first class functions +def create_adder(x): + def adder(y): + return x + y + return adder + +add_10 = create_adder(10) +add_10(3) # => 13 + +# There are also anonymous functions +(lambda x: x > 2)(3) # => True +(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5 + +# There are built-in higher order functions +list(map(add_10, [1, 2, 3])) # => [11, 12, 13] +list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3] + +list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7] + +# We can use list comprehensions for nice maps and filters +# List comprehension stores the output as a list which can itself be a nested list +[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] +[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] + +# You can construct set and dict comprehensions as well. +{x for x in 'abcddeef' if x not in 'abc'} # => {'d', 'e', 'f'} +{x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} + + +#################################################### +## 5. Modules +#################################################### + +# You can import modules +import math +print(math.sqrt(16)) # => 4.0 + +# You can get specific functions from a module +from math import ceil, floor +print(ceil(3.7)) # => 4.0 +print(floor(3.7)) # => 3.0 + +# You can import all functions from a module. +# Warning: this is not recommended +from math import * + +# You can shorten module names +import math as m +math.sqrt(16) == m.sqrt(16) # => True + +# Python modules are just ordinary Python files. You +# can write your own, and import them. The name of the +# module is the same as the name of the file. + +# You can find out which functions and attributes +# are defined in a module. +import math +dir(math) + +# If you have a Python script named math.py in the same +# folder as your current script, the file math.py will +# be loaded instead of the built-in Python module. +# This happens because the local folder has priority +# over Python's built-in libraries. + + +#################################################### +## 6. Classes +#################################################### + +# We use the "class" statement to create a class +class Human: + + # A class attribute. It is shared by all instances of this class + species = "H. sapiens" + + # Basic initializer, this is called when this class is instantiated. + # Note that the double leading and trailing underscores denote objects + # or attributes that are used by Python but that live in user-controlled + # namespaces. Methods(or objects or attributes) like: __init__, __str__, + # __repr__ etc. are called special methods (or sometimes called dunder methods) + # You should not invent such names on your own. + def __init__(self, name): + # Assign the argument to the instance's name attribute + self.name = name + + # Initialize property + self._age = 0 + + # An instance method. All methods take "self" as the first argument + def say(self, msg): + print("{name}: {message}".format(name=self.name, message=msg)) + + # Another instance method + def sing(self): + return 'yo... yo... microphone check... one two... one two...' + + # A class method is shared among all instances + # They are called with the calling class as the first argument + @classmethod + def get_species(cls): + return cls.species + + # A static method is called without a class or instance reference + @staticmethod + def grunt(): + return "*grunt*" + + # A property is just like a getter. + # It turns the method age() into an read-only attribute of the same name. + # There's no need to write trivial getters and setters in Python, though. + @property + def age(self): + return self._age + + # This allows the property to be set + @age.setter + def age(self, age): + self._age = age + + # This allows the property to be deleted + @age.deleter + def age(self): + del self._age + + +# When a Python interpreter reads a source file it executes all its code. +# This __name__ check makes sure this code block is only executed when this +# module is the main program. +if __name__ == '__main__': + # Instantiate a class + i = Human(name="Ian") + i.say("hi") # "Ian: hi" + j = Human("Joel") + j.say("hello") # "Joel: hello" + # i and j are instances of type Human, or in other words: they are Human objects + + # Call our class method + i.say(i.get_species()) # "Ian: H. sapiens" + # Change the shared attribute + Human.species = "H. neanderthalensis" + i.say(i.get_species()) # => "Ian: H. neanderthalensis" + j.say(j.get_species()) # => "Joel: H. neanderthalensis" + + # Call the static method + print(Human.grunt()) # => "*grunt*" + + # Cannot call static method with instance of object + # because i.grunt() will automatically put "self" (the object i) as an argument + print(i.grunt()) # => TypeError: grunt() takes 0 positional arguments but 1 was given + + # Update the property for this instance + i.age = 42 + # Get the property + i.say(i.age) # => "Ian: 42" + j.say(j.age) # => "Joel: 0" + # Delete the property + del i.age + # i.age # => this would raise an AttributeError + + +#################################################### +## 6.1 Inheritance +#################################################### + +# Inheritance allows new child classes to be defined that inherit methods and +# variables from their parent class. + +# Using the Human class defined above as the base or parent class, we can +# define a child class, Superhero, which inherits the class variables like +# "species", "name", and "age", as well as methods, like "sing" and "grunt" +# from the Human class, but can also have its own unique properties. + +# To take advantage of modularization by file you could place the classes above in their own files, +# say, human.py + +# To import functions from other files use the following format +# from "filename-without-extension" import "function-or-class" + +from human import Human + + +# Specify the parent class(es) as parameters to the class definition +class Superhero(Human): + + # If the child class should inherit all of the parent's definitions without + # any modifications, you can just use the "pass" keyword (and nothing else) + # but in this case it is commented out to allow for a unique child class: + # pass + + # Child classes can override their parents' attributes + species = 'Superhuman' + + # Children automatically inherit their parent class's constructor including + # its arguments, but can also define additional arguments or definitions + # and override its methods such as the class constructor. + # This constructor inherits the "name" argument from the "Human" class and + # adds the "superpower" and "movie" arguments: + def __init__(self, name, movie=False, + superpowers=["super strength", "bulletproofing"]): + + # add additional class attributes: + self.fictional = True + self.movie = movie + # be aware of mutable default values, since defaults are shared + self.superpowers = superpowers + + # The "super" function lets you access the parent class's methods + # that are overridden by the child, in this case, the __init__ method. + # This calls the parent class constructor: + super().__init__(name) + + # override the sing method + def sing(self): + return 'Dun, dun, DUN!' + + # add an additional instance method + def boast(self): + for power in self.superpowers: + print("I wield the power of {pow}!".format(pow=power)) + + +if __name__ == '__main__': + sup = Superhero(name="Tick") + + # Instance type checks + if isinstance(sup, Human): + print('I am human') + if type(sup) is Superhero: + print('I am a superhero') + + # Get the Method Resolution search Order used by both getattr() and super() + # This attribute is dynamic and can be updated + print(Superhero.__mro__) # => (, + # => , ) + + # Calls parent method but uses its own class attribute + print(sup.get_species()) # => Superhuman + + # Calls overridden method + print(sup.sing()) # => Dun, dun, DUN! + + # Calls method from Human + sup.say('Spoon') # => Tick: Spoon + + # Call method that exists only in Superhero + sup.boast() # => I wield the power of super strength! + # => I wield the power of bulletproofing! + + # Inherited class attribute + sup.age = 31 + print(sup.age) # => 31 + + # Attribute that only exists within Superhero + print('Am I Oscar eligible? ' + str(sup.movie)) + +#################################################### +## 6.2 Multiple Inheritance +#################################################### + +# Another class definition +# bat.py +class Bat: + + species = 'Baty' + + def __init__(self, can_fly=True): + self.fly = can_fly + + # This class also has a say method + def say(self, msg): + msg = '... ... ...' + return msg + + # And its own method as well + def sonar(self): + return '))) ... (((' + +if __name__ == '__main__': + b = Bat() + print(b.say('hello')) + print(b.fly) + + +# And yet another class definition that inherits from Superhero and Bat +# superhero.py +from superhero import Superhero +from bat import Bat + +# Define Batman as a child that inherits from both Superhero and Bat +class Batman(Superhero, Bat): + + def __init__(self, *args, **kwargs): + # Typically to inherit attributes you have to call super: + # super(Batman, self).__init__(*args, **kwargs) + # However we are dealing with multiple inheritance here, and super() + # only works with the next base class in the MRO list. + # So instead we explicitly call __init__ for all ancestors. + # The use of *args and **kwargs allows for a clean way to pass arguments, + # with each parent "peeling a layer of the onion". + Superhero.__init__(self, 'anonymous', movie=True, + superpowers=['Wealthy'], *args, **kwargs) + Bat.__init__(self, *args, can_fly=False, **kwargs) + # override the value for the name attribute + self.name = 'Sad Affleck' + + def sing(self): + return 'nan nan nan nan nan batman!' + + +if __name__ == '__main__': + sup = Batman() + + # Get the Method Resolution search Order used by both getattr() and super(). + # This attribute is dynamic and can be updated + print(Batman.__mro__) # => (, + # => , + # => , + # => , ) + + # Calls parent method but uses its own class attribute + print(sup.get_species()) # => Superhuman + + # Calls overridden method + print(sup.sing()) # => nan nan nan nan nan batman! + + # Calls method from Human, because inheritance order matters + sup.say('I agree') # => Sad Affleck: I agree + + # Call method that exists only in 2nd ancestor + print(sup.sonar()) # => ))) ... ((( + + # Inherited class attribute + sup.age = 100 + print(sup.age) # => 100 + + # Inherited attribute from 2nd ancestor whose default value was overridden. + print('Can I fly? ' + str(sup.fly)) # => Can I fly? False + + + +#################################################### +## 7. Advanced +#################################################### + +# Generators help you make lazy code. +def double_numbers(iterable): + for i in iterable: + yield i + i + +# Generators are memory-efficient because they only load the data needed to +# process the next value in the iterable. This allows them to perform +# operations on otherwise prohibitively large value ranges. +# NOTE: `range` replaces `xrange` in Python 3. +for i in double_numbers(range(1, 900000000)): # `range` is a generator. + print(i) + if i >= 30: + break + +# Just as you can create a list comprehension, you can create generator +# comprehensions as well. +values = (-x for x in [1,2,3,4,5]) +for x in values: + print(x) # prints -1 -2 -3 -4 -5 to console/terminal + +# You can also cast a generator comprehension directly to a list. +values = (-x for x in [1,2,3,4,5]) +gen_to_list = list(values) +print(gen_to_list) # => [-1, -2, -3, -4, -5] + + +# Decorators +# In this example `beg` wraps `say`. If say_please is True then it +# will change the returned message. +from functools import wraps + + +def beg(target_function): + @wraps(target_function) + def wrapper(*args, **kwargs): + msg, say_please = target_function(*args, **kwargs) + if say_please: + return "{} {}".format(msg, "Please! I am poor :(") + return msg + + return wrapper + + +@beg +def say(say_please=False): + msg = "Can you buy me a beer?" + return msg, say_please + + +print(say()) # Can you buy me a beer? +print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :( + diff --git a/learning/base-language/lists/lists.py b/learning/base-language/lists/lists.py new file mode 100644 index 0000000..dd35b1e --- /dev/null +++ b/learning/base-language/lists/lists.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 + +################################################################ +# lists +################################################################ + +days = ["Mon","Tue","Wed","Thu","Fri","Sat"] +print(days) # this will print arrays in array format + +#printing all elements of an array +for d in days: + print(d) + + +# creating empty lists +fruits = [] +fruits = list() + +# adding elements to list +fruits.append('apple') +fruits.append('banana') +fruits.append('orange') + + +# >>> cars.append("ford") +# >>> print(cars) +# ['ford'] +# >>> cars.append("chevy") +# >>> print(cars) +# ['ford', 'chevy'] +# >>> print(cars[0]) +# ford +# >>> print(cars[1]) +# chevy +# >>>print(len(cars)) +# 2 + + +# change value +## >>> cars[-1] = 'kia' +## >>> print(cars) +##['ford', 'kia'] +##>>> + +# insert at element number: +#vowel.insert(3, 'u') # .insert(index, element) + +# to reverse a list +# >>> array = list('spongebob') +# >>> reversed(array) # doesn't work +# +# >>> list(reversed(array)) +# ['b', 'o', 'b', 'e', 'g', 'n', 'o', 'p', 's'] +# >>> + + + +# assigning list elements to multiple variables +array = [1,2,3,4,5] +a,b,c,d,e = array +# this will assign a the value of 1, b the value of 2 and so on... + +# example +# >>> array = [2,4,6,18] +# >>> gerry, bob, tim, jim = array +# >>> gerry +# 2 +# >>> time +# Traceback (most recent call last): +# File "", line 1, in +# NameError: name 'time' is not defined +# >>> time +# Traceback (most recent call last): +# File "", line 1, in +# NameError: name 'time' is not defined +# >>> tim +# 6 + + +# delete elements of an array using del, remove() and pop() +colors = ["violet", "indigo", "blue", "green", "yellow", "orange", "red"] +# deleting by index +del colors[4] +# deleting by value +colors.remove("blue") # removes only the first occurrence +colors.pop(3) # .pop(index) deletes and returns the element +print(colors) + +# slicing lists +# array[startindex:end:step] +animal = ["dog", "cat", "cow", "pig", "giraffe"] +print(animal[1:4]) # animal[start:end] items start through end-1 +print(animal[-3:-1]) +print(animal[2:]) # animal[start:] items start through the rest of the array +print(animal[-4:]) +print(animal[:3]) # animal[:end] items from the beginning through end-1 +print(animal[:]) # copy of the whole array +print(animal[1:4:2]) # animal[start:end:step] start through not past end, by step + +# two dimensional lists + +# creating 2D lists +array = [1, 2, [3.1, 3.2, 3.3], 4] +array[2][1] # fetching values +array[2][2] +array[2][0] = 5 # assigning values +print(array) + +# tuples cannot be modified, read only - no add, delete +tuple = (5, 6, 7, 18) # () parenthesis to create tuples +print('Tuple:', tuple) +print("Tuple index 1:", tuple[1]) # number in square bracket is index of element you want to access + +# define sets +x = set('python') +y = set('powershell') + +print(x - y) # All the elements in x but not in y +# union +print(x | y) # Unique elements in x or y or both +# intersection +print(x & y) # Elements in both x and y +print(x ^ y) # Elements in x or y but not in both + + +################################### +## Arrays ## +################################### +# import the module, since Array is not a native data structure + +import array +a = array.array("I",[1,2,3,4,5]) # homogeneous, strong typed array +type(a) # get data type of the array +# not implicitly typecasted, you've to explicitly typecast elements with the array data type +a = array.array("I",[1,2,3,int(4.3),5]) +# throws error 'TypeError: integer argument expected, got float' +a = array.array("I",[1,2,3,4.3,5]) + +# operations on array +a.insert(1,7) # inserting elements +a.pop(3) # delete and return an element +a.reverse() # reverse the array + +# access elements +a[0] # list first element +a[-1] # list last element + +import array as arr + +numbers_list = [2, 5, 62, 5, 42, 52, 418, 5] +numbers_array = arr.array('i', numbers_list) + +print(numbers_array[2:5]) # 3rd to 5th +print(numbers_array[:-5]) # beginning to 4th +print(numbers_array[5:]) # 6th to end +print(numbers_array[:]) # beginning to end + +# see https://www.programiz.com/python-programming/array +# Unless you don't really need arrays (array module may be needed to interface with C code), their use is not highly recommended. + + +cars = ['bmw', 'audi', 'toyota', 'subaru'] +print(cars) + +cars.reverse() +print(cars) + +motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati'] +print(motorcycles) + +too_expensive = 'ducati' +motorcycles.remove(too_expensive) +print(motorcycles) +print(f"\nA {too_expensive.title()} is too expensive for me.") + +# Tuple +dimensions = (200, 50) +for dimension in dimensions: + print(dimension) + +even_numbers = list(range(2, 11, 2)) +print(even_numbers) + + +## copy lists + +my_foods = ['pizza', 'falafel', 'carrot cake'] +friend_foods = my_foods[:] + +my_foods.append('cannoli') +friend_foods.append('ice cream') + +print("My favorite foods are:") +print(my_foods) + +print("\nMy friend's favorite foods are:") +print(friend_foods) + +## list comprehension + +squares = [value**2 for value in range(1, 11)] +print(squares) + +## not in list +banned_users = ['andrew', 'carolina', 'david'] +user = 'marie' + +if user not in banned_users: + print(f"{user.title()}, you can post a response if you wish.") + + +## in keyword + +cars = ['audi', 'bmw', 'subaru', 'toyota'] + +for car in cars: + if car == 'bmw': + print(car.upper()) + else: + print(car.title()) + diff --git a/learning/base-language/lists/sort-list.py b/learning/base-language/lists/sort-list.py new file mode 100644 index 0000000..e9e4a77 --- /dev/null +++ b/learning/base-language/lists/sort-list.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +################################################################ +# From an imported list, this will sort and return a string +# pg 202 +################################################################ + +def alphalist( orginal_list=[] ): + + # Create working copy of list passed in + sorted_list = orginal_list.copy() + + sorted_list.sort() + + final_list = '' + + for name in sorted_list: + final_list += name + ', ' + + # strip the ending comma and space + final_list = final_list[:-2] + + # To return a list instead of a string, uncomment these lines and comment out the original return statement + # new_list = [] + # splitOrig = final_list.split(', ') + # for item in splitOrig: + # new_list.append(item) + # return new_list + + return final_list + +# Call function +list = [ 'a', 'q', 'b'] +new_list = alphalist(list) + +print(new_list) + +print('new list type = ') +print(type(new_list)) + +# preferred, single line +print('new list type = ',end='') +print(type(new_list)) diff --git a/learning/base-language/loops/cities.py b/learning/base-language/loops/cities.py new file mode 100644 index 0000000..19b2463 --- /dev/null +++ b/learning/base-language/loops/cities.py @@ -0,0 +1,10 @@ +prompt = "\nPlease enter the name of a city you have visited:" +prompt += "\n(Enter 'quit' when you are finished.) " + +while True: + city = input(prompt) + + if city == 'quit': + break + else: + print(f"I'd love to go to {city.title()}!") diff --git a/learning/base-language/loops/confirmed_users.py b/learning/base-language/loops/confirmed_users.py new file mode 100644 index 0000000..92e9d2c --- /dev/null +++ b/learning/base-language/loops/confirmed_users.py @@ -0,0 +1,17 @@ +# Start with users that need to be verified, +# and an empty list to hold confirmed users. +unconfirmed_users = ['alice', 'brian', 'candace'] +confirmed_users = [] + +# Verify each user until there are no more unconfirmed users. +# Move each verified user into the list of confirmed users. +while unconfirmed_users: + current_user = unconfirmed_users.pop() + + print(f"Verifying user: {current_user.title()}") + confirmed_users.append(current_user) + +# Display all confirmed users. +print("\nThe following users have been confirmed:") +for confirmed_user in confirmed_users: + print(confirmed_user.title()) diff --git a/learning/base-language/loops/counting.py b/learning/base-language/loops/counting.py new file mode 100644 index 0000000..4a33268 --- /dev/null +++ b/learning/base-language/loops/counting.py @@ -0,0 +1,7 @@ +current_number = 0 +while current_number < 10: + current_number += 1 + if current_number % 2 == 0: + continue + + print(current_number) diff --git a/learning/base-language/loops/even_or_odd.py b/learning/base-language/loops/even_or_odd.py new file mode 100644 index 0000000..23ebe90 --- /dev/null +++ b/learning/base-language/loops/even_or_odd.py @@ -0,0 +1,7 @@ +number = input("Enter a number, and I'll tell you if it's even or odd: ") +number = int(number) + +if number % 2 == 0: + print(f"\nThe number {number} is even.") +else: + print(f"\nThe number {number} is odd.") diff --git a/learning/base-language/loops/for-loop.py b/learning/base-language/loops/for-loop.py new file mode 100644 index 0000000..a983909 --- /dev/null +++ b/learning/base-language/loops/for-loop.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +# For loops are often used to iterate through collection objects: + +# Iterate through lists +shuttles = ['columbia', 'endeavour', 'challenger','discovery', 'atlantis', 'enterprise', 'pathfinder'] + +# Read shuttles list and enumerate into index and value +for index, value in enumerate(shuttles): + print(index, value) + +''' +prints: +0 columbia +1 endeavour +2 challenger +3 discovery +4 atlantis +5 enterprise +6 pathfinde +''' + +# Iterate through lists 2: using the enumerate() function to get index instead +days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"] +for i,d in enumerate(days): + print(i,d) + + +# Iterate through dictionary +dnsservers = {} +dnsservers = {"us": "ns1.cyberciti.com", "uk": "ns2.cyberciti.biz", "asia": "ns3.cyberciti.org" } + +# Python for loop for key,value using dict data type +# for key, value in dict.items(): +for location, server in dnsservers.items(): + print(server, "dns server is located in", location) + +''' +returns: +ns1.cyberciti.com dns server is located in us +ns2.cyberciti.biz dns server is located in uk +ns3.cyberciti.org dns server is located in asia +''' + +# basic for loop +# Prints out 1,2,3,4 +for i in range(1, 10): + if( i %5 == 0 ): + break + print(i) +else: + print("this is not printed because for loop is terminated because of break but not due to fail in condition") + +# use the break and continue statements +print('*'*10) +custom_range = range(5,30) +for x in custom_range: + #if (x == 7): + # break # when x ==7 loop will end and next block of code will execute + if (x %2 == 0) : + continue # when x == even number, loop will go back to start and run again ignoring the even numbers and only printing odd numbers + print(x) \ No newline at end of file diff --git a/learning/base-language/loops/greeter.py b/learning/base-language/loops/greeter.py new file mode 100644 index 0000000..037a98c --- /dev/null +++ b/learning/base-language/loops/greeter.py @@ -0,0 +1,5 @@ +prompt = "If you tell us who you are, we can personalize the messages you see." +prompt += "\nWhat is your first name? " + +name = input(prompt) +print(f"\nHello, {name}!") diff --git a/learning/base-language/loops/mountain_poll.py b/learning/base-language/loops/mountain_poll.py new file mode 100644 index 0000000..9e1655d --- /dev/null +++ b/learning/base-language/loops/mountain_poll.py @@ -0,0 +1,22 @@ +responses = {} + +# Set a flag to indicate that polling is active. +polling_active = True + +while polling_active: + # Prompt for the person's name and response. + name = input("\nWhat is your name? ") + response = input("Which mountain would you like to climb someday? ") + + # Store the response in the dictionary. + responses[name] = response + + # Find out if anyone else is going to take the poll. + repeat = input("Would you like to let another person respond? (yes/ no) ") + if repeat == 'no': + polling_active = False + +# Polling is complete. Show the results. +print("\n--- Poll Results ---") +for name, response in responses.items(): + print(f"{name} would like to climb {response}.") diff --git a/learning/base-language/loops/parrot.py b/learning/base-language/loops/parrot.py new file mode 100644 index 0000000..f23bcf0 --- /dev/null +++ b/learning/base-language/loops/parrot.py @@ -0,0 +1,11 @@ +prompt = "\nTell me something, and I will repeat it back to you:" +prompt += "\nEnter 'quit' to end the program. " + +active = True +while active: + message = input(prompt) + + if message == 'quit': + active = False + else: + print(message) diff --git a/learning/base-language/loops/pets.py b/learning/base-language/loops/pets.py new file mode 100644 index 0000000..97e7311 --- /dev/null +++ b/learning/base-language/loops/pets.py @@ -0,0 +1,7 @@ +pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat'] +print(pets) + +while 'cat' in pets: + pets.remove('cat') + +print(pets) diff --git a/learning/base-language/loops/rollercoaster.py b/learning/base-language/loops/rollercoaster.py new file mode 100644 index 0000000..7308efe --- /dev/null +++ b/learning/base-language/loops/rollercoaster.py @@ -0,0 +1,7 @@ +height = input("How tall are you, in inches? ") +height = int(height) + +if height >= 48: + print("\nYou're tall enough to ride!") +else: + print("\nYou'll be able to ride when you're a little older.") diff --git a/learning/base-language/loops/variables-conditionals-and-while-loop.py b/learning/base-language/loops/variables-conditionals-and-while-loop.py new file mode 100644 index 0000000..5a34d98 --- /dev/null +++ b/learning/base-language/loops/variables-conditionals-and-while-loop.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +################################################################ +# In WSL, first time setup: +# sudo apt-get update && sudo apt-get upgrade -y && sudo apt clean && sudo apt autoremove +# sudo apt-get install python3-pip + +# Don't spend too much time here, just see my post at https://automationadmin.com/2020/06/python-basic-syntax +################################################################ + +################################### +## Variables ## +################################### + +# assign var +a = 5 +print(a) + +# one to many +a = b = c = 3 +print(a) +print(b) +print(c) + +# many to many +a, b, c = 1, 4, 'gerry' +print(a) +print(b) +print(c) + +# read input +name = input("Enter your name: ") +print(name) + +################################### +## Conditionals ## +################################### +# python doesn't have switch cases, I pretty much just use if, elif, else instead +print("\n\nConditional:") +x, y = 100, 100 + +# conditional flow uses if, elif, else +if(x < y): + st = "x is less than y" +elif(x == y): + st = "x is the same as y" +else: + st ="x is greater than y" + +print(st) +# x is the same as y + +################################### +## While Loop ## +################################### + +# Prints out 0,1,2,3,4 +count = 0 +while count < 5: + print(count) + count += 1 # This is the same as count = count + 1 + +# Same thing, breaks on 5 or else would be infinite loop +count = 0 +while True: + print(count) + count += 1 + if count >= 5: + break + +count=0 +while(count<5): + print(count) + count +=1 +else: + # print("count value reached %d" %(count)) # older string formatting https://python-reference.readthedocs.io/en/latest/docs/str/formatting.html + print('count reached {}'.format(count)) + +################################### +## Inspections ## +################################### + +f = 0 +type(f) +# tells you the class. You can then look it up in python docs +# or directly in the terminal: +help(f) + diff --git a/learning/base-language/loops/while-division-calculator.py b/learning/base-language/loops/while-division-calculator.py new file mode 100644 index 0000000..f974688 --- /dev/null +++ b/learning/base-language/loops/while-division-calculator.py @@ -0,0 +1,16 @@ +print("Give me two numbers, and I'll divide them.") +print("Enter 'q' to quit.") + +while True: + first_number = input("\nFirst number: ") + if first_number == 'q': + break + second_number = input("Second number: ") + if second_number == 'q': + break + try: + answer = int(first_number) / int(second_number) + except ZeroDivisionError: + print("You can't divide by 0!") + else: + print(answer) diff --git a/learning/base-language/print/print.py b/learning/base-language/print/print.py new file mode 100644 index 0000000..e87ec04 --- /dev/null +++ b/learning/base-language/print/print.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +################################################################ +# Different ways to use print +################################################################ + +# Example 0: For python 3.6+, use f-strings most often +name = 'Gerry' +adjective = 'funny' +noun = 'person' +verb = 'runs' +print(f"Example 0: {name} is a {adjective} {noun} that {verb}") + +# Example 1: Cast var to string (if not already) +print("Example 1: This is a string " + str(123) ) + +# Example 2: multiple vars +name = 'Gerry' +adjective = 'funny' +noun = 'person' +verb = 'runs' +print('Example 2:',name,'is a',adjective,noun,'that',verb) +# notice the commas will add spaces, not really sure why? + +# Example 3: Positional +# This is my go-to if I'm not doing f strings +string = 'Example 3: {} is a {} {} that {}'.format(name, adjective,noun,verb) +print(string) +# Gerry is a funny person that runs + +# Example 4: Use numbers in braces to define sequence of arguments consumed by the string +string = 'Example 4: a:{0} b:{2} c:{1}'.format('red','green','blue') +print(string) +# 'a:red b:blue c:green' + +# Example 5: Adding zeros +string = 'Example 5: {0:03d}'.format(5) +print(string) +#'005' + +# Example 6: Using dictionaries for formatting +coor = {'latitude': '31.24E', 'longitude': '-125.181N'} +string = 'Example 6: Coordinates: {latitude}, {longitude}'.format(**coor) +print(string) +# 'Coordinates: 31.24E, -125.181N' \ No newline at end of file diff --git a/learning/base-language/read-write-files/loop-through-filesystem.py b/learning/base-language/read-write-files/loop-through-filesystem.py new file mode 100644 index 0000000..d96e179 --- /dev/null +++ b/learning/base-language/read-write-files/loop-through-filesystem.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +################################################################ +# Moving files in the file system +################################################################ + +import os +import shutil + +directory = '/myapp' + +try: + items = os.scandir(directory) +except Exception as e: + print("No files in the directory") + exit(0) + +match = 0 + +for item in items: + # print('processing :', item.name) + if item.path.endswith(".csv") and item.is_file() and item.name.find('dataset') == 0: + match = match + 1 + print('Found file to process: ', item.path) + dest = '/myapp/processed/' + item.name + # do something with the file + # move file to destination + try: + shutil.move(item.path, dest) + except Exception as e: + print("Unable to move folder to destination: ", item.name) + else: + pass + + if match == 0: + print("No files in the directory that match expected format") + exit(0) + else: + pass diff --git a/learning/base-language/read-write-files/read-txt/add-space-to-every-second-line.py b/learning/base-language/read-write-files/read-txt/add-space-to-every-second-line.py new file mode 100644 index 0000000..521191b --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/add-space-to-every-second-line.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +################################################################ +# Imports a text file +# Prints its output. +# If the index of the line is even, don't include a line break +# If the index of the line is odd, include a line break and spaces at begining +################################################################ + +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/quotes.txt" + +with open(filename,'r') as f: + + for one_line in enumerate(f.readlines()): + + # print(one_line) + # output: (0, '"The greatest glory in living lies not in never falling, but in rising every time we fall."\n') + + # if counter is even, print with no extra newline - remember indexes start at 0 so take line number minus 1! + + if one_line[0] %2 == 0: + # Pay attention that our conditional is comparing one+line[0] which is an index + # But then printing one_line[1] which is the quote + print(one_line[1],end='') + # otherwise print a couple spaces and add an extra newline + else: + print(' ' + one_line[1]) + +# prints: +# "The greatest glory in living lies not in never falling, but in rising every time we fall." +# Nelson Mandela + +# "The way to get started is to quit talking and begin doing." +# Walt Disney + +# "Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma – which is living with the results of other people's thinking." +# Steve Jobs + +# "If life were predictable it would cease to be life, and be without flavor." +# Eleanor Roosevelt + +# "If you look at what you have in life, you'll always have more. If you look at what you don't have in life, you'll never have enough." +# Oprah Winfrey + +# "If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." +# James Cameron + +# "Life is what happens when you're busy making other plans." +# John Lennon \ No newline at end of file diff --git a/learning/base-language/read-write-files/read-txt/files/alice.txt b/learning/base-language/read-write-files/read-txt/files/alice.txt new file mode 100644 index 0000000..a86da07 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/files/alice.txt @@ -0,0 +1,3736 @@ +Project Gutenberg’s Alice’s Adventures in Wonderland, by Lewis Carroll + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.org + + +Title: Alice’s Adventures in Wonderland + +Author: Lewis Carroll + +Posting Date: June 25, 2008 [EBook #11] +Release Date: March, 1994 +Last Updated: October 6, 2016 + +Language: English + +Character set encoding: UTF-8 + +*** START OF THIS PROJECT GUTENBERG EBOOK ALICE’S ADVENTURES IN WONDERLAND *** + + + + + + + + + + +ALICE’S ADVENTURES IN WONDERLAND + +Lewis Carroll + +THE MILLENNIUM FULCRUM EDITION 3.0 + + + + +CHAPTER I. Down the Rabbit-Hole + +Alice was beginning to get very tired of sitting by her sister on the +bank, and of having nothing to do: once or twice she had peeped into the +book her sister was reading, but it had no pictures or conversations in +it, ‘and what is the use of a book,’ thought Alice ‘without pictures or +conversations?’ + +So she was considering in her own mind (as well as she could, for the +hot day made her feel very sleepy and stupid), whether the pleasure +of making a daisy-chain would be worth the trouble of getting up and +picking the daisies, when suddenly a White Rabbit with pink eyes ran +close by her. + +There was nothing so VERY remarkable in that; nor did Alice think it so +VERY much out of the way to hear the Rabbit say to itself, ‘Oh dear! +Oh dear! I shall be late!’ (when she thought it over afterwards, it +occurred to her that she ought to have wondered at this, but at the time +it all seemed quite natural); but when the Rabbit actually TOOK A WATCH +OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on, +Alice started to her feet, for it flashed across her mind that she had +never before seen a rabbit with either a waistcoat-pocket, or a watch +to take out of it, and burning with curiosity, she ran across the field +after it, and fortunately was just in time to see it pop down a large +rabbit-hole under the hedge. + +In another moment down went Alice after it, never once considering how +in the world she was to get out again. + +The rabbit-hole went straight on like a tunnel for some way, and then +dipped suddenly down, so suddenly that Alice had not a moment to think +about stopping herself before she found herself falling down a very deep +well. + +Either the well was very deep, or she fell very slowly, for she had +plenty of time as she went down to look about her and to wonder what was +going to happen next. First, she tried to look down and make out what +she was coming to, but it was too dark to see anything; then she +looked at the sides of the well, and noticed that they were filled with +cupboards and book-shelves; here and there she saw maps and pictures +hung upon pegs. She took down a jar from one of the shelves as +she passed; it was labelled ‘ORANGE MARMALADE’, but to her great +disappointment it was empty: she did not like to drop the jar for fear +of killing somebody, so managed to put it into one of the cupboards as +she fell past it. + +‘Well!’ thought Alice to herself, ‘after such a fall as this, I shall +think nothing of tumbling down stairs! How brave they’ll all think me at +home! Why, I wouldn’t say anything about it, even if I fell off the top +of the house!’ (Which was very likely true.) + +Down, down, down. Would the fall NEVER come to an end! ‘I wonder how +many miles I’ve fallen by this time?’ she said aloud. ‘I must be getting +somewhere near the centre of the earth. Let me see: that would be four +thousand miles down, I think--’ (for, you see, Alice had learnt several +things of this sort in her lessons in the schoolroom, and though this +was not a VERY good opportunity for showing off her knowledge, as there +was no one to listen to her, still it was good practice to say it over) +‘--yes, that’s about the right distance--but then I wonder what Latitude +or Longitude I’ve got to?’ (Alice had no idea what Latitude was, or +Longitude either, but thought they were nice grand words to say.) + +Presently she began again. ‘I wonder if I shall fall right THROUGH the +earth! How funny it’ll seem to come out among the people that walk with +their heads downward! The Antipathies, I think--’ (she was rather glad +there WAS no one listening, this time, as it didn’t sound at all the +right word) ‘--but I shall have to ask them what the name of the country +is, you know. Please, Ma’am, is this New Zealand or Australia?’ (and +she tried to curtsey as she spoke--fancy CURTSEYING as you’re falling +through the air! Do you think you could manage it?) ‘And what an +ignorant little girl she’ll think me for asking! No, it’ll never do to +ask: perhaps I shall see it written up somewhere.’ + +Down, down, down. There was nothing else to do, so Alice soon began +talking again. ‘Dinah’ll miss me very much to-night, I should think!’ +(Dinah was the cat.) ‘I hope they’ll remember her saucer of milk at +tea-time. Dinah my dear! I wish you were down here with me! There are no +mice in the air, I’m afraid, but you might catch a bat, and that’s very +like a mouse, you know. But do cats eat bats, I wonder?’ And here Alice +began to get rather sleepy, and went on saying to herself, in a dreamy +sort of way, ‘Do cats eat bats? Do cats eat bats?’ and sometimes, ‘Do +bats eat cats?’ for, you see, as she couldn’t answer either question, +it didn’t much matter which way she put it. She felt that she was dozing +off, and had just begun to dream that she was walking hand in hand with +Dinah, and saying to her very earnestly, ‘Now, Dinah, tell me the truth: +did you ever eat a bat?’ when suddenly, thump! thump! down she came upon +a heap of sticks and dry leaves, and the fall was over. + +Alice was not a bit hurt, and she jumped up on to her feet in a moment: +she looked up, but it was all dark overhead; before her was another +long passage, and the White Rabbit was still in sight, hurrying down it. +There was not a moment to be lost: away went Alice like the wind, and +was just in time to hear it say, as it turned a corner, ‘Oh my ears +and whiskers, how late it’s getting!’ She was close behind it when she +turned the corner, but the Rabbit was no longer to be seen: she found +herself in a long, low hall, which was lit up by a row of lamps hanging +from the roof. + +There were doors all round the hall, but they were all locked; and when +Alice had been all the way down one side and up the other, trying every +door, she walked sadly down the middle, wondering how she was ever to +get out again. + +Suddenly she came upon a little three-legged table, all made of solid +glass; there was nothing on it except a tiny golden key, and Alice’s +first thought was that it might belong to one of the doors of the hall; +but, alas! either the locks were too large, or the key was too small, +but at any rate it would not open any of them. However, on the second +time round, she came upon a low curtain she had not noticed before, and +behind it was a little door about fifteen inches high: she tried the +little golden key in the lock, and to her great delight it fitted! + +Alice opened the door and found that it led into a small passage, not +much larger than a rat-hole: she knelt down and looked along the passage +into the loveliest garden you ever saw. How she longed to get out of +that dark hall, and wander about among those beds of bright flowers and +those cool fountains, but she could not even get her head through the +doorway; ‘and even if my head would go through,’ thought poor Alice, ‘it +would be of very little use without my shoulders. Oh, how I wish I could +shut up like a telescope! I think I could, if I only knew how to begin.’ +For, you see, so many out-of-the-way things had happened lately, +that Alice had begun to think that very few things indeed were really +impossible. + +There seemed to be no use in waiting by the little door, so she went +back to the table, half hoping she might find another key on it, or at +any rate a book of rules for shutting people up like telescopes: this +time she found a little bottle on it, [‘which certainly was not here +before,’ said Alice,) and round the neck of the bottle was a paper +label, with the words ‘DRINK ME’ beautifully printed on it in large +letters. + +It was all very well to say ‘Drink me,’ but the wise little Alice was +not going to do THAT in a hurry. ‘No, I’ll look first,’ she said, ‘and +see whether it’s marked “poison” or not’; for she had read several nice +little histories about children who had got burnt, and eaten up by wild +beasts and other unpleasant things, all because they WOULD not remember +the simple rules their friends had taught them: such as, that a red-hot +poker will burn you if you hold it too long; and that if you cut your +finger VERY deeply with a knife, it usually bleeds; and she had never +forgotten that, if you drink much from a bottle marked ‘poison,’ it is +almost certain to disagree with you, sooner or later. + +However, this bottle was NOT marked ‘poison,’ so Alice ventured to taste +it, and finding it very nice, (it had, in fact, a sort of mixed flavour +of cherry-tart, custard, pine-apple, roast turkey, toffee, and hot +buttered toast,) she very soon finished it off. + + * * * * * * * + + * * * * * * + + * * * * * * * + +‘What a curious feeling!’ said Alice; ‘I must be shutting up like a +telescope.’ + +And so it was indeed: she was now only ten inches high, and her face +brightened up at the thought that she was now the right size for going +through the little door into that lovely garden. First, however, she +waited for a few minutes to see if she was going to shrink any further: +she felt a little nervous about this; ‘for it might end, you know,’ said +Alice to herself, ‘in my going out altogether, like a candle. I wonder +what I should be like then?’ And she tried to fancy what the flame of a +candle is like after the candle is blown out, for she could not remember +ever having seen such a thing. + +After a while, finding that nothing more happened, she decided on going +into the garden at once; but, alas for poor Alice! when she got to the +door, she found she had forgotten the little golden key, and when she +went back to the table for it, she found she could not possibly reach +it: she could see it quite plainly through the glass, and she tried her +best to climb up one of the legs of the table, but it was too slippery; +and when she had tired herself out with trying, the poor little thing +sat down and cried. + +‘Come, there’s no use in crying like that!’ said Alice to herself, +rather sharply; ‘I advise you to leave off this minute!’ She generally +gave herself very good advice, (though she very seldom followed it), +and sometimes she scolded herself so severely as to bring tears into +her eyes; and once she remembered trying to box her own ears for having +cheated herself in a game of croquet she was playing against herself, +for this curious child was very fond of pretending to be two people. +‘But it’s no use now,’ thought poor Alice, ‘to pretend to be two people! +Why, there’s hardly enough of me left to make ONE respectable person!’ + +Soon her eye fell on a little glass box that was lying under the table: +she opened it, and found in it a very small cake, on which the words +‘EAT ME’ were beautifully marked in currants. ‘Well, I’ll eat it,’ said +Alice, ‘and if it makes me grow larger, I can reach the key; and if it +makes me grow smaller, I can creep under the door; so either way I’ll +get into the garden, and I don’t care which happens!’ + +She ate a little bit, and said anxiously to herself, ‘Which way? Which +way?’, holding her hand on the top of her head to feel which way it was +growing, and she was quite surprised to find that she remained the same +size: to be sure, this generally happens when one eats cake, but Alice +had got so much into the way of expecting nothing but out-of-the-way +things to happen, that it seemed quite dull and stupid for life to go on +in the common way. + +So she set to work, and very soon finished off the cake. + + * * * * * * * + + * * * * * * + + * * * * * * * + + + + +CHAPTER II. The Pool of Tears + +‘Curiouser and curiouser!’ cried Alice (she was so much surprised, that +for the moment she quite forgot how to speak good English); ‘now I’m +opening out like the largest telescope that ever was! Good-bye, feet!’ +(for when she looked down at her feet, they seemed to be almost out of +sight, they were getting so far off). ‘Oh, my poor little feet, I wonder +who will put on your shoes and stockings for you now, dears? I’m sure +_I_ shan’t be able! I shall be a great deal too far off to trouble +myself about you: you must manage the best way you can;--but I must be +kind to them,’ thought Alice, ‘or perhaps they won’t walk the way I want +to go! Let me see: I’ll give them a new pair of boots every Christmas.’ + +And she went on planning to herself how she would manage it. ‘They must +go by the carrier,’ she thought; ‘and how funny it’ll seem, sending +presents to one’s own feet! And how odd the directions will look! + + ALICE’S RIGHT FOOT, ESQ. + HEARTHRUG, + NEAR THE FENDER, + (WITH ALICE’S LOVE). + +Oh dear, what nonsense I’m talking!’ + +Just then her head struck against the roof of the hall: in fact she was +now more than nine feet high, and she at once took up the little golden +key and hurried off to the garden door. + +Poor Alice! It was as much as she could do, lying down on one side, to +look through into the garden with one eye; but to get through was more +hopeless than ever: she sat down and began to cry again. + +‘You ought to be ashamed of yourself,’ said Alice, ‘a great girl like +you,’ (she might well say this), ‘to go on crying in this way! Stop this +moment, I tell you!’ But she went on all the same, shedding gallons of +tears, until there was a large pool all round her, about four inches +deep and reaching half down the hall. + +After a time she heard a little pattering of feet in the distance, and +she hastily dried her eyes to see what was coming. It was the White +Rabbit returning, splendidly dressed, with a pair of white kid gloves in +one hand and a large fan in the other: he came trotting along in a great +hurry, muttering to himself as he came, ‘Oh! the Duchess, the Duchess! +Oh! won’t she be savage if I’ve kept her waiting!’ Alice felt so +desperate that she was ready to ask help of any one; so, when the Rabbit +came near her, she began, in a low, timid voice, ‘If you please, sir--’ +The Rabbit started violently, dropped the white kid gloves and the fan, +and skurried away into the darkness as hard as he could go. + +Alice took up the fan and gloves, and, as the hall was very hot, she +kept fanning herself all the time she went on talking: ‘Dear, dear! How +queer everything is to-day! And yesterday things went on just as usual. +I wonder if I’ve been changed in the night? Let me think: was I the +same when I got up this morning? I almost think I can remember feeling a +little different. But if I’m not the same, the next question is, Who +in the world am I? Ah, THAT’S the great puzzle!’ And she began thinking +over all the children she knew that were of the same age as herself, to +see if she could have been changed for any of them. + +‘I’m sure I’m not Ada,’ she said, ‘for her hair goes in such long +ringlets, and mine doesn’t go in ringlets at all; and I’m sure I can’t +be Mabel, for I know all sorts of things, and she, oh! she knows such a +very little! Besides, SHE’S she, and I’m I, and--oh dear, how puzzling +it all is! I’ll try if I know all the things I used to know. Let me +see: four times five is twelve, and four times six is thirteen, and +four times seven is--oh dear! I shall never get to twenty at that rate! +However, the Multiplication Table doesn’t signify: let’s try Geography. +London is the capital of Paris, and Paris is the capital of Rome, and +Rome--no, THAT’S all wrong, I’m certain! I must have been changed for +Mabel! I’ll try and say “How doth the little--“’ and she crossed her +hands on her lap as if she were saying lessons, and began to repeat it, +but her voice sounded hoarse and strange, and the words did not come the +same as they used to do:-- + + ‘How doth the little crocodile + Improve his shining tail, + And pour the waters of the Nile + On every golden scale! + + ‘How cheerfully he seems to grin, + How neatly spread his claws, + And welcome little fishes in + With gently smiling jaws!’ + +‘I’m sure those are not the right words,’ said poor Alice, and her eyes +filled with tears again as she went on, ‘I must be Mabel after all, and +I shall have to go and live in that poky little house, and have next to +no toys to play with, and oh! ever so many lessons to learn! No, I’ve +made up my mind about it; if I’m Mabel, I’ll stay down here! It’ll be no +use their putting their heads down and saying “Come up again, dear!” I +shall only look up and say “Who am I then? Tell me that first, and then, +if I like being that person, I’ll come up: if not, I’ll stay down here +till I’m somebody else”--but, oh dear!’ cried Alice, with a sudden burst +of tears, ‘I do wish they WOULD put their heads down! I am so VERY tired +of being all alone here!’ + +As she said this she looked down at her hands, and was surprised to see +that she had put on one of the Rabbit’s little white kid gloves while +she was talking. ‘How CAN I have done that?’ she thought. ‘I must +be growing small again.’ She got up and went to the table to measure +herself by it, and found that, as nearly as she could guess, she was now +about two feet high, and was going on shrinking rapidly: she soon found +out that the cause of this was the fan she was holding, and she dropped +it hastily, just in time to avoid shrinking away altogether. + +‘That WAS a narrow escape!’ said Alice, a good deal frightened at the +sudden change, but very glad to find herself still in existence; ‘and +now for the garden!’ and she ran with all speed back to the little door: +but, alas! the little door was shut again, and the little golden key was +lying on the glass table as before, ‘and things are worse than ever,’ +thought the poor child, ‘for I never was so small as this before, never! +And I declare it’s too bad, that it is!’ + +As she said these words her foot slipped, and in another moment, splash! +she was up to her chin in salt water. Her first idea was that she +had somehow fallen into the sea, ‘and in that case I can go back by +railway,’ she said to herself. (Alice had been to the seaside once in +her life, and had come to the general conclusion, that wherever you go +to on the English coast you find a number of bathing machines in the +sea, some children digging in the sand with wooden spades, then a row +of lodging houses, and behind them a railway station.) However, she soon +made out that she was in the pool of tears which she had wept when she +was nine feet high. + +‘I wish I hadn’t cried so much!’ said Alice, as she swam about, trying +to find her way out. ‘I shall be punished for it now, I suppose, by +being drowned in my own tears! That WILL be a queer thing, to be sure! +However, everything is queer to-day.’ + +Just then she heard something splashing about in the pool a little way +off, and she swam nearer to make out what it was: at first she thought +it must be a walrus or hippopotamus, but then she remembered how small +she was now, and she soon made out that it was only a mouse that had +slipped in like herself. + +‘Would it be of any use, now,’ thought Alice, ‘to speak to this mouse? +Everything is so out-of-the-way down here, that I should think very +likely it can talk: at any rate, there’s no harm in trying.’ So she +began: ‘O Mouse, do you know the way out of this pool? I am very tired +of swimming about here, O Mouse!’ (Alice thought this must be the right +way of speaking to a mouse: she had never done such a thing before, but +she remembered having seen in her brother’s Latin Grammar, ‘A mouse--of +a mouse--to a mouse--a mouse--O mouse!’) The Mouse looked at her rather +inquisitively, and seemed to her to wink with one of its little eyes, +but it said nothing. + +‘Perhaps it doesn’t understand English,’ thought Alice; ‘I daresay it’s +a French mouse, come over with William the Conqueror.’ (For, with all +her knowledge of history, Alice had no very clear notion how long ago +anything had happened.) So she began again: ‘Ou est ma chatte?’ which +was the first sentence in her French lesson-book. The Mouse gave a +sudden leap out of the water, and seemed to quiver all over with fright. +‘Oh, I beg your pardon!’ cried Alice hastily, afraid that she had hurt +the poor animal’s feelings. ‘I quite forgot you didn’t like cats.’ + +‘Not like cats!’ cried the Mouse, in a shrill, passionate voice. ‘Would +YOU like cats if you were me?’ + +‘Well, perhaps not,’ said Alice in a soothing tone: ‘don’t be angry +about it. And yet I wish I could show you our cat Dinah: I think you’d +take a fancy to cats if you could only see her. She is such a dear quiet +thing,’ Alice went on, half to herself, as she swam lazily about in the +pool, ‘and she sits purring so nicely by the fire, licking her paws and +washing her face--and she is such a nice soft thing to nurse--and she’s +such a capital one for catching mice--oh, I beg your pardon!’ cried +Alice again, for this time the Mouse was bristling all over, and she +felt certain it must be really offended. ‘We won’t talk about her any +more if you’d rather not.’ + +‘We indeed!’ cried the Mouse, who was trembling down to the end of his +tail. ‘As if I would talk on such a subject! Our family always HATED +cats: nasty, low, vulgar things! Don’t let me hear the name again!’ + +‘I won’t indeed!’ said Alice, in a great hurry to change the subject of +conversation. ‘Are you--are you fond--of--of dogs?’ The Mouse did not +answer, so Alice went on eagerly: ‘There is such a nice little dog near +our house I should like to show you! A little bright-eyed terrier, you +know, with oh, such long curly brown hair! And it’ll fetch things when +you throw them, and it’ll sit up and beg for its dinner, and all sorts +of things--I can’t remember half of them--and it belongs to a farmer, +you know, and he says it’s so useful, it’s worth a hundred pounds! He +says it kills all the rats and--oh dear!’ cried Alice in a sorrowful +tone, ‘I’m afraid I’ve offended it again!’ For the Mouse was swimming +away from her as hard as it could go, and making quite a commotion in +the pool as it went. + +So she called softly after it, ‘Mouse dear! Do come back again, and we +won’t talk about cats or dogs either, if you don’t like them!’ When the +Mouse heard this, it turned round and swam slowly back to her: its +face was quite pale (with passion, Alice thought), and it said in a low +trembling voice, ‘Let us get to the shore, and then I’ll tell you my +history, and you’ll understand why it is I hate cats and dogs.’ + +It was high time to go, for the pool was getting quite crowded with the +birds and animals that had fallen into it: there were a Duck and a Dodo, +a Lory and an Eaglet, and several other curious creatures. Alice led the +way, and the whole party swam to the shore. + + + + +CHAPTER III. A Caucus-Race and a Long Tale + +They were indeed a queer-looking party that assembled on the bank--the +birds with draggled feathers, the animals with their fur clinging close +to them, and all dripping wet, cross, and uncomfortable. + +The first question of course was, how to get dry again: they had a +consultation about this, and after a few minutes it seemed quite natural +to Alice to find herself talking familiarly with them, as if she had +known them all her life. Indeed, she had quite a long argument with the +Lory, who at last turned sulky, and would only say, ‘I am older than +you, and must know better’; and this Alice would not allow without +knowing how old it was, and, as the Lory positively refused to tell its +age, there was no more to be said. + +At last the Mouse, who seemed to be a person of authority among them, +called out, ‘Sit down, all of you, and listen to me! I’LL soon make you +dry enough!’ They all sat down at once, in a large ring, with the Mouse +in the middle. Alice kept her eyes anxiously fixed on it, for she felt +sure she would catch a bad cold if she did not get dry very soon. + +‘Ahem!’ said the Mouse with an important air, ‘are you all ready? This +is the driest thing I know. Silence all round, if you please! “William +the Conqueror, whose cause was favoured by the pope, was soon submitted +to by the English, who wanted leaders, and had been of late much +accustomed to usurpation and conquest. Edwin and Morcar, the earls of +Mercia and Northumbria--“’ + +‘Ugh!’ said the Lory, with a shiver. + +‘I beg your pardon!’ said the Mouse, frowning, but very politely: ‘Did +you speak?’ + +‘Not I!’ said the Lory hastily. + +‘I thought you did,’ said the Mouse. ‘--I proceed. “Edwin and Morcar, +the earls of Mercia and Northumbria, declared for him: and even Stigand, +the patriotic archbishop of Canterbury, found it advisable--“’ + +‘Found WHAT?’ said the Duck. + +‘Found IT,’ the Mouse replied rather crossly: ‘of course you know what +“it” means.’ + +‘I know what “it” means well enough, when I find a thing,’ said the +Duck: ‘it’s generally a frog or a worm. The question is, what did the +archbishop find?’ + +The Mouse did not notice this question, but hurriedly went on, ‘“--found +it advisable to go with Edgar Atheling to meet William and offer him the +crown. William’s conduct at first was moderate. But the insolence of his +Normans--” How are you getting on now, my dear?’ it continued, turning +to Alice as it spoke. + +‘As wet as ever,’ said Alice in a melancholy tone: ‘it doesn’t seem to +dry me at all.’ + +‘In that case,’ said the Dodo solemnly, rising to its feet, ‘I move +that the meeting adjourn, for the immediate adoption of more energetic +remedies--’ + +‘Speak English!’ said the Eaglet. ‘I don’t know the meaning of half +those long words, and, what’s more, I don’t believe you do either!’ And +the Eaglet bent down its head to hide a smile: some of the other birds +tittered audibly. + +‘What I was going to say,’ said the Dodo in an offended tone, ‘was, that +the best thing to get us dry would be a Caucus-race.’ + +‘What IS a Caucus-race?’ said Alice; not that she wanted much to know, +but the Dodo had paused as if it thought that SOMEBODY ought to speak, +and no one else seemed inclined to say anything. + +‘Why,’ said the Dodo, ‘the best way to explain it is to do it.’ (And, as +you might like to try the thing yourself, some winter day, I will tell +you how the Dodo managed it.) + +First it marked out a race-course, in a sort of circle, [‘the exact +shape doesn’t matter,’ it said,) and then all the party were placed +along the course, here and there. There was no ‘One, two, three, and +away,’ but they began running when they liked, and left off when they +liked, so that it was not easy to know when the race was over. However, +when they had been running half an hour or so, and were quite dry again, +the Dodo suddenly called out ‘The race is over!’ and they all crowded +round it, panting, and asking, ‘But who has won?’ + +This question the Dodo could not answer without a great deal of thought, +and it sat for a long time with one finger pressed upon its forehead +(the position in which you usually see Shakespeare, in the pictures +of him), while the rest waited in silence. At last the Dodo said, +‘EVERYBODY has won, and all must have prizes.’ + +‘But who is to give the prizes?’ quite a chorus of voices asked. + +‘Why, SHE, of course,’ said the Dodo, pointing to Alice with one finger; +and the whole party at once crowded round her, calling out in a confused +way, ‘Prizes! Prizes!’ + +Alice had no idea what to do, and in despair she put her hand in her +pocket, and pulled out a box of comfits, (luckily the salt water had +not got into it), and handed them round as prizes. There was exactly one +a-piece all round. + +‘But she must have a prize herself, you know,’ said the Mouse. + +‘Of course,’ the Dodo replied very gravely. ‘What else have you got in +your pocket?’ he went on, turning to Alice. + +‘Only a thimble,’ said Alice sadly. + +‘Hand it over here,’ said the Dodo. + +Then they all crowded round her once more, while the Dodo solemnly +presented the thimble, saying ‘We beg your acceptance of this elegant +thimble’; and, when it had finished this short speech, they all cheered. + +Alice thought the whole thing very absurd, but they all looked so grave +that she did not dare to laugh; and, as she could not think of anything +to say, she simply bowed, and took the thimble, looking as solemn as she +could. + +The next thing was to eat the comfits: this caused some noise and +confusion, as the large birds complained that they could not taste +theirs, and the small ones choked and had to be patted on the back. +However, it was over at last, and they sat down again in a ring, and +begged the Mouse to tell them something more. + +‘You promised to tell me your history, you know,’ said Alice, ‘and why +it is you hate--C and D,’ she added in a whisper, half afraid that it +would be offended again. + +‘Mine is a long and a sad tale!’ said the Mouse, turning to Alice, and +sighing. + +‘It IS a long tail, certainly,’ said Alice, looking down with wonder at +the Mouse’s tail; ‘but why do you call it sad?’ And she kept on puzzling +about it while the Mouse was speaking, so that her idea of the tale was +something like this:-- + + ‘Fury said to a + mouse, That he + met in the + house, + “Let us + both go to + law: I will + prosecute + YOU.--Come, + I’ll take no + denial; We + must have a + trial: For + really this + morning I’ve + nothing + to do.” + Said the + mouse to the + cur, “Such + a trial, + dear Sir, + With + no jury + or judge, + would be + wasting + our + breath.” + “I’ll be + judge, I’ll + be jury,” + Said + cunning + old Fury: + “I’ll + try the + whole + cause, + and + condemn + you + to + death.”’ + + +‘You are not attending!’ said the Mouse to Alice severely. ‘What are you +thinking of?’ + +‘I beg your pardon,’ said Alice very humbly: ‘you had got to the fifth +bend, I think?’ + +‘I had NOT!’ cried the Mouse, sharply and very angrily. + +‘A knot!’ said Alice, always ready to make herself useful, and looking +anxiously about her. ‘Oh, do let me help to undo it!’ + +‘I shall do nothing of the sort,’ said the Mouse, getting up and walking +away. ‘You insult me by talking such nonsense!’ + +‘I didn’t mean it!’ pleaded poor Alice. ‘But you’re so easily offended, +you know!’ + +The Mouse only growled in reply. + +‘Please come back and finish your story!’ Alice called after it; and the +others all joined in chorus, ‘Yes, please do!’ but the Mouse only shook +its head impatiently, and walked a little quicker. + +‘What a pity it wouldn’t stay!’ sighed the Lory, as soon as it was quite +out of sight; and an old Crab took the opportunity of saying to her +daughter ‘Ah, my dear! Let this be a lesson to you never to lose +YOUR temper!’ ‘Hold your tongue, Ma!’ said the young Crab, a little +snappishly. ‘You’re enough to try the patience of an oyster!’ + +‘I wish I had our Dinah here, I know I do!’ said Alice aloud, addressing +nobody in particular. ‘She’d soon fetch it back!’ + +‘And who is Dinah, if I might venture to ask the question?’ said the +Lory. + +Alice replied eagerly, for she was always ready to talk about her pet: +‘Dinah’s our cat. And she’s such a capital one for catching mice you +can’t think! And oh, I wish you could see her after the birds! Why, +she’ll eat a little bird as soon as look at it!’ + +This speech caused a remarkable sensation among the party. Some of the +birds hurried off at once: one old Magpie began wrapping itself up very +carefully, remarking, ‘I really must be getting home; the night-air +doesn’t suit my throat!’ and a Canary called out in a trembling voice to +its children, ‘Come away, my dears! It’s high time you were all in bed!’ +On various pretexts they all moved off, and Alice was soon left alone. + +‘I wish I hadn’t mentioned Dinah!’ she said to herself in a melancholy +tone. ‘Nobody seems to like her, down here, and I’m sure she’s the best +cat in the world! Oh, my dear Dinah! I wonder if I shall ever see you +any more!’ And here poor Alice began to cry again, for she felt very +lonely and low-spirited. In a little while, however, she again heard +a little pattering of footsteps in the distance, and she looked up +eagerly, half hoping that the Mouse had changed his mind, and was coming +back to finish his story. + + + + +CHAPTER IV. The Rabbit Sends in a Little Bill + +It was the White Rabbit, trotting slowly back again, and looking +anxiously about as it went, as if it had lost something; and she heard +it muttering to itself ‘The Duchess! The Duchess! Oh my dear paws! Oh +my fur and whiskers! She’ll get me executed, as sure as ferrets are +ferrets! Where CAN I have dropped them, I wonder?’ Alice guessed in a +moment that it was looking for the fan and the pair of white kid gloves, +and she very good-naturedly began hunting about for them, but they were +nowhere to be seen--everything seemed to have changed since her swim in +the pool, and the great hall, with the glass table and the little door, +had vanished completely. + +Very soon the Rabbit noticed Alice, as she went hunting about, and +called out to her in an angry tone, ‘Why, Mary Ann, what ARE you doing +out here? Run home this moment, and fetch me a pair of gloves and a fan! +Quick, now!’ And Alice was so much frightened that she ran off at once +in the direction it pointed to, without trying to explain the mistake it +had made. + +‘He took me for his housemaid,’ she said to herself as she ran. ‘How +surprised he’ll be when he finds out who I am! But I’d better take him +his fan and gloves--that is, if I can find them.’ As she said this, she +came upon a neat little house, on the door of which was a bright brass +plate with the name ‘W. RABBIT’ engraved upon it. She went in without +knocking, and hurried upstairs, in great fear lest she should meet the +real Mary Ann, and be turned out of the house before she had found the +fan and gloves. + +‘How queer it seems,’ Alice said to herself, ‘to be going messages for +a rabbit! I suppose Dinah’ll be sending me on messages next!’ And she +began fancying the sort of thing that would happen: ‘“Miss Alice! Come +here directly, and get ready for your walk!” “Coming in a minute, +nurse! But I’ve got to see that the mouse doesn’t get out.” Only I don’t +think,’ Alice went on, ‘that they’d let Dinah stop in the house if it +began ordering people about like that!’ + +By this time she had found her way into a tidy little room with a table +in the window, and on it (as she had hoped) a fan and two or three pairs +of tiny white kid gloves: she took up the fan and a pair of the gloves, +and was just going to leave the room, when her eye fell upon a little +bottle that stood near the looking-glass. There was no label this time +with the words ‘DRINK ME,’ but nevertheless she uncorked it and put it +to her lips. ‘I know SOMETHING interesting is sure to happen,’ she said +to herself, ‘whenever I eat or drink anything; so I’ll just see what +this bottle does. I do hope it’ll make me grow large again, for really +I’m quite tired of being such a tiny little thing!’ + +It did so indeed, and much sooner than she had expected: before she had +drunk half the bottle, she found her head pressing against the ceiling, +and had to stoop to save her neck from being broken. She hastily put +down the bottle, saying to herself ‘That’s quite enough--I hope I shan’t +grow any more--As it is, I can’t get out at the door--I do wish I hadn’t +drunk quite so much!’ + +Alas! it was too late to wish that! She went on growing, and growing, +and very soon had to kneel down on the floor: in another minute there +was not even room for this, and she tried the effect of lying down with +one elbow against the door, and the other arm curled round her head. +Still she went on growing, and, as a last resource, she put one arm out +of the window, and one foot up the chimney, and said to herself ‘Now I +can do no more, whatever happens. What WILL become of me?’ + +Luckily for Alice, the little magic bottle had now had its full effect, +and she grew no larger: still it was very uncomfortable, and, as there +seemed to be no sort of chance of her ever getting out of the room +again, no wonder she felt unhappy. + +‘It was much pleasanter at home,’ thought poor Alice, ‘when one wasn’t +always growing larger and smaller, and being ordered about by mice and +rabbits. I almost wish I hadn’t gone down that rabbit-hole--and yet--and +yet--it’s rather curious, you know, this sort of life! I do wonder what +CAN have happened to me! When I used to read fairy-tales, I fancied that +kind of thing never happened, and now here I am in the middle of one! +There ought to be a book written about me, that there ought! And when I +grow up, I’ll write one--but I’m grown up now,’ she added in a sorrowful +tone; ‘at least there’s no room to grow up any more HERE.’ + +‘But then,’ thought Alice, ‘shall I NEVER get any older than I am +now? That’ll be a comfort, one way--never to be an old woman--but +then--always to have lessons to learn! Oh, I shouldn’t like THAT!’ + +‘Oh, you foolish Alice!’ she answered herself. ‘How can you learn +lessons in here? Why, there’s hardly room for YOU, and no room at all +for any lesson-books!’ + +And so she went on, taking first one side and then the other, and making +quite a conversation of it altogether; but after a few minutes she heard +a voice outside, and stopped to listen. + +‘Mary Ann! Mary Ann!’ said the voice. ‘Fetch me my gloves this moment!’ +Then came a little pattering of feet on the stairs. Alice knew it was +the Rabbit coming to look for her, and she trembled till she shook the +house, quite forgetting that she was now about a thousand times as large +as the Rabbit, and had no reason to be afraid of it. + +Presently the Rabbit came up to the door, and tried to open it; but, as +the door opened inwards, and Alice’s elbow was pressed hard against it, +that attempt proved a failure. Alice heard it say to itself ‘Then I’ll +go round and get in at the window.’ + +‘THAT you won’t’ thought Alice, and, after waiting till she fancied +she heard the Rabbit just under the window, she suddenly spread out her +hand, and made a snatch in the air. She did not get hold of anything, +but she heard a little shriek and a fall, and a crash of broken glass, +from which she concluded that it was just possible it had fallen into a +cucumber-frame, or something of the sort. + +Next came an angry voice--the Rabbit’s--‘Pat! Pat! Where are you?’ And +then a voice she had never heard before, ‘Sure then I’m here! Digging +for apples, yer honour!’ + +‘Digging for apples, indeed!’ said the Rabbit angrily. ‘Here! Come and +help me out of THIS!’ (Sounds of more broken glass.) + +‘Now tell me, Pat, what’s that in the window?’ + +‘Sure, it’s an arm, yer honour!’ (He pronounced it ‘arrum.’) + +‘An arm, you goose! Who ever saw one that size? Why, it fills the whole +window!’ + +‘Sure, it does, yer honour: but it’s an arm for all that.’ + +‘Well, it’s got no business there, at any rate: go and take it away!’ + +There was a long silence after this, and Alice could only hear whispers +now and then; such as, ‘Sure, I don’t like it, yer honour, at all, at +all!’ ‘Do as I tell you, you coward!’ and at last she spread out her +hand again, and made another snatch in the air. This time there were +TWO little shrieks, and more sounds of broken glass. ‘What a number of +cucumber-frames there must be!’ thought Alice. ‘I wonder what they’ll do +next! As for pulling me out of the window, I only wish they COULD! I’m +sure I don’t want to stay in here any longer!’ + +She waited for some time without hearing anything more: at last came a +rumbling of little cartwheels, and the sound of a good many voices +all talking together: she made out the words: ‘Where’s the other +ladder?--Why, I hadn’t to bring but one; Bill’s got the other--Bill! +fetch it here, lad!--Here, put ‘em up at this corner--No, tie ‘em +together first--they don’t reach half high enough yet--Oh! they’ll +do well enough; don’t be particular--Here, Bill! catch hold of this +rope--Will the roof bear?--Mind that loose slate--Oh, it’s coming +down! Heads below!’ (a loud crash)--‘Now, who did that?--It was Bill, I +fancy--Who’s to go down the chimney?--Nay, I shan’t! YOU do it!--That I +won’t, then!--Bill’s to go down--Here, Bill! the master says you’re to +go down the chimney!’ + +‘Oh! So Bill’s got to come down the chimney, has he?’ said Alice to +herself. ‘Shy, they seem to put everything upon Bill! I wouldn’t be in +Bill’s place for a good deal: this fireplace is narrow, to be sure; but +I THINK I can kick a little!’ + +She drew her foot as far down the chimney as she could, and waited +till she heard a little animal (she couldn’t guess of what sort it was) +scratching and scrambling about in the chimney close above her: then, +saying to herself ‘This is Bill,’ she gave one sharp kick, and waited to +see what would happen next. + +The first thing she heard was a general chorus of ‘There goes Bill!’ +then the Rabbit’s voice along--‘Catch him, you by the hedge!’ then +silence, and then another confusion of voices--‘Hold up his head--Brandy +now--Don’t choke him--How was it, old fellow? What happened to you? Tell +us all about it!’ + +Last came a little feeble, squeaking voice, [‘That’s Bill,’ thought +Alice,) ‘Well, I hardly know--No more, thank ye; I’m better now--but I’m +a deal too flustered to tell you--all I know is, something comes at me +like a Jack-in-the-box, and up I goes like a sky-rocket!’ + +‘So you did, old fellow!’ said the others. + +‘We must burn the house down!’ said the Rabbit’s voice; and Alice called +out as loud as she could, ‘If you do. I’ll set Dinah at you!’ + +There was a dead silence instantly, and Alice thought to herself, ‘I +wonder what they WILL do next! If they had any sense, they’d take the +roof off.’ After a minute or two, they began moving about again, and +Alice heard the Rabbit say, ‘A barrowful will do, to begin with.’ + +‘A barrowful of WHAT?’ thought Alice; but she had not long to doubt, +for the next moment a shower of little pebbles came rattling in at the +window, and some of them hit her in the face. ‘I’ll put a stop to this,’ +she said to herself, and shouted out, ‘You’d better not do that again!’ +which produced another dead silence. + +Alice noticed with some surprise that the pebbles were all turning into +little cakes as they lay on the floor, and a bright idea came into her +head. ‘If I eat one of these cakes,’ she thought, ‘it’s sure to make +SOME change in my size; and as it can’t possibly make me larger, it must +make me smaller, I suppose.’ + +So she swallowed one of the cakes, and was delighted to find that she +began shrinking directly. As soon as she was small enough to get through +the door, she ran out of the house, and found quite a crowd of little +animals and birds waiting outside. The poor little Lizard, Bill, was +in the middle, being held up by two guinea-pigs, who were giving it +something out of a bottle. They all made a rush at Alice the moment she +appeared; but she ran off as hard as she could, and soon found herself +safe in a thick wood. + +‘The first thing I’ve got to do,’ said Alice to herself, as she wandered +about in the wood, ‘is to grow to my right size again; and the second +thing is to find my way into that lovely garden. I think that will be +the best plan.’ + +It sounded an excellent plan, no doubt, and very neatly and simply +arranged; the only difficulty was, that she had not the smallest idea +how to set about it; and while she was peering about anxiously among +the trees, a little sharp bark just over her head made her look up in a +great hurry. + +An enormous puppy was looking down at her with large round eyes, and +feebly stretching out one paw, trying to touch her. ‘Poor little thing!’ +said Alice, in a coaxing tone, and she tried hard to whistle to it; but +she was terribly frightened all the time at the thought that it might be +hungry, in which case it would be very likely to eat her up in spite of +all her coaxing. + +Hardly knowing what she did, she picked up a little bit of stick, and +held it out to the puppy; whereupon the puppy jumped into the air off +all its feet at once, with a yelp of delight, and rushed at the stick, +and made believe to worry it; then Alice dodged behind a great thistle, +to keep herself from being run over; and the moment she appeared on the +other side, the puppy made another rush at the stick, and tumbled head +over heels in its hurry to get hold of it; then Alice, thinking it was +very like having a game of play with a cart-horse, and expecting every +moment to be trampled under its feet, ran round the thistle again; then +the puppy began a series of short charges at the stick, running a very +little way forwards each time and a long way back, and barking hoarsely +all the while, till at last it sat down a good way off, panting, with +its tongue hanging out of its mouth, and its great eyes half shut. + +This seemed to Alice a good opportunity for making her escape; so she +set off at once, and ran till she was quite tired and out of breath, and +till the puppy’s bark sounded quite faint in the distance. + +‘And yet what a dear little puppy it was!’ said Alice, as she leant +against a buttercup to rest herself, and fanned herself with one of the +leaves: ‘I should have liked teaching it tricks very much, if--if I’d +only been the right size to do it! Oh dear! I’d nearly forgotten that +I’ve got to grow up again! Let me see--how IS it to be managed? I +suppose I ought to eat or drink something or other; but the great +question is, what?’ + +The great question certainly was, what? Alice looked all round her at +the flowers and the blades of grass, but she did not see anything that +looked like the right thing to eat or drink under the circumstances. +There was a large mushroom growing near her, about the same height as +herself; and when she had looked under it, and on both sides of it, and +behind it, it occurred to her that she might as well look and see what +was on the top of it. + +She stretched herself up on tiptoe, and peeped over the edge of the +mushroom, and her eyes immediately met those of a large caterpillar, +that was sitting on the top with its arms folded, quietly smoking a long +hookah, and taking not the smallest notice of her or of anything else. + + + + +CHAPTER V. Advice from a Caterpillar + +The Caterpillar and Alice looked at each other for some time in silence: +at last the Caterpillar took the hookah out of its mouth, and addressed +her in a languid, sleepy voice. + +‘Who are YOU?’ said the Caterpillar. + +This was not an encouraging opening for a conversation. Alice replied, +rather shyly, ‘I--I hardly know, sir, just at present--at least I know +who I WAS when I got up this morning, but I think I must have been +changed several times since then.’ + +‘What do you mean by that?’ said the Caterpillar sternly. ‘Explain +yourself!’ + +‘I can’t explain MYSELF, I’m afraid, sir’ said Alice, ‘because I’m not +myself, you see.’ + +‘I don’t see,’ said the Caterpillar. + +‘I’m afraid I can’t put it more clearly,’ Alice replied very politely, +‘for I can’t understand it myself to begin with; and being so many +different sizes in a day is very confusing.’ + +‘It isn’t,’ said the Caterpillar. + +‘Well, perhaps you haven’t found it so yet,’ said Alice; ‘but when you +have to turn into a chrysalis--you will some day, you know--and then +after that into a butterfly, I should think you’ll feel it a little +queer, won’t you?’ + +‘Not a bit,’ said the Caterpillar. + +‘Well, perhaps your feelings may be different,’ said Alice; ‘all I know +is, it would feel very queer to ME.’ + +‘You!’ said the Caterpillar contemptuously. ‘Who are YOU?’ + +Which brought them back again to the beginning of the conversation. +Alice felt a little irritated at the Caterpillar’s making such VERY +short remarks, and she drew herself up and said, very gravely, ‘I think, +you ought to tell me who YOU are, first.’ + +‘Why?’ said the Caterpillar. + +Here was another puzzling question; and as Alice could not think of any +good reason, and as the Caterpillar seemed to be in a VERY unpleasant +state of mind, she turned away. + +‘Come back!’ the Caterpillar called after her. ‘I’ve something important +to say!’ + +This sounded promising, certainly: Alice turned and came back again. + +‘Keep your temper,’ said the Caterpillar. + +‘Is that all?’ said Alice, swallowing down her anger as well as she +could. + +‘No,’ said the Caterpillar. + +Alice thought she might as well wait, as she had nothing else to do, and +perhaps after all it might tell her something worth hearing. For some +minutes it puffed away without speaking, but at last it unfolded its +arms, took the hookah out of its mouth again, and said, ‘So you think +you’re changed, do you?’ + +‘I’m afraid I am, sir,’ said Alice; ‘I can’t remember things as I +used--and I don’t keep the same size for ten minutes together!’ + +‘Can’t remember WHAT things?’ said the Caterpillar. + +‘Well, I’ve tried to say “HOW DOTH THE LITTLE BUSY BEE,” but it all came +different!’ Alice replied in a very melancholy voice. + +‘Repeat, “YOU ARE OLD, FATHER WILLIAM,”’ said the Caterpillar. + +Alice folded her hands, and began:-- + + ‘You are old, Father William,’ the young man said, + ‘And your hair has become very white; + And yet you incessantly stand on your head-- + Do you think, at your age, it is right?’ + + ‘In my youth,’ Father William replied to his son, + ‘I feared it might injure the brain; + But, now that I’m perfectly sure I have none, + Why, I do it again and again.’ + + ‘You are old,’ said the youth, ‘as I mentioned before, + And have grown most uncommonly fat; + Yet you turned a back-somersault in at the door-- + Pray, what is the reason of that?’ + + ‘In my youth,’ said the sage, as he shook his grey locks, + ‘I kept all my limbs very supple + By the use of this ointment--one shilling the box-- + Allow me to sell you a couple?’ + + ‘You are old,’ said the youth, ‘and your jaws are too weak + For anything tougher than suet; + Yet you finished the goose, with the bones and the beak-- + Pray how did you manage to do it?’ + + ‘In my youth,’ said his father, ‘I took to the law, + And argued each case with my wife; + And the muscular strength, which it gave to my jaw, + Has lasted the rest of my life.’ + + ‘You are old,’ said the youth, ‘one would hardly suppose + That your eye was as steady as ever; + Yet you balanced an eel on the end of your nose-- + What made you so awfully clever?’ + + ‘I have answered three questions, and that is enough,’ + Said his father; ‘don’t give yourself airs! + Do you think I can listen all day to such stuff? + Be off, or I’ll kick you down stairs!’ + + +‘That is not said right,’ said the Caterpillar. + +‘Not QUITE right, I’m afraid,’ said Alice, timidly; ‘some of the words +have got altered.’ + +‘It is wrong from beginning to end,’ said the Caterpillar decidedly, and +there was silence for some minutes. + +The Caterpillar was the first to speak. + +‘What size do you want to be?’ it asked. + +‘Oh, I’m not particular as to size,’ Alice hastily replied; ‘only one +doesn’t like changing so often, you know.’ + +‘I DON’T know,’ said the Caterpillar. + +Alice said nothing: she had never been so much contradicted in her life +before, and she felt that she was losing her temper. + +‘Are you content now?’ said the Caterpillar. + +‘Well, I should like to be a LITTLE larger, sir, if you wouldn’t mind,’ +said Alice: ‘three inches is such a wretched height to be.’ + +‘It is a very good height indeed!’ said the Caterpillar angrily, rearing +itself upright as it spoke (it was exactly three inches high). + +‘But I’m not used to it!’ pleaded poor Alice in a piteous tone. And +she thought of herself, ‘I wish the creatures wouldn’t be so easily +offended!’ + +‘You’ll get used to it in time,’ said the Caterpillar; and it put the +hookah into its mouth and began smoking again. + +This time Alice waited patiently until it chose to speak again. In +a minute or two the Caterpillar took the hookah out of its mouth +and yawned once or twice, and shook itself. Then it got down off the +mushroom, and crawled away in the grass, merely remarking as it went, +‘One side will make you grow taller, and the other side will make you +grow shorter.’ + +‘One side of WHAT? The other side of WHAT?’ thought Alice to herself. + +‘Of the mushroom,’ said the Caterpillar, just as if she had asked it +aloud; and in another moment it was out of sight. + +Alice remained looking thoughtfully at the mushroom for a minute, trying +to make out which were the two sides of it; and as it was perfectly +round, she found this a very difficult question. However, at last she +stretched her arms round it as far as they would go, and broke off a bit +of the edge with each hand. + +‘And now which is which?’ she said to herself, and nibbled a little of +the right-hand bit to try the effect: the next moment she felt a violent +blow underneath her chin: it had struck her foot! + +She was a good deal frightened by this very sudden change, but she felt +that there was no time to be lost, as she was shrinking rapidly; so she +set to work at once to eat some of the other bit. Her chin was pressed +so closely against her foot, that there was hardly room to open her +mouth; but she did it at last, and managed to swallow a morsel of the +lefthand bit. + + + * * * * * * * + + * * * * * * + + * * * * * * * + +‘Come, my head’s free at last!’ said Alice in a tone of delight, which +changed into alarm in another moment, when she found that her shoulders +were nowhere to be found: all she could see, when she looked down, was +an immense length of neck, which seemed to rise like a stalk out of a +sea of green leaves that lay far below her. + +‘What CAN all that green stuff be?’ said Alice. ‘And where HAVE my +shoulders got to? And oh, my poor hands, how is it I can’t see you?’ +She was moving them about as she spoke, but no result seemed to follow, +except a little shaking among the distant green leaves. + +As there seemed to be no chance of getting her hands up to her head, she +tried to get her head down to them, and was delighted to find that her +neck would bend about easily in any direction, like a serpent. She had +just succeeded in curving it down into a graceful zigzag, and was going +to dive in among the leaves, which she found to be nothing but the tops +of the trees under which she had been wandering, when a sharp hiss made +her draw back in a hurry: a large pigeon had flown into her face, and +was beating her violently with its wings. + +‘Serpent!’ screamed the Pigeon. + +‘I’m NOT a serpent!’ said Alice indignantly. ‘Let me alone!’ + +‘Serpent, I say again!’ repeated the Pigeon, but in a more subdued tone, +and added with a kind of sob, ‘I’ve tried every way, and nothing seems +to suit them!’ + +‘I haven’t the least idea what you’re talking about,’ said Alice. + +‘I’ve tried the roots of trees, and I’ve tried banks, and I’ve tried +hedges,’ the Pigeon went on, without attending to her; ‘but those +serpents! There’s no pleasing them!’ + +Alice was more and more puzzled, but she thought there was no use in +saying anything more till the Pigeon had finished. + +‘As if it wasn’t trouble enough hatching the eggs,’ said the Pigeon; +‘but I must be on the look-out for serpents night and day! Why, I +haven’t had a wink of sleep these three weeks!’ + +‘I’m very sorry you’ve been annoyed,’ said Alice, who was beginning to +see its meaning. + +‘And just as I’d taken the highest tree in the wood,’ continued the +Pigeon, raising its voice to a shriek, ‘and just as I was thinking I +should be free of them at last, they must needs come wriggling down from +the sky! Ugh, Serpent!’ + +‘But I’m NOT a serpent, I tell you!’ said Alice. ‘I’m a--I’m a--’ + +‘Well! WHAT are you?’ said the Pigeon. ‘I can see you’re trying to +invent something!’ + +‘I--I’m a little girl,’ said Alice, rather doubtfully, as she remembered +the number of changes she had gone through that day. + +‘A likely story indeed!’ said the Pigeon in a tone of the deepest +contempt. ‘I’ve seen a good many little girls in my time, but never ONE +with such a neck as that! No, no! You’re a serpent; and there’s no use +denying it. I suppose you’ll be telling me next that you never tasted an +egg!’ + +‘I HAVE tasted eggs, certainly,’ said Alice, who was a very truthful +child; ‘but little girls eat eggs quite as much as serpents do, you +know.’ + +‘I don’t believe it,’ said the Pigeon; ‘but if they do, why then they’re +a kind of serpent, that’s all I can say.’ + +This was such a new idea to Alice, that she was quite silent for a +minute or two, which gave the Pigeon the opportunity of adding, ‘You’re +looking for eggs, I know THAT well enough; and what does it matter to me +whether you’re a little girl or a serpent?’ + +‘It matters a good deal to ME,’ said Alice hastily; ‘but I’m not looking +for eggs, as it happens; and if I was, I shouldn’t want YOURS: I don’t +like them raw.’ + +‘Well, be off, then!’ said the Pigeon in a sulky tone, as it settled +down again into its nest. Alice crouched down among the trees as well as +she could, for her neck kept getting entangled among the branches, and +every now and then she had to stop and untwist it. After a while she +remembered that she still held the pieces of mushroom in her hands, and +she set to work very carefully, nibbling first at one and then at the +other, and growing sometimes taller and sometimes shorter, until she had +succeeded in bringing herself down to her usual height. + +It was so long since she had been anything near the right size, that it +felt quite strange at first; but she got used to it in a few minutes, +and began talking to herself, as usual. ‘Come, there’s half my plan done +now! How puzzling all these changes are! I’m never sure what I’m going +to be, from one minute to another! However, I’ve got back to my right +size: the next thing is, to get into that beautiful garden--how IS that +to be done, I wonder?’ As she said this, she came suddenly upon an open +place, with a little house in it about four feet high. ‘Whoever lives +there,’ thought Alice, ‘it’ll never do to come upon them THIS size: why, +I should frighten them out of their wits!’ So she began nibbling at the +righthand bit again, and did not venture to go near the house till she +had brought herself down to nine inches high. + + + + +CHAPTER VI. Pig and Pepper + +For a minute or two she stood looking at the house, and wondering what +to do next, when suddenly a footman in livery came running out of the +wood--(she considered him to be a footman because he was in livery: +otherwise, judging by his face only, she would have called him a +fish)--and rapped loudly at the door with his knuckles. It was opened +by another footman in livery, with a round face, and large eyes like a +frog; and both footmen, Alice noticed, had powdered hair that curled all +over their heads. She felt very curious to know what it was all about, +and crept a little way out of the wood to listen. + +The Fish-Footman began by producing from under his arm a great letter, +nearly as large as himself, and this he handed over to the other, +saying, in a solemn tone, ‘For the Duchess. An invitation from the Queen +to play croquet.’ The Frog-Footman repeated, in the same solemn tone, +only changing the order of the words a little, ‘From the Queen. An +invitation for the Duchess to play croquet.’ + +Then they both bowed low, and their curls got entangled together. + +Alice laughed so much at this, that she had to run back into the +wood for fear of their hearing her; and when she next peeped out the +Fish-Footman was gone, and the other was sitting on the ground near the +door, staring stupidly up into the sky. + +Alice went timidly up to the door, and knocked. + +‘There’s no sort of use in knocking,’ said the Footman, ‘and that for +two reasons. First, because I’m on the same side of the door as you +are; secondly, because they’re making such a noise inside, no one could +possibly hear you.’ And certainly there was a most extraordinary noise +going on within--a constant howling and sneezing, and every now and then +a great crash, as if a dish or kettle had been broken to pieces. + +‘Please, then,’ said Alice, ‘how am I to get in?’ + +‘There might be some sense in your knocking,’ the Footman went on +without attending to her, ‘if we had the door between us. For instance, +if you were INSIDE, you might knock, and I could let you out, you know.’ +He was looking up into the sky all the time he was speaking, and this +Alice thought decidedly uncivil. ‘But perhaps he can’t help it,’ she +said to herself; ‘his eyes are so VERY nearly at the top of his head. +But at any rate he might answer questions.--How am I to get in?’ she +repeated, aloud. + +‘I shall sit here,’ the Footman remarked, ‘till tomorrow--’ + +At this moment the door of the house opened, and a large plate came +skimming out, straight at the Footman’s head: it just grazed his nose, +and broke to pieces against one of the trees behind him. + +‘--or next day, maybe,’ the Footman continued in the same tone, exactly +as if nothing had happened. + +‘How am I to get in?’ asked Alice again, in a louder tone. + +‘ARE you to get in at all?’ said the Footman. ‘That’s the first +question, you know.’ + +It was, no doubt: only Alice did not like to be told so. ‘It’s really +dreadful,’ she muttered to herself, ‘the way all the creatures argue. +It’s enough to drive one crazy!’ + +The Footman seemed to think this a good opportunity for repeating his +remark, with variations. ‘I shall sit here,’ he said, ‘on and off, for +days and days.’ + +‘But what am I to do?’ said Alice. + +‘Anything you like,’ said the Footman, and began whistling. + +‘Oh, there’s no use in talking to him,’ said Alice desperately: ‘he’s +perfectly idiotic!’ And she opened the door and went in. + +The door led right into a large kitchen, which was full of smoke from +one end to the other: the Duchess was sitting on a three-legged stool in +the middle, nursing a baby; the cook was leaning over the fire, stirring +a large cauldron which seemed to be full of soup. + +‘There’s certainly too much pepper in that soup!’ Alice said to herself, +as well as she could for sneezing. + +There was certainly too much of it in the air. Even the Duchess +sneezed occasionally; and as for the baby, it was sneezing and howling +alternately without a moment’s pause. The only things in the kitchen +that did not sneeze, were the cook, and a large cat which was sitting on +the hearth and grinning from ear to ear. + +‘Please would you tell me,’ said Alice, a little timidly, for she was +not quite sure whether it was good manners for her to speak first, ‘why +your cat grins like that?’ + +‘It’s a Cheshire cat,’ said the Duchess, ‘and that’s why. Pig!’ + +She said the last word with such sudden violence that Alice quite +jumped; but she saw in another moment that it was addressed to the baby, +and not to her, so she took courage, and went on again:-- + +‘I didn’t know that Cheshire cats always grinned; in fact, I didn’t know +that cats COULD grin.’ + +‘They all can,’ said the Duchess; ‘and most of ‘em do.’ + +‘I don’t know of any that do,’ Alice said very politely, feeling quite +pleased to have got into a conversation. + +‘You don’t know much,’ said the Duchess; ‘and that’s a fact.’ + +Alice did not at all like the tone of this remark, and thought it would +be as well to introduce some other subject of conversation. While she +was trying to fix on one, the cook took the cauldron of soup off the +fire, and at once set to work throwing everything within her reach at +the Duchess and the baby--the fire-irons came first; then followed a +shower of saucepans, plates, and dishes. The Duchess took no notice of +them even when they hit her; and the baby was howling so much already, +that it was quite impossible to say whether the blows hurt it or not. + +‘Oh, PLEASE mind what you’re doing!’ cried Alice, jumping up and down in +an agony of terror. ‘Oh, there goes his PRECIOUS nose’; as an unusually +large saucepan flew close by it, and very nearly carried it off. + +‘If everybody minded their own business,’ the Duchess said in a hoarse +growl, ‘the world would go round a deal faster than it does.’ + +‘Which would NOT be an advantage,’ said Alice, who felt very glad to get +an opportunity of showing off a little of her knowledge. ‘Just think of +what work it would make with the day and night! You see the earth takes +twenty-four hours to turn round on its axis--’ + +‘Talking of axes,’ said the Duchess, ‘chop off her head!’ + +Alice glanced rather anxiously at the cook, to see if she meant to take +the hint; but the cook was busily stirring the soup, and seemed not to +be listening, so she went on again: ‘Twenty-four hours, I THINK; or is +it twelve? I--’ + +‘Oh, don’t bother ME,’ said the Duchess; ‘I never could abide figures!’ +And with that she began nursing her child again, singing a sort of +lullaby to it as she did so, and giving it a violent shake at the end of +every line: + + ‘Speak roughly to your little boy, + And beat him when he sneezes: + He only does it to annoy, + Because he knows it teases.’ + + CHORUS. + + (In which the cook and the baby joined):-- + + ‘Wow! wow! wow!’ + +While the Duchess sang the second verse of the song, she kept tossing +the baby violently up and down, and the poor little thing howled so, +that Alice could hardly hear the words:-- + + ‘I speak severely to my boy, + I beat him when he sneezes; + For he can thoroughly enjoy + The pepper when he pleases!’ + + CHORUS. + + ‘Wow! wow! wow!’ + +‘Here! you may nurse it a bit, if you like!’ the Duchess said to Alice, +flinging the baby at her as she spoke. ‘I must go and get ready to play +croquet with the Queen,’ and she hurried out of the room. The cook threw +a frying-pan after her as she went out, but it just missed her. + +Alice caught the baby with some difficulty, as it was a queer-shaped +little creature, and held out its arms and legs in all directions, ‘just +like a star-fish,’ thought Alice. The poor little thing was snorting +like a steam-engine when she caught it, and kept doubling itself up and +straightening itself out again, so that altogether, for the first minute +or two, it was as much as she could do to hold it. + +As soon as she had made out the proper way of nursing it, (which was to +twist it up into a sort of knot, and then keep tight hold of its right +ear and left foot, so as to prevent its undoing itself,) she carried +it out into the open air. ‘IF I don’t take this child away with me,’ +thought Alice, ‘they’re sure to kill it in a day or two: wouldn’t it be +murder to leave it behind?’ She said the last words out loud, and the +little thing grunted in reply (it had left off sneezing by this time). +‘Don’t grunt,’ said Alice; ‘that’s not at all a proper way of expressing +yourself.’ + +The baby grunted again, and Alice looked very anxiously into its face to +see what was the matter with it. There could be no doubt that it had +a VERY turn-up nose, much more like a snout than a real nose; also its +eyes were getting extremely small for a baby: altogether Alice did not +like the look of the thing at all. ‘But perhaps it was only sobbing,’ +she thought, and looked into its eyes again, to see if there were any +tears. + +No, there were no tears. ‘If you’re going to turn into a pig, my dear,’ +said Alice, seriously, ‘I’ll have nothing more to do with you. Mind +now!’ The poor little thing sobbed again (or grunted, it was impossible +to say which), and they went on for some while in silence. + +Alice was just beginning to think to herself, ‘Now, what am I to do with +this creature when I get it home?’ when it grunted again, so violently, +that she looked down into its face in some alarm. This time there could +be NO mistake about it: it was neither more nor less than a pig, and she +felt that it would be quite absurd for her to carry it further. + +So she set the little creature down, and felt quite relieved to see +it trot away quietly into the wood. ‘If it had grown up,’ she said +to herself, ‘it would have made a dreadfully ugly child: but it makes +rather a handsome pig, I think.’ And she began thinking over other +children she knew, who might do very well as pigs, and was just saying +to herself, ‘if one only knew the right way to change them--’ when she +was a little startled by seeing the Cheshire Cat sitting on a bough of a +tree a few yards off. + +The Cat only grinned when it saw Alice. It looked good-natured, she +thought: still it had VERY long claws and a great many teeth, so she +felt that it ought to be treated with respect. + +‘Cheshire Puss,’ she began, rather timidly, as she did not at all know +whether it would like the name: however, it only grinned a little wider. +‘Come, it’s pleased so far,’ thought Alice, and she went on. ‘Would you +tell me, please, which way I ought to go from here?’ + +‘That depends a good deal on where you want to get to,’ said the Cat. + +‘I don’t much care where--’ said Alice. + +‘Then it doesn’t matter which way you go,’ said the Cat. + +‘--so long as I get SOMEWHERE,’ Alice added as an explanation. + +‘Oh, you’re sure to do that,’ said the Cat, ‘if you only walk long +enough.’ + +Alice felt that this could not be denied, so she tried another question. +‘What sort of people live about here?’ + +‘In THAT direction,’ the Cat said, waving its right paw round, ‘lives +a Hatter: and in THAT direction,’ waving the other paw, ‘lives a March +Hare. Visit either you like: they’re both mad.’ + +‘But I don’t want to go among mad people,’ Alice remarked. + +‘Oh, you can’t help that,’ said the Cat: ‘we’re all mad here. I’m mad. +You’re mad.’ + +‘How do you know I’m mad?’ said Alice. + +‘You must be,’ said the Cat, ‘or you wouldn’t have come here.’ + +Alice didn’t think that proved it at all; however, she went on ‘And how +do you know that you’re mad?’ + +‘To begin with,’ said the Cat, ‘a dog’s not mad. You grant that?’ + +‘I suppose so,’ said Alice. + +‘Well, then,’ the Cat went on, ‘you see, a dog growls when it’s angry, +and wags its tail when it’s pleased. Now I growl when I’m pleased, and +wag my tail when I’m angry. Therefore I’m mad.’ + +‘I call it purring, not growling,’ said Alice. + +‘Call it what you like,’ said the Cat. ‘Do you play croquet with the +Queen to-day?’ + +‘I should like it very much,’ said Alice, ‘but I haven’t been invited +yet.’ + +‘You’ll see me there,’ said the Cat, and vanished. + +Alice was not much surprised at this, she was getting so used to queer +things happening. While she was looking at the place where it had been, +it suddenly appeared again. + +‘By-the-bye, what became of the baby?’ said the Cat. ‘I’d nearly +forgotten to ask.’ + +‘It turned into a pig,’ Alice quietly said, just as if it had come back +in a natural way. + +‘I thought it would,’ said the Cat, and vanished again. + +Alice waited a little, half expecting to see it again, but it did not +appear, and after a minute or two she walked on in the direction in +which the March Hare was said to live. ‘I’ve seen hatters before,’ she +said to herself; ‘the March Hare will be much the most interesting, and +perhaps as this is May it won’t be raving mad--at least not so mad as +it was in March.’ As she said this, she looked up, and there was the Cat +again, sitting on a branch of a tree. + +‘Did you say pig, or fig?’ said the Cat. + +‘I said pig,’ replied Alice; ‘and I wish you wouldn’t keep appearing and +vanishing so suddenly: you make one quite giddy.’ + +‘All right,’ said the Cat; and this time it vanished quite slowly, +beginning with the end of the tail, and ending with the grin, which +remained some time after the rest of it had gone. + +‘Well! I’ve often seen a cat without a grin,’ thought Alice; ‘but a grin +without a cat! It’s the most curious thing I ever saw in my life!’ + +She had not gone much farther before she came in sight of the house +of the March Hare: she thought it must be the right house, because the +chimneys were shaped like ears and the roof was thatched with fur. It +was so large a house, that she did not like to go nearer till she had +nibbled some more of the lefthand bit of mushroom, and raised herself to +about two feet high: even then she walked up towards it rather timidly, +saying to herself ‘Suppose it should be raving mad after all! I almost +wish I’d gone to see the Hatter instead!’ + + + + +CHAPTER VII. A Mad Tea-Party + +There was a table set out under a tree in front of the house, and the +March Hare and the Hatter were having tea at it: a Dormouse was sitting +between them, fast asleep, and the other two were using it as a +cushion, resting their elbows on it, and talking over its head. ‘Very +uncomfortable for the Dormouse,’ thought Alice; ‘only, as it’s asleep, I +suppose it doesn’t mind.’ + +The table was a large one, but the three were all crowded together at +one corner of it: ‘No room! No room!’ they cried out when they saw Alice +coming. ‘There’s PLENTY of room!’ said Alice indignantly, and she sat +down in a large arm-chair at one end of the table. + +‘Have some wine,’ the March Hare said in an encouraging tone. + +Alice looked all round the table, but there was nothing on it but tea. +‘I don’t see any wine,’ she remarked. + +‘There isn’t any,’ said the March Hare. + +‘Then it wasn’t very civil of you to offer it,’ said Alice angrily. + +‘It wasn’t very civil of you to sit down without being invited,’ said +the March Hare. + +‘I didn’t know it was YOUR table,’ said Alice; ‘it’s laid for a great +many more than three.’ + +‘Your hair wants cutting,’ said the Hatter. He had been looking at Alice +for some time with great curiosity, and this was his first speech. + +‘You should learn not to make personal remarks,’ Alice said with some +severity; ‘it’s very rude.’ + +The Hatter opened his eyes very wide on hearing this; but all he SAID +was, ‘Why is a raven like a writing-desk?’ + +‘Come, we shall have some fun now!’ thought Alice. ‘I’m glad they’ve +begun asking riddles.--I believe I can guess that,’ she added aloud. + +‘Do you mean that you think you can find out the answer to it?’ said the +March Hare. + +‘Exactly so,’ said Alice. + +‘Then you should say what you mean,’ the March Hare went on. + +‘I do,’ Alice hastily replied; ‘at least--at least I mean what I +say--that’s the same thing, you know.’ + +‘Not the same thing a bit!’ said the Hatter. ‘You might just as well say +that “I see what I eat” is the same thing as “I eat what I see”!’ + +‘You might just as well say,’ added the March Hare, ‘that “I like what I +get” is the same thing as “I get what I like”!’ + +‘You might just as well say,’ added the Dormouse, who seemed to be +talking in his sleep, ‘that “I breathe when I sleep” is the same thing +as “I sleep when I breathe”!’ + +‘It IS the same thing with you,’ said the Hatter, and here the +conversation dropped, and the party sat silent for a minute, while Alice +thought over all she could remember about ravens and writing-desks, +which wasn’t much. + +The Hatter was the first to break the silence. ‘What day of the month +is it?’ he said, turning to Alice: he had taken his watch out of his +pocket, and was looking at it uneasily, shaking it every now and then, +and holding it to his ear. + +Alice considered a little, and then said ‘The fourth.’ + +‘Two days wrong!’ sighed the Hatter. ‘I told you butter wouldn’t suit +the works!’ he added looking angrily at the March Hare. + +‘It was the BEST butter,’ the March Hare meekly replied. + +‘Yes, but some crumbs must have got in as well,’ the Hatter grumbled: +‘you shouldn’t have put it in with the bread-knife.’ + +The March Hare took the watch and looked at it gloomily: then he dipped +it into his cup of tea, and looked at it again: but he could think of +nothing better to say than his first remark, ‘It was the BEST butter, +you know.’ + +Alice had been looking over his shoulder with some curiosity. ‘What a +funny watch!’ she remarked. ‘It tells the day of the month, and doesn’t +tell what o’clock it is!’ + +‘Why should it?’ muttered the Hatter. ‘Does YOUR watch tell you what +year it is?’ + +‘Of course not,’ Alice replied very readily: ‘but that’s because it +stays the same year for such a long time together.’ + +‘Which is just the case with MINE,’ said the Hatter. + +Alice felt dreadfully puzzled. The Hatter’s remark seemed to have no +sort of meaning in it, and yet it was certainly English. ‘I don’t quite +understand you,’ she said, as politely as she could. + +‘The Dormouse is asleep again,’ said the Hatter, and he poured a little +hot tea upon its nose. + +The Dormouse shook its head impatiently, and said, without opening its +eyes, ‘Of course, of course; just what I was going to remark myself.’ + +‘Have you guessed the riddle yet?’ the Hatter said, turning to Alice +again. + +‘No, I give it up,’ Alice replied: ‘what’s the answer?’ + +‘I haven’t the slightest idea,’ said the Hatter. + +‘Nor I,’ said the March Hare. + +Alice sighed wearily. ‘I think you might do something better with the +time,’ she said, ‘than waste it in asking riddles that have no answers.’ + +‘If you knew Time as well as I do,’ said the Hatter, ‘you wouldn’t talk +about wasting IT. It’s HIM.’ + +‘I don’t know what you mean,’ said Alice. + +‘Of course you don’t!’ the Hatter said, tossing his head contemptuously. +‘I dare say you never even spoke to Time!’ + +‘Perhaps not,’ Alice cautiously replied: ‘but I know I have to beat time +when I learn music.’ + +‘Ah! that accounts for it,’ said the Hatter. ‘He won’t stand beating. +Now, if you only kept on good terms with him, he’d do almost anything +you liked with the clock. For instance, suppose it were nine o’clock in +the morning, just time to begin lessons: you’d only have to whisper a +hint to Time, and round goes the clock in a twinkling! Half-past one, +time for dinner!’ + +[‘I only wish it was,’ the March Hare said to itself in a whisper.) + +‘That would be grand, certainly,’ said Alice thoughtfully: ‘but then--I +shouldn’t be hungry for it, you know.’ + +‘Not at first, perhaps,’ said the Hatter: ‘but you could keep it to +half-past one as long as you liked.’ + +‘Is that the way YOU manage?’ Alice asked. + +The Hatter shook his head mournfully. ‘Not I!’ he replied. ‘We +quarrelled last March--just before HE went mad, you know--’ (pointing +with his tea spoon at the March Hare,) ‘--it was at the great concert +given by the Queen of Hearts, and I had to sing + + “Twinkle, twinkle, little bat! + How I wonder what you’re at!” + +You know the song, perhaps?’ + +‘I’ve heard something like it,’ said Alice. + +‘It goes on, you know,’ the Hatter continued, ‘in this way:-- + + “Up above the world you fly, + Like a tea-tray in the sky. + Twinkle, twinkle--“’ + +Here the Dormouse shook itself, and began singing in its sleep ‘Twinkle, +twinkle, twinkle, twinkle--’ and went on so long that they had to pinch +it to make it stop. + +‘Well, I’d hardly finished the first verse,’ said the Hatter, ‘when the +Queen jumped up and bawled out, “He’s murdering the time! Off with his +head!”’ + +‘How dreadfully savage!’ exclaimed Alice. + +‘And ever since that,’ the Hatter went on in a mournful tone, ‘he won’t +do a thing I ask! It’s always six o’clock now.’ + +A bright idea came into Alice’s head. ‘Is that the reason so many +tea-things are put out here?’ she asked. + +‘Yes, that’s it,’ said the Hatter with a sigh: ‘it’s always tea-time, +and we’ve no time to wash the things between whiles.’ + +‘Then you keep moving round, I suppose?’ said Alice. + +‘Exactly so,’ said the Hatter: ‘as the things get used up.’ + +‘But what happens when you come to the beginning again?’ Alice ventured +to ask. + +‘Suppose we change the subject,’ the March Hare interrupted, yawning. +‘I’m getting tired of this. I vote the young lady tells us a story.’ + +‘I’m afraid I don’t know one,’ said Alice, rather alarmed at the +proposal. + +‘Then the Dormouse shall!’ they both cried. ‘Wake up, Dormouse!’ And +they pinched it on both sides at once. + +The Dormouse slowly opened his eyes. ‘I wasn’t asleep,’ he said in a +hoarse, feeble voice: ‘I heard every word you fellows were saying.’ + +‘Tell us a story!’ said the March Hare. + +‘Yes, please do!’ pleaded Alice. + +‘And be quick about it,’ added the Hatter, ‘or you’ll be asleep again +before it’s done.’ + +‘Once upon a time there were three little sisters,’ the Dormouse began +in a great hurry; ‘and their names were Elsie, Lacie, and Tillie; and +they lived at the bottom of a well--’ + +‘What did they live on?’ said Alice, who always took a great interest in +questions of eating and drinking. + +‘They lived on treacle,’ said the Dormouse, after thinking a minute or +two. + +‘They couldn’t have done that, you know,’ Alice gently remarked; ‘they’d +have been ill.’ + +‘So they were,’ said the Dormouse; ‘VERY ill.’ + +Alice tried to fancy to herself what such an extraordinary ways of +living would be like, but it puzzled her too much, so she went on: ‘But +why did they live at the bottom of a well?’ + +‘Take some more tea,’ the March Hare said to Alice, very earnestly. + +‘I’ve had nothing yet,’ Alice replied in an offended tone, ‘so I can’t +take more.’ + +‘You mean you can’t take LESS,’ said the Hatter: ‘it’s very easy to take +MORE than nothing.’ + +‘Nobody asked YOUR opinion,’ said Alice. + +‘Who’s making personal remarks now?’ the Hatter asked triumphantly. + +Alice did not quite know what to say to this: so she helped herself +to some tea and bread-and-butter, and then turned to the Dormouse, and +repeated her question. ‘Why did they live at the bottom of a well?’ + +The Dormouse again took a minute or two to think about it, and then +said, ‘It was a treacle-well.’ + +‘There’s no such thing!’ Alice was beginning very angrily, but the +Hatter and the March Hare went ‘Sh! sh!’ and the Dormouse sulkily +remarked, ‘If you can’t be civil, you’d better finish the story for +yourself.’ + +‘No, please go on!’ Alice said very humbly; ‘I won’t interrupt again. I +dare say there may be ONE.’ + +‘One, indeed!’ said the Dormouse indignantly. However, he consented to +go on. ‘And so these three little sisters--they were learning to draw, +you know--’ + +‘What did they draw?’ said Alice, quite forgetting her promise. + +‘Treacle,’ said the Dormouse, without considering at all this time. + +‘I want a clean cup,’ interrupted the Hatter: ‘let’s all move one place +on.’ + +He moved on as he spoke, and the Dormouse followed him: the March Hare +moved into the Dormouse’s place, and Alice rather unwillingly took +the place of the March Hare. The Hatter was the only one who got any +advantage from the change: and Alice was a good deal worse off than +before, as the March Hare had just upset the milk-jug into his plate. + +Alice did not wish to offend the Dormouse again, so she began very +cautiously: ‘But I don’t understand. Where did they draw the treacle +from?’ + +‘You can draw water out of a water-well,’ said the Hatter; ‘so I should +think you could draw treacle out of a treacle-well--eh, stupid?’ + +‘But they were IN the well,’ Alice said to the Dormouse, not choosing to +notice this last remark. + +‘Of course they were’, said the Dormouse; ‘--well in.’ + +This answer so confused poor Alice, that she let the Dormouse go on for +some time without interrupting it. + +‘They were learning to draw,’ the Dormouse went on, yawning and rubbing +its eyes, for it was getting very sleepy; ‘and they drew all manner of +things--everything that begins with an M--’ + +‘Why with an M?’ said Alice. + +‘Why not?’ said the March Hare. + +Alice was silent. + +The Dormouse had closed its eyes by this time, and was going off into +a doze; but, on being pinched by the Hatter, it woke up again with +a little shriek, and went on: ‘--that begins with an M, such as +mouse-traps, and the moon, and memory, and muchness--you know you say +things are “much of a muchness”--did you ever see such a thing as a +drawing of a muchness?’ + +‘Really, now you ask me,’ said Alice, very much confused, ‘I don’t +think--’ + +‘Then you shouldn’t talk,’ said the Hatter. + +This piece of rudeness was more than Alice could bear: she got up in +great disgust, and walked off; the Dormouse fell asleep instantly, and +neither of the others took the least notice of her going, though she +looked back once or twice, half hoping that they would call after her: +the last time she saw them, they were trying to put the Dormouse into +the teapot. + +‘At any rate I’ll never go THERE again!’ said Alice as she picked her +way through the wood. ‘It’s the stupidest tea-party I ever was at in all +my life!’ + +Just as she said this, she noticed that one of the trees had a door +leading right into it. ‘That’s very curious!’ she thought. ‘But +everything’s curious today. I think I may as well go in at once.’ And in +she went. + +Once more she found herself in the long hall, and close to the little +glass table. ‘Now, I’ll manage better this time,’ she said to herself, +and began by taking the little golden key, and unlocking the door that +led into the garden. Then she went to work nibbling at the mushroom (she +had kept a piece of it in her pocket) till she was about a foot high: +then she walked down the little passage: and THEN--she found herself at +last in the beautiful garden, among the bright flower-beds and the cool +fountains. + + + + +CHAPTER VIII. The Queen’s Croquet-Ground + +A large rose-tree stood near the entrance of the garden: the roses +growing on it were white, but there were three gardeners at it, busily +painting them red. Alice thought this a very curious thing, and she went +nearer to watch them, and just as she came up to them she heard one of +them say, ‘Look out now, Five! Don’t go splashing paint over me like +that!’ + +‘I couldn’t help it,’ said Five, in a sulky tone; ‘Seven jogged my +elbow.’ + +On which Seven looked up and said, ‘That’s right, Five! Always lay the +blame on others!’ + +‘YOU’D better not talk!’ said Five. ‘I heard the Queen say only +yesterday you deserved to be beheaded!’ + +‘What for?’ said the one who had spoken first. + +‘That’s none of YOUR business, Two!’ said Seven. + +‘Yes, it IS his business!’ said Five, ‘and I’ll tell him--it was for +bringing the cook tulip-roots instead of onions.’ + +Seven flung down his brush, and had just begun ‘Well, of all the unjust +things--’ when his eye chanced to fall upon Alice, as she stood watching +them, and he checked himself suddenly: the others looked round also, and +all of them bowed low. + +‘Would you tell me,’ said Alice, a little timidly, ‘why you are painting +those roses?’ + +Five and Seven said nothing, but looked at Two. Two began in a low +voice, ‘Why the fact is, you see, Miss, this here ought to have been a +RED rose-tree, and we put a white one in by mistake; and if the Queen +was to find it out, we should all have our heads cut off, you know. +So you see, Miss, we’re doing our best, afore she comes, to--’ At this +moment Five, who had been anxiously looking across the garden, called +out ‘The Queen! The Queen!’ and the three gardeners instantly threw +themselves flat upon their faces. There was a sound of many footsteps, +and Alice looked round, eager to see the Queen. + +First came ten soldiers carrying clubs; these were all shaped like +the three gardeners, oblong and flat, with their hands and feet at the +corners: next the ten courtiers; these were ornamented all over with +diamonds, and walked two and two, as the soldiers did. After these came +the royal children; there were ten of them, and the little dears came +jumping merrily along hand in hand, in couples: they were all ornamented +with hearts. Next came the guests, mostly Kings and Queens, and among +them Alice recognised the White Rabbit: it was talking in a hurried +nervous manner, smiling at everything that was said, and went by without +noticing her. Then followed the Knave of Hearts, carrying the King’s +crown on a crimson velvet cushion; and, last of all this grand +procession, came THE KING AND QUEEN OF HEARTS. + +Alice was rather doubtful whether she ought not to lie down on her face +like the three gardeners, but she could not remember ever having heard +of such a rule at processions; ‘and besides, what would be the use of +a procession,’ thought she, ‘if people had all to lie down upon their +faces, so that they couldn’t see it?’ So she stood still where she was, +and waited. + +When the procession came opposite to Alice, they all stopped and looked +at her, and the Queen said severely ‘Who is this?’ She said it to the +Knave of Hearts, who only bowed and smiled in reply. + +‘Idiot!’ said the Queen, tossing her head impatiently; and, turning to +Alice, she went on, ‘What’s your name, child?’ + +‘My name is Alice, so please your Majesty,’ said Alice very politely; +but she added, to herself, ‘Why, they’re only a pack of cards, after +all. I needn’t be afraid of them!’ + +‘And who are THESE?’ said the Queen, pointing to the three gardeners who +were lying round the rosetree; for, you see, as they were lying on their +faces, and the pattern on their backs was the same as the rest of the +pack, she could not tell whether they were gardeners, or soldiers, or +courtiers, or three of her own children. + +‘How should I know?’ said Alice, surprised at her own courage. ‘It’s no +business of MINE.’ + +The Queen turned crimson with fury, and, after glaring at her for a +moment like a wild beast, screamed ‘Off with her head! Off--’ + +‘Nonsense!’ said Alice, very loudly and decidedly, and the Queen was +silent. + +The King laid his hand upon her arm, and timidly said ‘Consider, my +dear: she is only a child!’ + +The Queen turned angrily away from him, and said to the Knave ‘Turn them +over!’ + +The Knave did so, very carefully, with one foot. + +‘Get up!’ said the Queen, in a shrill, loud voice, and the three +gardeners instantly jumped up, and began bowing to the King, the Queen, +the royal children, and everybody else. + +‘Leave off that!’ screamed the Queen. ‘You make me giddy.’ And then, +turning to the rose-tree, she went on, ‘What HAVE you been doing here?’ + +‘May it please your Majesty,’ said Two, in a very humble tone, going +down on one knee as he spoke, ‘we were trying--’ + +‘I see!’ said the Queen, who had meanwhile been examining the roses. +‘Off with their heads!’ and the procession moved on, three of the +soldiers remaining behind to execute the unfortunate gardeners, who ran +to Alice for protection. + +‘You shan’t be beheaded!’ said Alice, and she put them into a large +flower-pot that stood near. The three soldiers wandered about for a +minute or two, looking for them, and then quietly marched off after the +others. + +‘Are their heads off?’ shouted the Queen. + +‘Their heads are gone, if it please your Majesty!’ the soldiers shouted +in reply. + +‘That’s right!’ shouted the Queen. ‘Can you play croquet?’ + +The soldiers were silent, and looked at Alice, as the question was +evidently meant for her. + +‘Yes!’ shouted Alice. + +‘Come on, then!’ roared the Queen, and Alice joined the procession, +wondering very much what would happen next. + +‘It’s--it’s a very fine day!’ said a timid voice at her side. She was +walking by the White Rabbit, who was peeping anxiously into her face. + +‘Very,’ said Alice: ‘--where’s the Duchess?’ + +‘Hush! Hush!’ said the Rabbit in a low, hurried tone. He looked +anxiously over his shoulder as he spoke, and then raised himself upon +tiptoe, put his mouth close to her ear, and whispered ‘She’s under +sentence of execution.’ + +‘What for?’ said Alice. + +‘Did you say “What a pity!”?’ the Rabbit asked. + +‘No, I didn’t,’ said Alice: ‘I don’t think it’s at all a pity. I said +“What for?”’ + +‘She boxed the Queen’s ears--’ the Rabbit began. Alice gave a little +scream of laughter. ‘Oh, hush!’ the Rabbit whispered in a frightened +tone. ‘The Queen will hear you! You see, she came rather late, and the +Queen said--’ + +‘Get to your places!’ shouted the Queen in a voice of thunder, and +people began running about in all directions, tumbling up against each +other; however, they got settled down in a minute or two, and the game +began. Alice thought she had never seen such a curious croquet-ground in +her life; it was all ridges and furrows; the balls were live hedgehogs, +the mallets live flamingoes, and the soldiers had to double themselves +up and to stand on their hands and feet, to make the arches. + +The chief difficulty Alice found at first was in managing her flamingo: +she succeeded in getting its body tucked away, comfortably enough, under +her arm, with its legs hanging down, but generally, just as she had got +its neck nicely straightened out, and was going to give the hedgehog a +blow with its head, it WOULD twist itself round and look up in her face, +with such a puzzled expression that she could not help bursting out +laughing: and when she had got its head down, and was going to begin +again, it was very provoking to find that the hedgehog had unrolled +itself, and was in the act of crawling away: besides all this, there was +generally a ridge or furrow in the way wherever she wanted to send the +hedgehog to, and, as the doubled-up soldiers were always getting up +and walking off to other parts of the ground, Alice soon came to the +conclusion that it was a very difficult game indeed. + +The players all played at once without waiting for turns, quarrelling +all the while, and fighting for the hedgehogs; and in a very short +time the Queen was in a furious passion, and went stamping about, and +shouting ‘Off with his head!’ or ‘Off with her head!’ about once in a +minute. + +Alice began to feel very uneasy: to be sure, she had not as yet had any +dispute with the Queen, but she knew that it might happen any minute, +‘and then,’ thought she, ‘what would become of me? They’re dreadfully +fond of beheading people here; the great wonder is, that there’s any one +left alive!’ + +She was looking about for some way of escape, and wondering whether she +could get away without being seen, when she noticed a curious appearance +in the air: it puzzled her very much at first, but, after watching it +a minute or two, she made it out to be a grin, and she said to herself +‘It’s the Cheshire Cat: now I shall have somebody to talk to.’ + +‘How are you getting on?’ said the Cat, as soon as there was mouth +enough for it to speak with. + +Alice waited till the eyes appeared, and then nodded. ‘It’s no use +speaking to it,’ she thought, ‘till its ears have come, or at least one +of them.’ In another minute the whole head appeared, and then Alice put +down her flamingo, and began an account of the game, feeling very glad +she had someone to listen to her. The Cat seemed to think that there was +enough of it now in sight, and no more of it appeared. + +‘I don’t think they play at all fairly,’ Alice began, in rather a +complaining tone, ‘and they all quarrel so dreadfully one can’t hear +oneself speak--and they don’t seem to have any rules in particular; +at least, if there are, nobody attends to them--and you’ve no idea how +confusing it is all the things being alive; for instance, there’s the +arch I’ve got to go through next walking about at the other end of the +ground--and I should have croqueted the Queen’s hedgehog just now, only +it ran away when it saw mine coming!’ + +‘How do you like the Queen?’ said the Cat in a low voice. + +‘Not at all,’ said Alice: ‘she’s so extremely--’ Just then she noticed +that the Queen was close behind her, listening: so she went on, +‘--likely to win, that it’s hardly worth while finishing the game.’ + +The Queen smiled and passed on. + +‘Who ARE you talking to?’ said the King, going up to Alice, and looking +at the Cat’s head with great curiosity. + +‘It’s a friend of mine--a Cheshire Cat,’ said Alice: ‘allow me to +introduce it.’ + +‘I don’t like the look of it at all,’ said the King: ‘however, it may +kiss my hand if it likes.’ + +‘I’d rather not,’ the Cat remarked. + +‘Don’t be impertinent,’ said the King, ‘and don’t look at me like that!’ +He got behind Alice as he spoke. + +‘A cat may look at a king,’ said Alice. ‘I’ve read that in some book, +but I don’t remember where.’ + +‘Well, it must be removed,’ said the King very decidedly, and he called +the Queen, who was passing at the moment, ‘My dear! I wish you would +have this cat removed!’ + +The Queen had only one way of settling all difficulties, great or small. +‘Off with his head!’ she said, without even looking round. + +‘I’ll fetch the executioner myself,’ said the King eagerly, and he +hurried off. + +Alice thought she might as well go back, and see how the game was going +on, as she heard the Queen’s voice in the distance, screaming with +passion. She had already heard her sentence three of the players to be +executed for having missed their turns, and she did not like the look +of things at all, as the game was in such confusion that she never knew +whether it was her turn or not. So she went in search of her hedgehog. + +The hedgehog was engaged in a fight with another hedgehog, which seemed +to Alice an excellent opportunity for croqueting one of them with the +other: the only difficulty was, that her flamingo was gone across to the +other side of the garden, where Alice could see it trying in a helpless +sort of way to fly up into a tree. + +By the time she had caught the flamingo and brought it back, the fight +was over, and both the hedgehogs were out of sight: ‘but it doesn’t +matter much,’ thought Alice, ‘as all the arches are gone from this side +of the ground.’ So she tucked it away under her arm, that it might not +escape again, and went back for a little more conversation with her +friend. + +When she got back to the Cheshire Cat, she was surprised to find quite a +large crowd collected round it: there was a dispute going on between +the executioner, the King, and the Queen, who were all talking at once, +while all the rest were quite silent, and looked very uncomfortable. + +The moment Alice appeared, she was appealed to by all three to settle +the question, and they repeated their arguments to her, though, as they +all spoke at once, she found it very hard indeed to make out exactly +what they said. + +The executioner’s argument was, that you couldn’t cut off a head unless +there was a body to cut it off from: that he had never had to do such a +thing before, and he wasn’t going to begin at HIS time of life. + +The King’s argument was, that anything that had a head could be +beheaded, and that you weren’t to talk nonsense. + +The Queen’s argument was, that if something wasn’t done about it in less +than no time she’d have everybody executed, all round. (It was this last +remark that had made the whole party look so grave and anxious.) + +Alice could think of nothing else to say but ‘It belongs to the Duchess: +you’d better ask HER about it.’ + +‘She’s in prison,’ the Queen said to the executioner: ‘fetch her here.’ +And the executioner went off like an arrow. + + The Cat’s head began fading away the moment he was gone, and, +by the time he had come back with the Duchess, it had entirely +disappeared; so the King and the executioner ran wildly up and down +looking for it, while the rest of the party went back to the game. + + + + +CHAPTER IX. The Mock Turtle’s Story + +‘You can’t think how glad I am to see you again, you dear old thing!’ +said the Duchess, as she tucked her arm affectionately into Alice’s, and +they walked off together. + +Alice was very glad to find her in such a pleasant temper, and thought +to herself that perhaps it was only the pepper that had made her so +savage when they met in the kitchen. + +‘When I’M a Duchess,’ she said to herself, (not in a very hopeful tone +though), ‘I won’t have any pepper in my kitchen AT ALL. Soup does very +well without--Maybe it’s always pepper that makes people hot-tempered,’ +she went on, very much pleased at having found out a new kind of +rule, ‘and vinegar that makes them sour--and camomile that makes +them bitter--and--and barley-sugar and such things that make children +sweet-tempered. I only wish people knew that: then they wouldn’t be so +stingy about it, you know--’ + +She had quite forgotten the Duchess by this time, and was a little +startled when she heard her voice close to her ear. ‘You’re thinking +about something, my dear, and that makes you forget to talk. I can’t +tell you just now what the moral of that is, but I shall remember it in +a bit.’ + +‘Perhaps it hasn’t one,’ Alice ventured to remark. + +‘Tut, tut, child!’ said the Duchess. ‘Everything’s got a moral, if only +you can find it.’ And she squeezed herself up closer to Alice’s side as +she spoke. + +Alice did not much like keeping so close to her: first, because the +Duchess was VERY ugly; and secondly, because she was exactly the +right height to rest her chin upon Alice’s shoulder, and it was an +uncomfortably sharp chin. However, she did not like to be rude, so she +bore it as well as she could. + +‘The game’s going on rather better now,’ she said, by way of keeping up +the conversation a little. + +‘’Tis so,’ said the Duchess: ‘and the moral of that is--“Oh, ‘tis love, +‘tis love, that makes the world go round!”’ + +‘Somebody said,’ Alice whispered, ‘that it’s done by everybody minding +their own business!’ + +‘Ah, well! It means much the same thing,’ said the Duchess, digging her +sharp little chin into Alice’s shoulder as she added, ‘and the moral +of THAT is--“Take care of the sense, and the sounds will take care of +themselves.”’ + +‘How fond she is of finding morals in things!’ Alice thought to herself. + +‘I dare say you’re wondering why I don’t put my arm round your waist,’ +the Duchess said after a pause: ‘the reason is, that I’m doubtful about +the temper of your flamingo. Shall I try the experiment?’ + +‘HE might bite,’ Alice cautiously replied, not feeling at all anxious to +have the experiment tried. + +‘Very true,’ said the Duchess: ‘flamingoes and mustard both bite. And +the moral of that is--“Birds of a feather flock together.”’ + +‘Only mustard isn’t a bird,’ Alice remarked. + +‘Right, as usual,’ said the Duchess: ‘what a clear way you have of +putting things!’ + +‘It’s a mineral, I THINK,’ said Alice. + +‘Of course it is,’ said the Duchess, who seemed ready to agree to +everything that Alice said; ‘there’s a large mustard-mine near here. And +the moral of that is--“The more there is of mine, the less there is of +yours.”’ + +‘Oh, I know!’ exclaimed Alice, who had not attended to this last remark, +‘it’s a vegetable. It doesn’t look like one, but it is.’ + +‘I quite agree with you,’ said the Duchess; ‘and the moral of that +is--“Be what you would seem to be”--or if you’d like it put more +simply--“Never imagine yourself not to be otherwise than what it might +appear to others that what you were or might have been was not otherwise +than what you had been would have appeared to them to be otherwise.”’ + +‘I think I should understand that better,’ Alice said very politely, ‘if +I had it written down: but I can’t quite follow it as you say it.’ + +‘That’s nothing to what I could say if I chose,’ the Duchess replied, in +a pleased tone. + +‘Pray don’t trouble yourself to say it any longer than that,’ said +Alice. + +‘Oh, don’t talk about trouble!’ said the Duchess. ‘I make you a present +of everything I’ve said as yet.’ + +‘A cheap sort of present!’ thought Alice. ‘I’m glad they don’t give +birthday presents like that!’ But she did not venture to say it out +loud. + +‘Thinking again?’ the Duchess asked, with another dig of her sharp +little chin. + +‘I’ve a right to think,’ said Alice sharply, for she was beginning to +feel a little worried. + +‘Just about as much right,’ said the Duchess, ‘as pigs have to fly; and +the m--’ + +But here, to Alice’s great surprise, the Duchess’s voice died away, even +in the middle of her favourite word ‘moral,’ and the arm that was linked +into hers began to tremble. Alice looked up, and there stood the Queen +in front of them, with her arms folded, frowning like a thunderstorm. + +‘A fine day, your Majesty!’ the Duchess began in a low, weak voice. + +‘Now, I give you fair warning,’ shouted the Queen, stamping on the +ground as she spoke; ‘either you or your head must be off, and that in +about half no time! Take your choice!’ + +The Duchess took her choice, and was gone in a moment. + +‘Let’s go on with the game,’ the Queen said to Alice; and Alice was +too much frightened to say a word, but slowly followed her back to the +croquet-ground. + +The other guests had taken advantage of the Queen’s absence, and were +resting in the shade: however, the moment they saw her, they hurried +back to the game, the Queen merely remarking that a moment’s delay would +cost them their lives. + +All the time they were playing the Queen never left off quarrelling with +the other players, and shouting ‘Off with his head!’ or ‘Off with her +head!’ Those whom she sentenced were taken into custody by the soldiers, +who of course had to leave off being arches to do this, so that by +the end of half an hour or so there were no arches left, and all the +players, except the King, the Queen, and Alice, were in custody and +under sentence of execution. + +Then the Queen left off, quite out of breath, and said to Alice, ‘Have +you seen the Mock Turtle yet?’ + +‘No,’ said Alice. ‘I don’t even know what a Mock Turtle is.’ + +‘It’s the thing Mock Turtle Soup is made from,’ said the Queen. + +‘I never saw one, or heard of one,’ said Alice. + +‘Come on, then,’ said the Queen, ‘and he shall tell you his history,’ + +As they walked off together, Alice heard the King say in a low voice, +to the company generally, ‘You are all pardoned.’ ‘Come, THAT’S a good +thing!’ she said to herself, for she had felt quite unhappy at the +number of executions the Queen had ordered. + +They very soon came upon a Gryphon, lying fast asleep in the sun. +(IF you don’t know what a Gryphon is, look at the picture.) ‘Up, lazy +thing!’ said the Queen, ‘and take this young lady to see the Mock +Turtle, and to hear his history. I must go back and see after some +executions I have ordered’; and she walked off, leaving Alice alone with +the Gryphon. Alice did not quite like the look of the creature, but on +the whole she thought it would be quite as safe to stay with it as to go +after that savage Queen: so she waited. + +The Gryphon sat up and rubbed its eyes: then it watched the Queen till +she was out of sight: then it chuckled. ‘What fun!’ said the Gryphon, +half to itself, half to Alice. + +‘What IS the fun?’ said Alice. + +‘Why, SHE,’ said the Gryphon. ‘It’s all her fancy, that: they never +executes nobody, you know. Come on!’ + +‘Everybody says “come on!” here,’ thought Alice, as she went slowly +after it: ‘I never was so ordered about in all my life, never!’ + +They had not gone far before they saw the Mock Turtle in the distance, +sitting sad and lonely on a little ledge of rock, and, as they came +nearer, Alice could hear him sighing as if his heart would break. She +pitied him deeply. ‘What is his sorrow?’ she asked the Gryphon, and the +Gryphon answered, very nearly in the same words as before, ‘It’s all his +fancy, that: he hasn’t got no sorrow, you know. Come on!’ + +So they went up to the Mock Turtle, who looked at them with large eyes +full of tears, but said nothing. + +‘This here young lady,’ said the Gryphon, ‘she wants for to know your +history, she do.’ + +‘I’ll tell it her,’ said the Mock Turtle in a deep, hollow tone: ‘sit +down, both of you, and don’t speak a word till I’ve finished.’ + +So they sat down, and nobody spoke for some minutes. Alice thought to +herself, ‘I don’t see how he can EVEN finish, if he doesn’t begin.’ But +she waited patiently. + +‘Once,’ said the Mock Turtle at last, with a deep sigh, ‘I was a real +Turtle.’ + +These words were followed by a very long silence, broken only by an +occasional exclamation of ‘Hjckrrh!’ from the Gryphon, and the constant +heavy sobbing of the Mock Turtle. Alice was very nearly getting up and +saying, ‘Thank you, sir, for your interesting story,’ but she could +not help thinking there MUST be more to come, so she sat still and said +nothing. + +‘When we were little,’ the Mock Turtle went on at last, more calmly, +though still sobbing a little now and then, ‘we went to school in the +sea. The master was an old Turtle--we used to call him Tortoise--’ + +‘Why did you call him Tortoise, if he wasn’t one?’ Alice asked. + +‘We called him Tortoise because he taught us,’ said the Mock Turtle +angrily: ‘really you are very dull!’ + +‘You ought to be ashamed of yourself for asking such a simple question,’ +added the Gryphon; and then they both sat silent and looked at poor +Alice, who felt ready to sink into the earth. At last the Gryphon said +to the Mock Turtle, ‘Drive on, old fellow! Don’t be all day about it!’ +and he went on in these words: + +‘Yes, we went to school in the sea, though you mayn’t believe it--’ + +‘I never said I didn’t!’ interrupted Alice. + +‘You did,’ said the Mock Turtle. + +‘Hold your tongue!’ added the Gryphon, before Alice could speak again. +The Mock Turtle went on. + +‘We had the best of educations--in fact, we went to school every day--’ + +‘I’VE been to a day-school, too,’ said Alice; ‘you needn’t be so proud +as all that.’ + +‘With extras?’ asked the Mock Turtle a little anxiously. + +‘Yes,’ said Alice, ‘we learned French and music.’ + +‘And washing?’ said the Mock Turtle. + +‘Certainly not!’ said Alice indignantly. + +‘Ah! then yours wasn’t a really good school,’ said the Mock Turtle in +a tone of great relief. ‘Now at OURS they had at the end of the bill, +“French, music, AND WASHING--extra.”’ + +‘You couldn’t have wanted it much,’ said Alice; ‘living at the bottom of +the sea.’ + +‘I couldn’t afford to learn it.’ said the Mock Turtle with a sigh. ‘I +only took the regular course.’ + +‘What was that?’ inquired Alice. + +‘Reeling and Writhing, of course, to begin with,’ the Mock Turtle +replied; ‘and then the different branches of Arithmetic--Ambition, +Distraction, Uglification, and Derision.’ + +‘I never heard of “Uglification,”’ Alice ventured to say. ‘What is it?’ + +The Gryphon lifted up both its paws in surprise. ‘What! Never heard of +uglifying!’ it exclaimed. ‘You know what to beautify is, I suppose?’ + +‘Yes,’ said Alice doubtfully: ‘it means--to--make--anything--prettier.’ + +‘Well, then,’ the Gryphon went on, ‘if you don’t know what to uglify is, +you ARE a simpleton.’ + +Alice did not feel encouraged to ask any more questions about it, so she +turned to the Mock Turtle, and said ‘What else had you to learn?’ + +‘Well, there was Mystery,’ the Mock Turtle replied, counting off +the subjects on his flappers, ‘--Mystery, ancient and modern, with +Seaography: then Drawling--the Drawling-master was an old conger-eel, +that used to come once a week: HE taught us Drawling, Stretching, and +Fainting in Coils.’ + +‘What was THAT like?’ said Alice. + +‘Well, I can’t show it you myself,’ the Mock Turtle said: ‘I’m too +stiff. And the Gryphon never learnt it.’ + +‘Hadn’t time,’ said the Gryphon: ‘I went to the Classics master, though. +He was an old crab, HE was.’ + +‘I never went to him,’ the Mock Turtle said with a sigh: ‘he taught +Laughing and Grief, they used to say.’ + +‘So he did, so he did,’ said the Gryphon, sighing in his turn; and both +creatures hid their faces in their paws. + +‘And how many hours a day did you do lessons?’ said Alice, in a hurry to +change the subject. + +‘Ten hours the first day,’ said the Mock Turtle: ‘nine the next, and so +on.’ + +‘What a curious plan!’ exclaimed Alice. + +‘That’s the reason they’re called lessons,’ the Gryphon remarked: +‘because they lessen from day to day.’ + +This was quite a new idea to Alice, and she thought it over a little +before she made her next remark. ‘Then the eleventh day must have been a +holiday?’ + +‘Of course it was,’ said the Mock Turtle. + +‘And how did you manage on the twelfth?’ Alice went on eagerly. + +‘That’s enough about lessons,’ the Gryphon interrupted in a very decided +tone: ‘tell her something about the games now.’ + + + + +CHAPTER X. The Lobster Quadrille + +The Mock Turtle sighed deeply, and drew the back of one flapper across +his eyes. He looked at Alice, and tried to speak, but for a minute or +two sobs choked his voice. ‘Same as if he had a bone in his throat,’ +said the Gryphon: and it set to work shaking him and punching him in +the back. At last the Mock Turtle recovered his voice, and, with tears +running down his cheeks, he went on again:-- + +‘You may not have lived much under the sea--’ [‘I haven’t,’ said +Alice)--‘and perhaps you were never even introduced to a lobster--’ +(Alice began to say ‘I once tasted--’ but checked herself hastily, and +said ‘No, never’) ‘--so you can have no idea what a delightful thing a +Lobster Quadrille is!’ + +‘No, indeed,’ said Alice. ‘What sort of a dance is it?’ + +‘Why,’ said the Gryphon, ‘you first form into a line along the +sea-shore--’ + +‘Two lines!’ cried the Mock Turtle. ‘Seals, turtles, salmon, and so on; +then, when you’ve cleared all the jelly-fish out of the way--’ + +‘THAT generally takes some time,’ interrupted the Gryphon. + +‘--you advance twice--’ + +‘Each with a lobster as a partner!’ cried the Gryphon. + +‘Of course,’ the Mock Turtle said: ‘advance twice, set to partners--’ + +‘--change lobsters, and retire in same order,’ continued the Gryphon. + +‘Then, you know,’ the Mock Turtle went on, ‘you throw the--’ + +‘The lobsters!’ shouted the Gryphon, with a bound into the air. + +‘--as far out to sea as you can--’ + +‘Swim after them!’ screamed the Gryphon. + +‘Turn a somersault in the sea!’ cried the Mock Turtle, capering wildly +about. + +‘Change lobsters again!’ yelled the Gryphon at the top of its voice. + +‘Back to land again, and that’s all the first figure,’ said the Mock +Turtle, suddenly dropping his voice; and the two creatures, who had been +jumping about like mad things all this time, sat down again very sadly +and quietly, and looked at Alice. + +‘It must be a very pretty dance,’ said Alice timidly. + +‘Would you like to see a little of it?’ said the Mock Turtle. + +‘Very much indeed,’ said Alice. + +‘Come, let’s try the first figure!’ said the Mock Turtle to the Gryphon. +‘We can do without lobsters, you know. Which shall sing?’ + +‘Oh, YOU sing,’ said the Gryphon. ‘I’ve forgotten the words.’ + +So they began solemnly dancing round and round Alice, every now and +then treading on her toes when they passed too close, and waving their +forepaws to mark the time, while the Mock Turtle sang this, very slowly +and sadly:-- + + ‘“Will you walk a little faster?” said a whiting to a snail. + “There’s a porpoise close behind us, and he’s treading on my tail. + + See how eagerly the lobsters and the turtles all advance! + They are waiting on the shingle--will you come and join the dance? + + Will you, won’t you, will you, won’t you, will you join the dance? + Will you, won’t you, will you, won’t you, won’t you join the dance? + + “You can really have no notion how delightful it will be + When they take us up and throw us, with the lobsters, out to sea!” + But the snail replied “Too far, too far!” and gave a look askance-- + Said he thanked the whiting kindly, but he would not join the dance. + + Would not, could not, would not, could not, would not join the dance. + Would not, could not, would not, could not, could not join the dance. + + ‘“What matters it how far we go?” his scaly friend replied. + “There is another shore, you know, upon the other side. + The further off from England the nearer is to France-- + Then turn not pale, beloved snail, but come and join the dance. + + Will you, won’t you, will you, won’t you, will you join the dance? + Will you, won’t you, will you, won’t you, won’t you join the dance?”’ + +‘Thank you, it’s a very interesting dance to watch,’ said Alice, feeling +very glad that it was over at last: ‘and I do so like that curious song +about the whiting!’ + +‘Oh, as to the whiting,’ said the Mock Turtle, ‘they--you’ve seen them, +of course?’ + +‘Yes,’ said Alice, ‘I’ve often seen them at dinn--’ she checked herself +hastily. + +‘I don’t know where Dinn may be,’ said the Mock Turtle, ‘but if you’ve +seen them so often, of course you know what they’re like.’ + +‘I believe so,’ Alice replied thoughtfully. ‘They have their tails in +their mouths--and they’re all over crumbs.’ + +‘You’re wrong about the crumbs,’ said the Mock Turtle: ‘crumbs would all +wash off in the sea. But they HAVE their tails in their mouths; and the +reason is--’ here the Mock Turtle yawned and shut his eyes.--‘Tell her +about the reason and all that,’ he said to the Gryphon. + +‘The reason is,’ said the Gryphon, ‘that they WOULD go with the lobsters +to the dance. So they got thrown out to sea. So they had to fall a long +way. So they got their tails fast in their mouths. So they couldn’t get +them out again. That’s all.’ + +‘Thank you,’ said Alice, ‘it’s very interesting. I never knew so much +about a whiting before.’ + +‘I can tell you more than that, if you like,’ said the Gryphon. ‘Do you +know why it’s called a whiting?’ + +‘I never thought about it,’ said Alice. ‘Why?’ + +‘IT DOES THE BOOTS AND SHOES.’ the Gryphon replied very solemnly. + +Alice was thoroughly puzzled. ‘Does the boots and shoes!’ she repeated +in a wondering tone. + +‘Why, what are YOUR shoes done with?’ said the Gryphon. ‘I mean, what +makes them so shiny?’ + +Alice looked down at them, and considered a little before she gave her +answer. ‘They’re done with blacking, I believe.’ + +‘Boots and shoes under the sea,’ the Gryphon went on in a deep voice, +‘are done with a whiting. Now you know.’ + +‘And what are they made of?’ Alice asked in a tone of great curiosity. + +‘Soles and eels, of course,’ the Gryphon replied rather impatiently: +‘any shrimp could have told you that.’ + +‘If I’d been the whiting,’ said Alice, whose thoughts were still running +on the song, ‘I’d have said to the porpoise, “Keep back, please: we +don’t want YOU with us!”’ + +‘They were obliged to have him with them,’ the Mock Turtle said: ‘no +wise fish would go anywhere without a porpoise.’ + +‘Wouldn’t it really?’ said Alice in a tone of great surprise. + +‘Of course not,’ said the Mock Turtle: ‘why, if a fish came to ME, and +told me he was going a journey, I should say “With what porpoise?”’ + +‘Don’t you mean “purpose”?’ said Alice. + +‘I mean what I say,’ the Mock Turtle replied in an offended tone. And +the Gryphon added ‘Come, let’s hear some of YOUR adventures.’ + +‘I could tell you my adventures--beginning from this morning,’ said +Alice a little timidly: ‘but it’s no use going back to yesterday, +because I was a different person then.’ + +‘Explain all that,’ said the Mock Turtle. + +‘No, no! The adventures first,’ said the Gryphon in an impatient tone: +‘explanations take such a dreadful time.’ + +So Alice began telling them her adventures from the time when she first +saw the White Rabbit. She was a little nervous about it just at first, +the two creatures got so close to her, one on each side, and opened +their eyes and mouths so VERY wide, but she gained courage as she went +on. Her listeners were perfectly quiet till she got to the part about +her repeating ‘YOU ARE OLD, FATHER WILLIAM,’ to the Caterpillar, and the +words all coming different, and then the Mock Turtle drew a long breath, +and said ‘That’s very curious.’ + +‘It’s all about as curious as it can be,’ said the Gryphon. + +‘It all came different!’ the Mock Turtle repeated thoughtfully. ‘I +should like to hear her try and repeat something now. Tell her to +begin.’ He looked at the Gryphon as if he thought it had some kind of +authority over Alice. + +‘Stand up and repeat “‘TIS THE VOICE OF THE SLUGGARD,”’ said the +Gryphon. + +‘How the creatures order one about, and make one repeat lessons!’ +thought Alice; ‘I might as well be at school at once.’ However, she +got up, and began to repeat it, but her head was so full of the Lobster +Quadrille, that she hardly knew what she was saying, and the words came +very queer indeed:-- + + ‘’Tis the voice of the Lobster; I heard him declare, + “You have baked me too brown, I must sugar my hair.” + As a duck with its eyelids, so he with his nose + Trims his belt and his buttons, and turns out his toes.’ + + [later editions continued as follows + When the sands are all dry, he is gay as a lark, + And will talk in contemptuous tones of the Shark, + But, when the tide rises and sharks are around, + His voice has a timid and tremulous sound.] + +‘That’s different from what I used to say when I was a child,’ said the +Gryphon. + +‘Well, I never heard it before,’ said the Mock Turtle; ‘but it sounds +uncommon nonsense.’ + +Alice said nothing; she had sat down with her face in her hands, +wondering if anything would EVER happen in a natural way again. + +‘I should like to have it explained,’ said the Mock Turtle. + +‘She can’t explain it,’ said the Gryphon hastily. ‘Go on with the next +verse.’ + +‘But about his toes?’ the Mock Turtle persisted. ‘How COULD he turn them +out with his nose, you know?’ + +‘It’s the first position in dancing.’ Alice said; but was dreadfully +puzzled by the whole thing, and longed to change the subject. + +‘Go on with the next verse,’ the Gryphon repeated impatiently: ‘it +begins “I passed by his garden.”’ + +Alice did not dare to disobey, though she felt sure it would all come +wrong, and she went on in a trembling voice:-- + + ‘I passed by his garden, and marked, with one eye, + How the Owl and the Panther were sharing a pie--’ + + [later editions continued as follows + The Panther took pie-crust, and gravy, and meat, + While the Owl had the dish as its share of the treat. + When the pie was all finished, the Owl, as a boon, + Was kindly permitted to pocket the spoon: + While the Panther received knife and fork with a growl, + And concluded the banquet--] + +‘What IS the use of repeating all that stuff,’ the Mock Turtle +interrupted, ‘if you don’t explain it as you go on? It’s by far the most +confusing thing I ever heard!’ + +‘Yes, I think you’d better leave off,’ said the Gryphon: and Alice was +only too glad to do so. + +‘Shall we try another figure of the Lobster Quadrille?’ the Gryphon went +on. ‘Or would you like the Mock Turtle to sing you a song?’ + +‘Oh, a song, please, if the Mock Turtle would be so kind,’ Alice +replied, so eagerly that the Gryphon said, in a rather offended tone, +‘Hm! No accounting for tastes! Sing her “Turtle Soup,” will you, old +fellow?’ + +The Mock Turtle sighed deeply, and began, in a voice sometimes choked +with sobs, to sing this:-- + + ‘Beautiful Soup, so rich and green, + Waiting in a hot tureen! + Who for such dainties would not stoop? + Soup of the evening, beautiful Soup! + Soup of the evening, beautiful Soup! + Beau--ootiful Soo--oop! + Beau--ootiful Soo--oop! + Soo--oop of the e--e--evening, + Beautiful, beautiful Soup! + + ‘Beautiful Soup! Who cares for fish, + Game, or any other dish? + Who would not give all else for two + Pennyworth only of beautiful Soup? + Pennyworth only of beautiful Soup? + Beau--ootiful Soo--oop! + Beau--ootiful Soo--oop! + Soo--oop of the e--e--evening, + Beautiful, beauti--FUL SOUP!’ + +‘Chorus again!’ cried the Gryphon, and the Mock Turtle had just begun +to repeat it, when a cry of ‘The trial’s beginning!’ was heard in the +distance. + +‘Come on!’ cried the Gryphon, and, taking Alice by the hand, it hurried +off, without waiting for the end of the song. + +‘What trial is it?’ Alice panted as she ran; but the Gryphon only +answered ‘Come on!’ and ran the faster, while more and more faintly +came, carried on the breeze that followed them, the melancholy words:-- + + ‘Soo--oop of the e--e--evening, + Beautiful, beautiful Soup!’ + + + + +CHAPTER XI. Who Stole the Tarts? + +The King and Queen of Hearts were seated on their throne when they +arrived, with a great crowd assembled about them--all sorts of little +birds and beasts, as well as the whole pack of cards: the Knave was +standing before them, in chains, with a soldier on each side to guard +him; and near the King was the White Rabbit, with a trumpet in one hand, +and a scroll of parchment in the other. In the very middle of the court +was a table, with a large dish of tarts upon it: they looked so good, +that it made Alice quite hungry to look at them--‘I wish they’d get the +trial done,’ she thought, ‘and hand round the refreshments!’ But there +seemed to be no chance of this, so she began looking at everything about +her, to pass away the time. + +Alice had never been in a court of justice before, but she had read +about them in books, and she was quite pleased to find that she knew +the name of nearly everything there. ‘That’s the judge,’ she said to +herself, ‘because of his great wig.’ + +The judge, by the way, was the King; and as he wore his crown over the +wig, (look at the frontispiece if you want to see how he did it,) he did +not look at all comfortable, and it was certainly not becoming. + +‘And that’s the jury-box,’ thought Alice, ‘and those twelve creatures,’ +(she was obliged to say ‘creatures,’ you see, because some of them were +animals, and some were birds,) ‘I suppose they are the jurors.’ She said +this last word two or three times over to herself, being rather proud of +it: for she thought, and rightly too, that very few little girls of her +age knew the meaning of it at all. However, ‘jury-men’ would have done +just as well. + +The twelve jurors were all writing very busily on slates. ‘What are they +doing?’ Alice whispered to the Gryphon. ‘They can’t have anything to put +down yet, before the trial’s begun.’ + +‘They’re putting down their names,’ the Gryphon whispered in reply, ‘for +fear they should forget them before the end of the trial.’ + +‘Stupid things!’ Alice began in a loud, indignant voice, but she stopped +hastily, for the White Rabbit cried out, ‘Silence in the court!’ and the +King put on his spectacles and looked anxiously round, to make out who +was talking. + +Alice could see, as well as if she were looking over their shoulders, +that all the jurors were writing down ‘stupid things!’ on their slates, +and she could even make out that one of them didn’t know how to spell +‘stupid,’ and that he had to ask his neighbour to tell him. ‘A nice +muddle their slates’ll be in before the trial’s over!’ thought Alice. + +One of the jurors had a pencil that squeaked. This of course, Alice +could not stand, and she went round the court and got behind him, and +very soon found an opportunity of taking it away. She did it so quickly +that the poor little juror (it was Bill, the Lizard) could not make out +at all what had become of it; so, after hunting all about for it, he was +obliged to write with one finger for the rest of the day; and this was +of very little use, as it left no mark on the slate. + +‘Herald, read the accusation!’ said the King. + +On this the White Rabbit blew three blasts on the trumpet, and then +unrolled the parchment scroll, and read as follows:-- + + ‘The Queen of Hearts, she made some tarts, + All on a summer day: + The Knave of Hearts, he stole those tarts, + And took them quite away!’ + +‘Consider your verdict,’ the King said to the jury. + +‘Not yet, not yet!’ the Rabbit hastily interrupted. ‘There’s a great +deal to come before that!’ + +‘Call the first witness,’ said the King; and the White Rabbit blew three +blasts on the trumpet, and called out, ‘First witness!’ + +The first witness was the Hatter. He came in with a teacup in one +hand and a piece of bread-and-butter in the other. ‘I beg pardon, your +Majesty,’ he began, ‘for bringing these in: but I hadn’t quite finished +my tea when I was sent for.’ + +‘You ought to have finished,’ said the King. ‘When did you begin?’ + +The Hatter looked at the March Hare, who had followed him into the +court, arm-in-arm with the Dormouse. ‘Fourteenth of March, I think it +was,’ he said. + +‘Fifteenth,’ said the March Hare. + +‘Sixteenth,’ added the Dormouse. + +‘Write that down,’ the King said to the jury, and the jury eagerly +wrote down all three dates on their slates, and then added them up, and +reduced the answer to shillings and pence. + +‘Take off your hat,’ the King said to the Hatter. + +‘It isn’t mine,’ said the Hatter. + +‘Stolen!’ the King exclaimed, turning to the jury, who instantly made a +memorandum of the fact. + +‘I keep them to sell,’ the Hatter added as an explanation; ‘I’ve none of +my own. I’m a hatter.’ + +Here the Queen put on her spectacles, and began staring at the Hatter, +who turned pale and fidgeted. + +‘Give your evidence,’ said the King; ‘and don’t be nervous, or I’ll have +you executed on the spot.’ + +This did not seem to encourage the witness at all: he kept shifting +from one foot to the other, looking uneasily at the Queen, and in +his confusion he bit a large piece out of his teacup instead of the +bread-and-butter. + +Just at this moment Alice felt a very curious sensation, which puzzled +her a good deal until she made out what it was: she was beginning to +grow larger again, and she thought at first she would get up and leave +the court; but on second thoughts she decided to remain where she was as +long as there was room for her. + +‘I wish you wouldn’t squeeze so.’ said the Dormouse, who was sitting +next to her. ‘I can hardly breathe.’ + +‘I can’t help it,’ said Alice very meekly: ‘I’m growing.’ + +‘You’ve no right to grow here,’ said the Dormouse. + +‘Don’t talk nonsense,’ said Alice more boldly: ‘you know you’re growing +too.’ + +‘Yes, but I grow at a reasonable pace,’ said the Dormouse: ‘not in that +ridiculous fashion.’ And he got up very sulkily and crossed over to the +other side of the court. + +All this time the Queen had never left off staring at the Hatter, and, +just as the Dormouse crossed the court, she said to one of the officers +of the court, ‘Bring me the list of the singers in the last concert!’ on +which the wretched Hatter trembled so, that he shook both his shoes off. + +‘Give your evidence,’ the King repeated angrily, ‘or I’ll have you +executed, whether you’re nervous or not.’ + +‘I’m a poor man, your Majesty,’ the Hatter began, in a trembling voice, +‘--and I hadn’t begun my tea--not above a week or so--and what with the +bread-and-butter getting so thin--and the twinkling of the tea--’ + +‘The twinkling of the what?’ said the King. + +‘It began with the tea,’ the Hatter replied. + +‘Of course twinkling begins with a T!’ said the King sharply. ‘Do you +take me for a dunce? Go on!’ + +‘I’m a poor man,’ the Hatter went on, ‘and most things twinkled after +that--only the March Hare said--’ + +‘I didn’t!’ the March Hare interrupted in a great hurry. + +‘You did!’ said the Hatter. + +‘I deny it!’ said the March Hare. + +‘He denies it,’ said the King: ‘leave out that part.’ + +‘Well, at any rate, the Dormouse said--’ the Hatter went on, looking +anxiously round to see if he would deny it too: but the Dormouse denied +nothing, being fast asleep. + +‘After that,’ continued the Hatter, ‘I cut some more bread-and-butter--’ + +‘But what did the Dormouse say?’ one of the jury asked. + +‘That I can’t remember,’ said the Hatter. + +‘You MUST remember,’ remarked the King, ‘or I’ll have you executed.’ + +The miserable Hatter dropped his teacup and bread-and-butter, and went +down on one knee. ‘I’m a poor man, your Majesty,’ he began. + +‘You’re a very poor speaker,’ said the King. + +Here one of the guinea-pigs cheered, and was immediately suppressed by +the officers of the court. (As that is rather a hard word, I will just +explain to you how it was done. They had a large canvas bag, which tied +up at the mouth with strings: into this they slipped the guinea-pig, +head first, and then sat upon it.) + +‘I’m glad I’ve seen that done,’ thought Alice. ‘I’ve so often read +in the newspapers, at the end of trials, “There was some attempts +at applause, which was immediately suppressed by the officers of the +court,” and I never understood what it meant till now.’ + +‘If that’s all you know about it, you may stand down,’ continued the +King. + +‘I can’t go no lower,’ said the Hatter: ‘I’m on the floor, as it is.’ + +‘Then you may SIT down,’ the King replied. + +Here the other guinea-pig cheered, and was suppressed. + +‘Come, that finished the guinea-pigs!’ thought Alice. ‘Now we shall get +on better.’ + +‘I’d rather finish my tea,’ said the Hatter, with an anxious look at the +Queen, who was reading the list of singers. + +‘You may go,’ said the King, and the Hatter hurriedly left the court, +without even waiting to put his shoes on. + +‘--and just take his head off outside,’ the Queen added to one of the +officers: but the Hatter was out of sight before the officer could get +to the door. + +‘Call the next witness!’ said the King. + +The next witness was the Duchess’s cook. She carried the pepper-box in +her hand, and Alice guessed who it was, even before she got into the +court, by the way the people near the door began sneezing all at once. + +‘Give your evidence,’ said the King. + +‘Shan’t,’ said the cook. + +The King looked anxiously at the White Rabbit, who said in a low voice, +‘Your Majesty must cross-examine THIS witness.’ + +‘Well, if I must, I must,’ the King said, with a melancholy air, and, +after folding his arms and frowning at the cook till his eyes were +nearly out of sight, he said in a deep voice, ‘What are tarts made of?’ + +‘Pepper, mostly,’ said the cook. + +‘Treacle,’ said a sleepy voice behind her. + +‘Collar that Dormouse,’ the Queen shrieked out. ‘Behead that Dormouse! +Turn that Dormouse out of court! Suppress him! Pinch him! Off with his +whiskers!’ + +For some minutes the whole court was in confusion, getting the Dormouse +turned out, and, by the time they had settled down again, the cook had +disappeared. + +‘Never mind!’ said the King, with an air of great relief. ‘Call the next +witness.’ And he added in an undertone to the Queen, ‘Really, my dear, +YOU must cross-examine the next witness. It quite makes my forehead +ache!’ + +Alice watched the White Rabbit as he fumbled over the list, feeling very +curious to see what the next witness would be like, ‘--for they haven’t +got much evidence YET,’ she said to herself. Imagine her surprise, when +the White Rabbit read out, at the top of his shrill little voice, the +name ‘Alice!’ + + + + +CHAPTER XII. Alice’s Evidence + + +‘Here!’ cried Alice, quite forgetting in the flurry of the moment how +large she had grown in the last few minutes, and she jumped up in such +a hurry that she tipped over the jury-box with the edge of her skirt, +upsetting all the jurymen on to the heads of the crowd below, and there +they lay sprawling about, reminding her very much of a globe of goldfish +she had accidentally upset the week before. + +‘Oh, I BEG your pardon!’ she exclaimed in a tone of great dismay, and +began picking them up again as quickly as she could, for the accident of +the goldfish kept running in her head, and she had a vague sort of idea +that they must be collected at once and put back into the jury-box, or +they would die. + +‘The trial cannot proceed,’ said the King in a very grave voice, ‘until +all the jurymen are back in their proper places--ALL,’ he repeated with +great emphasis, looking hard at Alice as he said do. + +Alice looked at the jury-box, and saw that, in her haste, she had put +the Lizard in head downwards, and the poor little thing was waving its +tail about in a melancholy way, being quite unable to move. She soon got +it out again, and put it right; ‘not that it signifies much,’ she said +to herself; ‘I should think it would be QUITE as much use in the trial +one way up as the other.’ + +As soon as the jury had a little recovered from the shock of being +upset, and their slates and pencils had been found and handed back to +them, they set to work very diligently to write out a history of the +accident, all except the Lizard, who seemed too much overcome to do +anything but sit with its mouth open, gazing up into the roof of the +court. + +‘What do you know about this business?’ the King said to Alice. + +‘Nothing,’ said Alice. + +‘Nothing WHATEVER?’ persisted the King. + +‘Nothing whatever,’ said Alice. + +‘That’s very important,’ the King said, turning to the jury. They were +just beginning to write this down on their slates, when the White Rabbit +interrupted: ‘UNimportant, your Majesty means, of course,’ he said in a +very respectful tone, but frowning and making faces at him as he spoke. + +‘UNimportant, of course, I meant,’ the King hastily said, and went on +to himself in an undertone, + +‘important--unimportant--unimportant--important--’ as if he were trying +which word sounded best. + +Some of the jury wrote it down ‘important,’ and some ‘unimportant.’ +Alice could see this, as she was near enough to look over their slates; +‘but it doesn’t matter a bit,’ she thought to herself. + +At this moment the King, who had been for some time busily writing in +his note-book, cackled out ‘Silence!’ and read out from his book, ‘Rule +Forty-two. ALL PERSONS MORE THAN A MILE HIGH TO LEAVE THE COURT.’ + +Everybody looked at Alice. + +‘I’M not a mile high,’ said Alice. + +‘You are,’ said the King. + +‘Nearly two miles high,’ added the Queen. + +‘Well, I shan’t go, at any rate,’ said Alice: ‘besides, that’s not a +regular rule: you invented it just now.’ + +‘It’s the oldest rule in the book,’ said the King. + +‘Then it ought to be Number One,’ said Alice. + +The King turned pale, and shut his note-book hastily. ‘Consider your +verdict,’ he said to the jury, in a low, trembling voice. + +‘There’s more evidence to come yet, please your Majesty,’ said the White +Rabbit, jumping up in a great hurry; ‘this paper has just been picked +up.’ + +‘What’s in it?’ said the Queen. + +‘I haven’t opened it yet,’ said the White Rabbit, ‘but it seems to be a +letter, written by the prisoner to--to somebody.’ + +‘It must have been that,’ said the King, ‘unless it was written to +nobody, which isn’t usual, you know.’ + +‘Who is it directed to?’ said one of the jurymen. + +‘It isn’t directed at all,’ said the White Rabbit; ‘in fact, there’s +nothing written on the OUTSIDE.’ He unfolded the paper as he spoke, and +added ‘It isn’t a letter, after all: it’s a set of verses.’ + +‘Are they in the prisoner’s handwriting?’ asked another of the jurymen. + +‘No, they’re not,’ said the White Rabbit, ‘and that’s the queerest thing +about it.’ (The jury all looked puzzled.) + +‘He must have imitated somebody else’s hand,’ said the King. (The jury +all brightened up again.) + +‘Please your Majesty,’ said the Knave, ‘I didn’t write it, and they +can’t prove I did: there’s no name signed at the end.’ + +‘If you didn’t sign it,’ said the King, ‘that only makes the matter +worse. You MUST have meant some mischief, or else you’d have signed your +name like an honest man.’ + +There was a general clapping of hands at this: it was the first really +clever thing the King had said that day. + +‘That PROVES his guilt,’ said the Queen. + +‘It proves nothing of the sort!’ said Alice. ‘Why, you don’t even know +what they’re about!’ + +‘Read them,’ said the King. + +The White Rabbit put on his spectacles. ‘Where shall I begin, please +your Majesty?’ he asked. + +‘Begin at the beginning,’ the King said gravely, ‘and go on till you +come to the end: then stop.’ + +These were the verses the White Rabbit read:-- + + ‘They told me you had been to her, + And mentioned me to him: + She gave me a good character, + But said I could not swim. + + He sent them word I had not gone + (We know it to be true): + If she should push the matter on, + What would become of you? + + I gave her one, they gave him two, + You gave us three or more; + They all returned from him to you, + Though they were mine before. + + If I or she should chance to be + Involved in this affair, + He trusts to you to set them free, + Exactly as we were. + + My notion was that you had been + (Before she had this fit) + An obstacle that came between + Him, and ourselves, and it. + + Don’t let him know she liked them best, + For this must ever be + A secret, kept from all the rest, + Between yourself and me.’ + +‘That’s the most important piece of evidence we’ve heard yet,’ said the +King, rubbing his hands; ‘so now let the jury--’ + +‘If any one of them can explain it,’ said Alice, (she had grown so large +in the last few minutes that she wasn’t a bit afraid of interrupting +him,) ‘I’ll give him sixpence. _I_ don’t believe there’s an atom of +meaning in it.’ + +The jury all wrote down on their slates, ‘SHE doesn’t believe there’s an +atom of meaning in it,’ but none of them attempted to explain the paper. + +‘If there’s no meaning in it,’ said the King, ‘that saves a world of +trouble, you know, as we needn’t try to find any. And yet I don’t know,’ +he went on, spreading out the verses on his knee, and looking at them +with one eye; ‘I seem to see some meaning in them, after all. “--SAID +I COULD NOT SWIM--” you can’t swim, can you?’ he added, turning to the +Knave. + +The Knave shook his head sadly. ‘Do I look like it?’ he said. (Which he +certainly did NOT, being made entirely of cardboard.) + +‘All right, so far,’ said the King, and he went on muttering over +the verses to himself: ‘“WE KNOW IT TO BE TRUE--” that’s the jury, of +course--“I GAVE HER ONE, THEY GAVE HIM TWO--” why, that must be what he +did with the tarts, you know--’ + +‘But, it goes on “THEY ALL RETURNED FROM HIM TO YOU,”’ said Alice. + +‘Why, there they are!’ said the King triumphantly, pointing to the tarts +on the table. ‘Nothing can be clearer than THAT. Then again--“BEFORE SHE +HAD THIS FIT--” you never had fits, my dear, I think?’ he said to the +Queen. + +‘Never!’ said the Queen furiously, throwing an inkstand at the Lizard +as she spoke. (The unfortunate little Bill had left off writing on his +slate with one finger, as he found it made no mark; but he now hastily +began again, using the ink, that was trickling down his face, as long as +it lasted.) + +‘Then the words don’t FIT you,’ said the King, looking round the court +with a smile. There was a dead silence. + +‘It’s a pun!’ the King added in an offended tone, and everybody laughed, +‘Let the jury consider their verdict,’ the King said, for about the +twentieth time that day. + +‘No, no!’ said the Queen. ‘Sentence first--verdict afterwards.’ + +‘Stuff and nonsense!’ said Alice loudly. ‘The idea of having the +sentence first!’ + +‘Hold your tongue!’ said the Queen, turning purple. + +‘I won’t!’ said Alice. + +‘Off with her head!’ the Queen shouted at the top of her voice. Nobody +moved. + +‘Who cares for you?’ said Alice, (she had grown to her full size by this +time.) ‘You’re nothing but a pack of cards!’ + +At this the whole pack rose up into the air, and came flying down upon +her: she gave a little scream, half of fright and half of anger, and +tried to beat them off, and found herself lying on the bank, with her +head in the lap of her sister, who was gently brushing away some dead +leaves that had fluttered down from the trees upon her face. + +‘Wake up, Alice dear!’ said her sister; ‘Why, what a long sleep you’ve +had!’ + +‘Oh, I’ve had such a curious dream!’ said Alice, and she told her +sister, as well as she could remember them, all these strange Adventures +of hers that you have just been reading about; and when she had +finished, her sister kissed her, and said, ‘It WAS a curious dream, +dear, certainly: but now run in to your tea; it’s getting late.’ So +Alice got up and ran off, thinking while she ran, as well she might, +what a wonderful dream it had been. + +But her sister sat still just as she left her, leaning her head on her +hand, watching the setting sun, and thinking of little Alice and all her +wonderful Adventures, till she too began dreaming after a fashion, and +this was her dream:-- + +First, she dreamed of little Alice herself, and once again the tiny +hands were clasped upon her knee, and the bright eager eyes were looking +up into hers--she could hear the very tones of her voice, and see that +queer little toss of her head to keep back the wandering hair that +WOULD always get into her eyes--and still as she listened, or seemed to +listen, the whole place around her became alive with the strange creatures +of her little sister’s dream. + +The long grass rustled at her feet as the White Rabbit hurried by--the +frightened Mouse splashed his way through the neighbouring pool--she +could hear the rattle of the teacups as the March Hare and his friends +shared their never-ending meal, and the shrill voice of the Queen +ordering off her unfortunate guests to execution--once more the pig-baby +was sneezing on the Duchess’s knee, while plates and dishes crashed +around it--once more the shriek of the Gryphon, the squeaking of the +Lizard’s slate-pencil, and the choking of the suppressed guinea-pigs, +filled the air, mixed up with the distant sobs of the miserable Mock +Turtle. + +So she sat on, with closed eyes, and half believed herself in +Wonderland, though she knew she had but to open them again, and all +would change to dull reality--the grass would be only rustling in the +wind, and the pool rippling to the waving of the reeds--the rattling +teacups would change to tinkling sheep-bells, and the Queen’s shrill +cries to the voice of the shepherd boy--and the sneeze of the baby, the +shriek of the Gryphon, and all the other queer noises, would change (she +knew) to the confused clamour of the busy farm-yard--while the lowing +of the cattle in the distance would take the place of the Mock Turtle’s +heavy sobs. + +Lastly, she pictured to herself how this same little sister of hers +would, in the after-time, be herself a grown woman; and how she would +keep, through all her riper years, the simple and loving heart of her +childhood: and how she would gather about her other little children, and +make THEIR eyes bright and eager with many a strange tale, perhaps even +with the dream of Wonderland of long ago: and how she would feel with +all their simple sorrows, and find a pleasure in all their simple joys, +remembering her own child-life, and the happy summer days. + + THE END + + + + + +End of Project Gutenberg’s Alice’s Adventures in Wonderland, by Lewis Carroll + +*** END OF THIS PROJECT GUTENBERG EBOOK ALICE’S ADVENTURES IN WONDERLAND *** + +***** This file should be named 11-0.txt or 11-0.zip ***** +This and all associated files of various formats will be found in: + http://www.gutenberg.org/1/11/ + + + +Updated editions will replace the previous one--the old editions +will be renamed. + +Creating the works from public domain print editions means that no +one owns a United States copyright in these works, so the Foundation +(and you!) can copy and distribute it in the United States without +permission and without paying copyright royalties. Special rules, +set forth in the General Terms of Use part of this license, apply to +copying and distributing Project Gutenberg-tm electronic works to +protect the PROJECT GUTENBERG-tm concept and trademark. Project +Gutenberg is a registered trademark, and may not be used if you +charge for the eBooks, unless you receive specific permission. If you +do not charge anything for copies of this eBook, complying with the +rules is very easy. You may use this eBook for nearly any purpose +such as creation of derivative works, reports, performances and +research. They may be modified and printed and given away--you may do +practically ANYTHING with public domain eBooks. Redistribution is +subject to the trademark license, especially commercial +redistribution. + + + +*** START: FULL LICENSE *** + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase “Project +Gutenberg”), you agree to comply with all the terms of the Full Project +Gutenberg-tm License (available with this file or online at +http://gutenberg.org/license). + + +Section 1. General Terms of Use and Redistributing Project Gutenberg-tm +electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or destroy +all copies of Project Gutenberg-tm electronic works in your possession. +If you paid a fee for obtaining a copy of or access to a Project +Gutenberg-tm electronic work and you do not agree to be bound by the +terms of this agreement, you may obtain a refund from the person or +entity to whom you paid the fee as set forth in paragraph 1.E.8. + +1.B. “Project Gutenberg” is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this agreement +and help preserve free future access to Project Gutenberg-tm electronic +works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation (“the Foundation” + or PGLAF), owns a compilation copyright in the collection of Project +Gutenberg-tm electronic works. Nearly all the individual works in the +collection are in the public domain in the United States. If an +individual work is in the public domain in the United States and you are +located in the United States, we do not claim a right to prevent you from +copying, distributing, performing, displaying or creating derivative +works based on the work as long as all references to Project Gutenberg +are removed. Of course, we hope that you will support the Project +Gutenberg-tm mission of promoting free access to electronic works by +freely sharing Project Gutenberg-tm works in compliance with the terms of +this agreement for keeping the Project Gutenberg-tm name associated with +the work. You can easily comply with the terms of this agreement by +keeping this work in the same format with its attached full Project +Gutenberg-tm License when you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are in +a constant state of change. If you are outside the United States, check +the laws of your country in addition to the terms of this agreement +before downloading, copying, displaying, performing, distributing or +creating derivative works based on this work or any other Project +Gutenberg-tm work. The Foundation makes no representations concerning +the copyright status of any work in any country outside the United +States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other immediate +access to, the full Project Gutenberg-tm License must appear prominently +whenever any copy of a Project Gutenberg-tm work (any work on which the +phrase “Project Gutenberg” appears, or with which the phrase “Project +Gutenberg” is associated) is accessed, displayed, performed, viewed, +copied or distributed: + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.org + +1.E.2. If an individual Project Gutenberg-tm electronic work is derived +from the public domain (does not contain a notice indicating that it is +posted with permission of the copyright holder), the work can be copied +and distributed to anyone in the United States without paying any fees +or charges. If you are redistributing or providing access to a work +with the phrase “Project Gutenberg” associated with or appearing on the +work, you must comply either with the requirements of paragraphs 1.E.1 +through 1.E.7 or obtain permission for the use of the work and the +Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or +1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any additional +terms imposed by the copyright holder. Additional terms will be linked +to the Project Gutenberg-tm License for all works posted with the +permission of the copyright holder found at the beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including any +word processing or hypertext form. However, if you provide access to or +distribute copies of a Project Gutenberg-tm work in a format other than +“Plain Vanilla ASCII” or other format used in the official version +posted on the official Project Gutenberg-tm web site (www.gutenberg.org), +you must, at no additional cost, fee or expense to the user, provide a +copy, a means of exporting a copy, or a means of obtaining a copy upon +request, of the work in its original “Plain Vanilla ASCII” or other +form. Any alternate format must include the full Project Gutenberg-tm +License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works provided +that + +- You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is + owed to the owner of the Project Gutenberg-tm trademark, but he + has agreed to donate royalties under this paragraph to the + Project Gutenberg Literary Archive Foundation. Royalty payments + must be paid within 60 days following each date on which you + prepare (or are legally required to prepare) your periodic tax + returns. Royalty payments should be clearly marked as such and + sent to the Project Gutenberg Literary Archive Foundation at the + address specified in Section 4, “Information about donations to + the Project Gutenberg Literary Archive Foundation.” + +- You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or + destroy all copies of the works possessed in a physical medium + and discontinue all use of and all access to other copies of + Project Gutenberg-tm works. + +- You provide, in accordance with paragraph 1.F.3, a full refund of any + money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days + of receipt of the work. + +- You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm +electronic work or group of works on different terms than are set +forth in this agreement, you must obtain permission in writing from +both the Project Gutenberg Literary Archive Foundation and Michael +Hart, the owner of the Project Gutenberg-tm trademark. Contact the +Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +public domain works in creating the Project Gutenberg-tm +collection. Despite these efforts, Project Gutenberg-tm electronic +works, and the medium on which they may be stored, may contain +“Defects,” such as, but not limited to, incomplete, inaccurate or +corrupt data, transcription errors, a copyright or other intellectual +property infringement, a defective or damaged disk or other medium, a +computer virus, or computer codes that damage or cannot be read by +your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the “Right +of Replacement or Refund” described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium with +your written explanation. The person or entity that provided you with +the defective work may elect to provide a replacement copy in lieu of a +refund. If you received the work electronically, the person or entity +providing it to you may choose to give you a second opportunity to +receive the work electronically in lieu of a refund. If the second copy +is also defective, you may demand a refund in writing without further +opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you ‘AS-IS’ WITH NO OTHER +WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of damages. +If any disclaimer or limitation set forth in this agreement violates the +law of the state applicable to this agreement, the agreement shall be +interpreted to make the maximum disclaimer or limitation permitted by +the applicable state law. The invalidity or unenforceability of any +provision of this agreement shall not void the remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in accordance +with this agreement, and any volunteers associated with the production, +promotion and distribution of Project Gutenberg-tm electronic works, +harmless from all liability, costs and expenses, including legal fees, +that arise directly or indirectly from any of the following which you do +or cause to occur: (a) distribution of this or any Project Gutenberg-tm +work, (b) alteration, modification, or additions or deletions to any +Project Gutenberg-tm work, and (c) any Defect you cause. + + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of computers +including obsolete, old, middle-aged and new computers. It exists +because of the efforts of hundreds of volunteers and donations from +people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need, is critical to reaching Project Gutenberg-tm’s +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future generations. +To learn more about the Project Gutenberg Literary Archive Foundation +and how your efforts and donations can help, see Sections 3 and 4 +and the Foundation web page at http://www.pglaf.org. + + +Section 3. Information about the Project Gutenberg Literary Archive +Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation’s EIN or federal tax identification +number is 64-6221541. Its 501(c)(3) letter is posted at +http://pglaf.org/fundraising. Contributions to the Project Gutenberg +Literary Archive Foundation are tax deductible to the full extent +permitted by U.S. federal laws and your state’s laws. + +The Foundation’s principal office is located at 4557 Melan Dr. S. +Fairbanks, AK, 99712., but its volunteers and employees are scattered +throughout numerous locations. Its business office is located at +809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email +business@pglaf.org. Email contact links and up to date contact +information can be found at the Foundation’s web site and official +page at http://pglaf.org + +For additional contact information: + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To +SEND DONATIONS or determine the status of compliance for any +particular state visit http://pglaf.org + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including checks, online payments and credit card donations. +To donate, please visit: http://pglaf.org/donate + + +Section 5. General Information About Project Gutenberg-tm electronic +works. + +Professor Michael S. Hart is the originator of the Project Gutenberg-tm +concept of a library of electronic works that could be freely shared +with anyone. For thirty years, he produced and distributed Project +Gutenberg-tm eBooks with only a loose network of volunteer support. + + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as Public Domain in the U.S. +unless a copyright notice is included. Thus, we do not necessarily +keep eBooks in compliance with any particular paper edition. + + +Most people start at our Web site which has the main PG search facility: + + http://www.gutenberg.org + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. \ No newline at end of file diff --git a/learning/base-language/read-write-files/read-txt/files/numbers.json b/learning/base-language/read-write-files/read-txt/files/numbers.json new file mode 100644 index 0000000..026cfca --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/files/numbers.json @@ -0,0 +1 @@ +[2, 3, 5, 7, 11, 13] \ No newline at end of file diff --git a/learning/base-language/read-write-files/read-txt/files/quotes.txt b/learning/base-language/read-write-files/read-txt/files/quotes.txt new file mode 100644 index 0000000..c261f71 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/files/quotes.txt @@ -0,0 +1,14 @@ +"The greatest glory in living lies not in never falling, but in rising every time we fall." +Nelson Mandela +"The way to get started is to quit talking and begin doing." +Walt Disney +"Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma – which is living with the results of other people's thinking." +Steve Jobs +"If life were predictable it would cease to be life, and be without flavor." +Eleanor Roosevelt +"If you look at what you have in life, you'll always have more. If you look at what you don't have in life, you'll never have enough." +Oprah Winfrey +"If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." +James Cameron +"Life is what happens when you're busy making other plans." +John Lennon diff --git a/learning/base-language/read-write-files/read-txt/files/username.json b/learning/base-language/read-write-files/read-txt/files/username.json new file mode 100644 index 0000000..920c138 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/files/username.json @@ -0,0 +1 @@ +"eric" \ No newline at end of file diff --git a/learning/base-language/read-write-files/read-txt/read-json-list.py b/learning/base-language/read-write-files/read-txt/read-json-list.py new file mode 100644 index 0000000..5f329c7 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/read-json-list.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +################################################################ +# Imports a text file that was exported in json format +# result is a list object +################################################################ + +import json +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/numbers.json" + +with open(filename) as f: + numbers = json.load(f) + +print(numbers) diff --git a/learning/base-language/read-write-files/read-txt/read-json-string.py b/learning/base-language/read-write-files/read-txt/read-json-string.py new file mode 100644 index 0000000..e618675 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/read-json-string.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +################################################################ +# Imports a text file that was exported in json format +# Result is a string object +################################################################ + +import json +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/username.json" + +with open(filename) as f: + username = json.load(f) + print(f"Welcome back, {username}!") diff --git a/learning/base-language/read-write-files/read-txt/read-txt-2-relative-path.py b/learning/base-language/read-write-files/read-txt/read-txt-2-relative-path.py new file mode 100644 index 0000000..f474d37 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/read-txt-2-relative-path.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +################################################################ +# Example reading a file and counting its words +# not sure if this is the best way, but it was fun and it worked! +################################################################ + +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/alice.txt" + +try: + with open(filename, encoding='utf-8') as f: + contents = f.read() +except FileNotFoundError: + print(f"Sorry, the file {filename} does not exist.") +else: + # Count the approximate number of words in the file. + words = contents.split() + num_words = len(words) + print(f"The file {filename} has about {num_words} words.") diff --git a/learning/base-language/read-write-files/read-txt/read-txt-3-relative-path.py b/learning/base-language/read-write-files/read-txt/read-txt-3-relative-path.py new file mode 100644 index 0000000..f37cea8 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/read-txt-3-relative-path.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +################################################################ +# Example reading a file and counting its words +# not sure if this is the best way, but it was fun and it worked! +################################################################ + +import os + +script_path = os.path.dirname(os.path.realpath(__file__)) +os.chdir(script_path) + +filename = "files/alice.txt" + +try: + with open(filename, encoding='utf-8') as f: + contents = f.read() +except FileNotFoundError: + print(f"Sorry, the file {filename} does not exist.") +else: + # Count the approximate number of words in the file. + words = contents.split() + num_words = len(words) + print(f"The file {filename} has about {num_words} words.") diff --git a/learning/base-language/read-write-files/read-txt/read-txt.py b/learning/base-language/read-write-files/read-txt/read-txt.py new file mode 100644 index 0000000..93746aa --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/read-txt.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +################################################################ +# Example reading a file and counting its words +################################################################ + + +filename = 'C:\\_gwill\\repo-home\\h1python\\learning\\base-language\\files-exceptions\\read-txt\\files\\alice.txt' + +try: + with open(filename, encoding='utf-8') as f: + contents = f.read() +except FileNotFoundError: + print(f"Sorry, the file {filename} does not exist.") +else: + # Count the approximate number of words in the file. + words = contents.split() + num_words = len(words) + print(f"The file {filename} has about {num_words} words.") diff --git a/learning/base-language/read-write-files/read-txt/testing.md b/learning/base-language/read-write-files/read-txt/testing.md new file mode 100644 index 0000000..bea0fe5 --- /dev/null +++ b/learning/base-language/read-write-files/read-txt/testing.md @@ -0,0 +1,26 @@ + + +print('getcwd: ', os.getcwd()) + +print('__file__: ', __file__) + +print('basename: ', os.path.basename(__file__)) + + +print('dirname: ', os.path.dirname(__file__)) + +path = str(sys.argv[0]) + +print(f"Path is {path}") + +split = os.path.split(path) + +print(f"split path is: {split}") + +dir_name = split[0] + +print(f"Dir name is : {dir_name}") + +os.chdir(dir_name) + +https://diveintopython3.net/comprehensions.html \ No newline at end of file diff --git a/learning/base-language/read-write-files/write-text/append.py b/learning/base-language/read-write-files/write-text/append.py new file mode 100644 index 0000000..f43b740 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/append.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +################################################################ +# Appends text to a file +################################################################ + +import json +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/programming.txt" + +with open(filename, 'a') as file_object: + file_object.write("text1.\n") + file_object.write("text2.\n") + +# this does not work because it will just create the file! +# try: +# with open(filename, 'a') as file_object: +# file_object.write("text1.\n") +# file_object.write("text2.\n") +# except FileNotFoundError: +# print(f"File does not exist: {filename}") diff --git a/learning/base-language/read-write-files/write-text/files/numbers.json b/learning/base-language/read-write-files/write-text/files/numbers.json new file mode 100644 index 0000000..026cfca --- /dev/null +++ b/learning/base-language/read-write-files/write-text/files/numbers.json @@ -0,0 +1 @@ +[2, 3, 5, 7, 11, 13] \ No newline at end of file diff --git a/learning/base-language/read-write-files/write-text/files/programming.txt b/learning/base-language/read-write-files/write-text/files/programming.txt new file mode 100644 index 0000000..71f39e6 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/files/programming.txt @@ -0,0 +1,6 @@ +I love programming. +I love creating new games. +I also love finding meaning in large datasets. +I love creating apps that can run in a browser. +text1. +text2. diff --git a/learning/base-language/read-write-files/write-text/files/programming2.txt b/learning/base-language/read-write-files/write-text/files/programming2.txt new file mode 100644 index 0000000..916afc4 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/files/programming2.txt @@ -0,0 +1,2 @@ +text1. +text2. diff --git a/learning/base-language/read-write-files/write-text/files/username.json b/learning/base-language/read-write-files/write-text/files/username.json new file mode 100644 index 0000000..da52e98 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/files/username.json @@ -0,0 +1 @@ +"gerry" \ No newline at end of file diff --git a/learning/base-language/read-write-files/write-text/write-json-list.py b/learning/base-language/read-write-files/write-text/write-json-list.py new file mode 100644 index 0000000..43154d5 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/write-json-list.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +################################################################ +# Exports a text file in json format to be imported as a list +################################################################ + +import json +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + +# Use that as the basis for importing the file +filename = f"{rel_path}files/numbers.json" + +numbers = [2, 3, 5, 7, 11, 13] + +with open(filename, 'w') as f: + json.dump(numbers, f) diff --git a/learning/base-language/read-write-files/write-text/write-json.py b/learning/base-language/read-write-files/write-text/write-json.py new file mode 100644 index 0000000..df860a4 --- /dev/null +++ b/learning/base-language/read-write-files/write-text/write-json.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +################################################################ +# Similar to write json, this will +# Call greet_user() which will check for a username file +# If it exists, it greets the user +# If it doesn't, it creates the file so that it will work on next run +################################################################ + +import json +import sys +import os + +# get script path from arguments +path = str(sys.argv[0]) +path_list = list(path.split("/")) + +# build a relative path excluding the last argument +rel_path = '' +for path in path_list[0:-1]: + rel_path = rel_path + path + os.sep + + + +def get_stored_username(): + """Get stored username if available.""" + filename = f"{rel_path}files/username.json" + try: + with open(filename) as f: + username = json.load(f) + except FileNotFoundError: + return None + else: + return username + +def get_new_username(): + """Prompt for a new username.""" + username = input("What is your name? ") + filename = f"{rel_path}files/username.json" + with open(filename, 'w') as f: + json.dump(username, f) + return username + +def greet_user(): + """Greet the user by name.""" + username = get_stored_username() + if username: + print(f"Welcome back, {username}!") + else: + username = get_new_username() + print(f"We'll remember you when you come back, {username}!") + +greet_user() \ No newline at end of file diff --git a/learning/base-language/sleep-clear/sleep-and-clear-2.py b/learning/base-language/sleep-clear/sleep-and-clear-2.py new file mode 100644 index 0000000..1f93278 --- /dev/null +++ b/learning/base-language/sleep-clear/sleep-and-clear-2.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +################################################################ +# This time we import then entire modules instead of submodules +# This is my preferred way for standard library modules (heavily tested), +# not third party modules though. +################################################################ + +import os +import time + +def clear(): + # for windows + if os.name == 'nt': + clear = os.system('cls') + # for mac and linux(here, os.name is 'posix') + else: + clear = os.system('clear') + return clear + +# print out some text +print('hello gerry\n'*10) + +# sleep for 2 seconds after printing output +time.sleep(2) + +# now call function we defined above +clear() \ No newline at end of file diff --git a/learning/base-language/sleep-clear/sleep-and-clear.py b/learning/base-language/sleep-clear/sleep-and-clear.py new file mode 100644 index 0000000..56f073c --- /dev/null +++ b/learning/base-language/sleep-clear/sleep-and-clear.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +################################################################ +# Importing submodules - not my preferred way +################################################################ + +from os import system, name +from time import sleep + +def clear(): + + if name == 'nt': + clear = system('cls') + else: + clear = system('clear') + return clear + +# print out some text +print('hello gerry\n'*10) + +# sleep for 2 seconds after printing output +sleep(2) + +# now call function we defined above +clear() \ No newline at end of file diff --git a/learning/base-language/strings/strings.py b/learning/base-language/strings/strings.py new file mode 100644 index 0000000..db2eb92 --- /dev/null +++ b/learning/base-language/strings/strings.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +################################################################ +# Text +################################################################ + +# common string operators +string = 'Gerry' + +# concatenation +string +' LastName' +#'Gerry LastName' + +# repetition +string*5 +#'GerryGerryGerryGerryGerry' + +# access character at index +string[1] +#'e' + +# length of string +len('python') + +# find characters at an index +'python'.index('o') + +# changing the case +print('hello world'.capitalize()) # capitalize 1st char of the string +print('hello world'.title()) # all words to title case + +# splitting a string .split() +print(string.split(' ')) # splitting the string by white space +print(string.split('my')) # splitting the string by a word, eq. 'my' +print(string.split('\n')) # splitting by newline character + +# joining string .join() +print(' '.join('Gerry')) # Joining each char with a space +print('*'.join('Gerry')) # Joining each char with a '*' + +# reversing a string reversed() +print(''.join(reversed('python'))) # reversing a string +print(''.join(list('python')[::-1])) # reversing a string / print(‘Python'[::-1]) is much more elegant and easy to remember + +# strip/trim a string .strip() , .lstrip() , .rstrip() +print(' Hey there '.strip()) # stripping white spaces from both ends of the string +print(' Hey there '.lstrip()) # stripping white spaces from left end of the string +print(' Hey there '.rstrip()) # stripping white spaces from right end of the string + +# string padding .rjust() .ljust(), .center() +print('Hello'.rjust(30)) +print('Hello'.ljust(30, '-')) +print('Hello'.rjust(30, '*')) +print('Hello'.center(30, '_')) + + +first_name = "ada" +last_name = "lovelace" +full_name = f"{first_name} {last_name}" +message = f"Hello, {full_name.title()}!" +print(message) + +name = "Ada Lovelace" +print(name.upper()) +print(name.lower()) \ No newline at end of file diff --git a/learning/csv-read-write/read-csv-1.py b/learning/csv-read-write/read-csv-1.py new file mode 100644 index 0000000..b030ec7 --- /dev/null +++ b/learning/csv-read-write/read-csv-1.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +################################################################ +# Import CSV, print index and content for each row +################################################################ + +import csv + +with open('C:\\_gwill\\repo-home\\h1python\\learning\\csv-read-write\\sample.csv', encoding='utf-8', newline='') as f: + reader = csv.reader(f) + # optional, print row number + reader = enumerate(csv.reader(f)) + + # loop through one row at a time, i is counter, row is entire row + for i, row in reader: + print(i, row) + +''' +prints: +0 ['Full Name', 'Birth Year', 'Date Joined', 'Is Active', 'Balance'] +1 ['Angst, Annie', '1982', '1/11/2011', 'TRUE', '$300.00'] +2 ['Bob, Marshall', '2000', '12/10/2001', 'FALSE', '$60.00'] +3 ['', '', '', '', ''] +4 ['Malaise, Mindy', '2006', '5/5/2005', 'TRUE', '$550.00 '] +''' diff --git a/learning/csv-read-write/read-csv-2.py b/learning/csv-read-write/read-csv-2.py new file mode 100644 index 0000000..b9e9867 --- /dev/null +++ b/learning/csv-read-write/read-csv-2.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +################################################################ +# Adding more details +################################################################ + +import csv +import datetime as dt +from re import sub +with open('C:\\_gwill\\repo-home\\h1python\\learning\\csv-read-write\\sample.csv', encoding='utf-8', newline='') as f: + reader = csv.reader(f) + # optional, print row number + reader = enumerate(csv.reader(f)) + + # loop through one row at a time, i is counter, row is entire row + for i, row in reader: + + # ignore header row + if i > 0: + try: + full_name = row[0].split(',') + last_name = full_name[0].strip() + first_name = full_name[1].strip() + except IndexError: + full_name = first_name = last_name = '' + + # birth year is int or 0 for empty string + birth_year = int(row[1] or 0) + + try: + date_joined = dt.datetime.strptime(row[2], "%m/%d/%y").date() + except ValueError: + date_joined = None + + # cast to bool + is_active = bool(row[3]) + + # remove $, commas, and leading/trailing spaces + #str_balance = (row[4].replace('$','').replace(',','')).strip() + + # If you prefer to use regex: + str_balance = (sub(r'[\s\$,]','',row[4])).strip() + + #balance is a float or 0 for empty string + balance = float(str_balance or 0) + + print(first_name, last_name, birth_year,date_joined,is_active,balance) +print('done') + +''' +prints: +Annie Angst 1982 None True 300.0 +Marshall Bob 2000 None True 60.0 + 0 None False 0.0 +Mindy Malaise 2006 None True 550.0 +done +''' \ No newline at end of file diff --git a/learning/csv-read-write/read-csv-3.py b/learning/csv-read-write/read-csv-3.py new file mode 100644 index 0000000..14240e2 --- /dev/null +++ b/learning/csv-read-write/read-csv-3.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +################################################################ +# Same as before, but place in functions, return as dict +################################################################ + +#pg 301 + +import datetime as dt +import csv + +def fname(any): + try: + nm = any.split(",") + return nm[1] + except IndexError: + return "" + +def lname(any): + try: + nm = any.split(",") + return nm[0] + except IndexError: + return "" + +def integer(any): + return int(any or 0) + +def date(any): + try: + return dt.datetime.strptime(any, "%m/%d/%Y").date() + except ValueError: + return None + +def boolean(any): + return bool(any) + + +def floatnum(any): + s_balance = (any.replace("$", "").replace(",", "")).strip() + return float(s_balance or 0) + +# create empty dict +people = {} + +with open("C:\\_gwill\\repo-home\\h1python\\learning\\csv-read-write\\sample.csv", encoding="utf-8", newline="") as f: + # reader = csv.reader(f) + # optional, print row number + reader = enumerate(csv.reader(f)) + + # skip first row + f.readline() + + # read through next rows with i as counter + for i, row in reader: + + # from each row create person object with a unique id + newdict = dict( + { + "first_name": fname(row[0]), + "last_name": lname(row[0]), + "birth_year": integer(row[1]), + "date_joined": date(row[2]), + "is_active": boolean(row[3]), + "balance": floatnum(row[4]), + } + ) + people[i + 1] = newdict + +# When loop is done, show all people +for person in people.keys(): + id = person + print( id, people[person]['first_name'], \ + people[person]['last_name'], \ + people[person]['birth_year'], \ + people[person]['date_joined'], \ + people[person]['is_active'], \ + people[person]['balance']) + diff --git a/learning/csv-read-write/read-csv.py b/learning/csv-read-write/read-csv.py new file mode 100644 index 0000000..aea36d3 --- /dev/null +++ b/learning/csv-read-write/read-csv.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +################################################################ +# This is a template function that will parse a CSV and extract 3 fields +################################################################ + +import csv + +def read_csv(filename): + add_remove = [] + app_name = [] + perm_name = [] + + with open(filename) as csvDataFile: + csvReader = csv.reader(csvDataFile) + for row in csvReader: + add_remove.append(row[1]) + app_name.append(row[5]) + perm_name.append(row[7]) + return add_remove, app_name, perm_name + +file = '/csv/dataset_500.csv' +add_remove, app_name, perm_name = read_csv(file) +print(add_remove[1]) +print(app_name[1]) +print(perm_name[1]) \ No newline at end of file diff --git a/learning/csv-read-write/sample.csv b/learning/csv-read-write/sample.csv new file mode 100644 index 0000000..e5df7f8 --- /dev/null +++ b/learning/csv-read-write/sample.csv @@ -0,0 +1,5 @@ +"Full Name","Birth Year","Date Joined","Is Active","Balance" +"Angst, Annie",1982,1/11/2011,TRUE,$300.00 +"Bob, Marshall",2000,12/10/2001,FALSE,$60.00 +,,,, +"Malaise, Mindy",2006,5/5/2005,TRUE,$549.64 \ No newline at end of file diff --git a/learning/handling-secrets/dot-env.py b/learning/handling-secrets/dot-env.py new file mode 100644 index 0000000..3614f16 --- /dev/null +++ b/learning/handling-secrets/dot-env.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of package dotenv +# Step 1: Create your venv folder and add the python-dotenv package: + +# cd /myuser/scripts +# mkdir csv +# cd csv +# scl enable rh-python36 bash +# python -m venv /myuser/scripts/csv/venv +# source /myuser/scripts/csv/venv/bin/activate +# pip install --upgrade pip +# python3 -m pip install python-dotenv + +# Step 2: Create an .env file at the root of your csv folder and enter + +# EMAIL="somePass" +# MYVAR="hunter2" + +# Step 3: Enter the following in your .py file that should also be at root of csv folder +################################################################ + +import os +from dotenv import load_dotenv + +load_dotenv() + +def main(): + try: + myvar = os.environ["MYVAR"] + except KeyError: + print("Unable to get environmental variables") + except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + # same for EMAIL + +if __name__ == '__main__': + main() + + diff --git a/learning/logging/base-logging-template.py b/learning/logging/base-logging-template.py new file mode 100644 index 0000000..f3bc15b --- /dev/null +++ b/learning/logging/base-logging-template.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of logging module +################################################################ + +import logging +import logging.handlers +import sys + +## Set it up +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +handler = logging.handlers.RotatingFileHandler( + '/home/user/gerry/example3.log', maxBytes=(1048576*5), backupCount=7 +) +formatter = logging.Formatter("%(asctime)s => %(levelname)s : %(message)s", datefmt='%Y-%m-%d %I:%M:%S %p') +handler.setFormatter(formatter) +logger.addHandler(handler) +logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + + +## Test it +logging.info('hello world') + +logging.error('error - hello world') + +logging.warning('warning - hello world') + +name = 'Gerry' +logging.error('%s raised an error', name) + +## Log an error +a = 5 +b = 0 + +try: + c = a / b +except Exception as e: + logging.error("Exception occurred", exc_info=True) diff --git a/learning/readme.md b/learning/readme.md new file mode 100644 index 0000000..a205ea1 --- /dev/null +++ b/learning/readme.md @@ -0,0 +1,21 @@ +## Learning Repo: + +Please be patient with me as these notes will appear scatter-brained for a while... + +## Books Read: + +### Python All-in-One For Dummies (7 books in one) + +- ISBN-13: 978-1119557593 +- ISBN-10: 1119557593 +- [Link](https://www.amazon.com/Python-All-One-Dummies-Shovic/dp/1119557593) + +### Python Crash Course - Second Edition + +- ISBN-13: 978-1593279288 +- ISBN-10: 1593279280 +- [Link](https://github.com/ehmatthes/pcc_2e/) + +### Disclaimer + +These are modified versions of the book's example exercises used for learning purposes only. Please contact me if this is in violation of Copyright and I will take it down immediately. \ No newline at end of file diff --git a/learning/requests/alphavantage-bitcoin.py b/learning/requests/alphavantage-bitcoin.py new file mode 100644 index 0000000..d9929b2 --- /dev/null +++ b/learning/requests/alphavantage-bitcoin.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using AlphaVantage API +# Sign up to get an API key and import it +# This script currently just gets the 5 latest values for bitcoin but can do others as well +# will eventually replace my powershell script at https://automationadmin.com/2020/09/ps-send-email-bitcoin +################################################################ + +import requests +from requests.auth import HTTPBasicAuth +import sys +from dotenv import load_dotenv +import os +import json +import math + +load_dotenv() + +try: + api_key = os.environ["API_KEY"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +url = f"https://www.alphavantage.co/query?function=DIGITAL_CURRENCY_DAILY&symbol=BTC&market=USD&apikey={api_key}" +payload = {} +headers = { + 'Content-Type': 'application/json', +} +r = requests.request("GET", url, headers=headers, data=payload) +req = r.json() + +## print whole req + +# print(req) +# print(dir(req)) + +## dump to file system + +# filename = 'req.json' +# with open(filename, 'w') as f: +# json.dump(req, f) + +## get all the keys and values +#print(req['Time Series (Daily)']) + +## get just the keys +#print(req['Time Series (Daily)'].keys()) + +## sort them +keylist = list(req['Time Series (Digital Currency Daily)'].keys()) +keylist.sort(reverse=True) + +## give me just the top 5 +# print(keylist[0:5]) + + +## print their values to make sure we got them +first_five_list = keylist[0:5] +# print(type(first_five_list)) + +# # Print the closing price last 5 days +# for first_five in first_five_list: +# #print(req['Time Series (Digital Currency Daily)'][first_five]) +# print(req['Time Series (Digital Currency Daily)'][first_five]['4b. close (USD)']) + +# ''' +# prints: +# 40485.86000000 +# 40088.22000000 +# 40582.81000000 +# 39432.28000000 +# 36769.36000000 +# ''' + +# for first_five in first_five_list: +# key = first_five +# value = req['Time Series (Digital Currency Daily)'][first_five] +# close = value['4b. close (USD)'] +# print(f"{key} : {close}") + # ''' +# prints: + # 2021-01-10 : 40485.86000000 + # 2021-01-09 : 40088.22000000 + # 2021-01-08 : 40582.81000000 + # 2021-01-07 : 39432.28000000 + # 2021-01-06 : 36769.36000000 +# ''' + +# now that we see the top five, store in variables for comparisons +first_key = first_five_list[0] +first_value = req['Time Series (Digital Currency Daily)'][first_key]['4b. close (USD)'] +first_value = math.floor(float(first_value)) + +second_key = first_five_list[1] +second_value = req['Time Series (Digital Currency Daily)'][second_key]['4b. close (USD)'] +second_value = math.floor(float(second_value)) + + +third_key = first_five_list[2] +third_value = req['Time Series (Digital Currency Daily)'][third_key]['4b. close (USD)'] +third_value = math.floor(float(third_value)) + +fourth_key = first_five_list[3] +fourth_value = req['Time Series (Digital Currency Daily)'][fourth_key]['4b. close (USD)'] +fourth_value = math.floor(float(fourth_value)) + +fifth_key = first_five_list[4] +fifth_value = req['Time Series (Digital Currency Daily)'][fifth_key]['4b. close (USD)'] +fifth_value = math.floor(float(fifth_value)) + +print(f"{first_key}: {first_value}") +print(f"{second_key}: {second_value}") +print(f"{third_key}: {third_value}") +print(f"{fourth_key}: {fourth_value}") +print(f"{fifth_key}: {fifth_value}") + +# now compare +# if first_value < second_value: +# print("First is less than second") +# else: +# print("First is greater than second") + +# if first_value < second_value < third_value: +# print("First is less than second and third") +# else: +# print("First is greater than second and third") + +# if we want to see if it is on a 5 day trend upwards +# if first_value > second_value > third_value > fourth_value > fifth_value: +# print("Trend is upwards for five days straight") +# else: +# print("Trend is not upwards for five days straight") + +# # if we want to see if it is on a 5 day trend downwards +# if first_value < second_value < third_value < fourth_value < fifth_value: +# print("Trend is downwards for five days straight") +# else: +# print("Trend is not downwards for five days straight") + +# print(first_value / second_value) + +if first_value < second_value < third_value : + print("Trend is downwards for three days straight") + # Here I will send an email to buy if down for 3 days straight +else: + print("Trend is not downwards for three days straight") \ No newline at end of file diff --git a/learning/requests/alphavantage.py b/learning/requests/alphavantage.py new file mode 100644 index 0000000..00cd13f --- /dev/null +++ b/learning/requests/alphavantage.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using AlphaVantage API +# Sign up to get an API key and import it +# This script currently just gets the 5 latest values for bitcoin but can do others as well +# will eventually replace my powershell script at https://automationadmin.com/2020/09/ps-send-email-bitcoin +################################################################ + +import requests +from requests.auth import HTTPBasicAuth +import sys +from dotenv import load_dotenv +import os +import json + +load_dotenv() + +try: + api_key = os.environ["API_KEY"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +# funds = ["VFIFX", "VWUSX", "VTSAX", "BTCUSD"] + +# for fund in funds: +# url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={fund}&apikey={api_key}" +# payload = {} +# headers = { +# 'Content-Type': 'application/json', +# } +# r = requests.request("GET", url, headers=headers, data=payload) +# print(r.text) + + +url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=BTCUSD&apikey={api_key}" +payload = {} +headers = { + 'Content-Type': 'application/json', +} +r = requests.request("GET", url, headers=headers, data=payload) +req = r.json() + +## print whole req + +# print(req) +# print(dir(req)) + +## dump to file system + +# filename = 'req.json' +# with open(filename, 'w') as f: +# json.dump(req, f) + +## get all the keys and values +#print(req['Time Series (Daily)']) + +## get just the keys +#print(req['Time Series (Daily)'].keys()) + +## sort them +keylist = list(req['Time Series (Daily)'].keys()) +keylist.sort(reverse=True) + +## give me just the top 5 +print(keylist[0:5]) + + +## print their values to make sure we got them +first_five_list = keylist[0:5] + +for first_five in first_five_list: + print(req['Time Series (Daily)'][first_five]) \ No newline at end of file diff --git a/learning/requests/r.json b/learning/requests/r.json new file mode 100644 index 0000000..37d1292 --- /dev/null +++ b/learning/requests/r.json @@ -0,0 +1,1011 @@ +{ + "Meta Data": { + "1. Information": "Daily Time Series with Splits and Dividend Events", + "2. Symbol": "BTCUSD", + "3. Last Refreshed": "2021-01-01", + "4. Output Size": "Compact", + "5. Time Zone": "US/Eastern" + }, + "Time Series (Daily)": { + "2021-01-01": { + "1. open": "28927.210136", + "2. high": "29020.3755355", + "3. low": "28693.0762325", + "4. close": "28916.0755335", + "5. adjusted close": "28916.0755335", + "6. volume": "2260", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-31": { + "1. open": "28880.6876533", + "2. high": "29310.7371088", + "3. low": "27738.297271", + "4. close": "28927.4768816", + "5. adjusted close": "28927.4768816", + "6. volume": "108475", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-30": { + "1. open": "27371.4333907", + "2. high": "28995.1467226", + "3. low": "27341.8638384", + "4. close": "28880.9190147", + "5. adjusted close": "28880.9190147", + "6. volume": "126424", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-29": { + "1. open": "27069.3066769", + "2. high": "27400.0426719", + "3. low": "25869.2813135", + "4. close": "27375.2986851", + "5. adjusted close": "27375.2986851", + "6. volume": "94267", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-28": { + "1. open": "26431.618384", + "2. high": "27491.9392603", + "3. low": "26145.4484069", + "4. close": "27070.9922045", + "5. adjusted close": "27070.9922045", + "6. volume": "104526", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-27": { + "1. open": "26485.2849109", + "2. high": "28408.3489921", + "3. low": "25700.757443", + "4. close": "26306.9624867", + "5. adjusted close": "26306.9624867", + "6. volume": "193809", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-26": { + "1. open": "24712.0975159", + "2. high": "26841.7352415", + "3. low": "24494.6131659", + "4. close": "26482.5141427", + "5. adjusted close": "26482.5141427", + "6. volume": "129978", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-25": { + "1. open": "23712.729656", + "2. high": "24788.8293163", + "3. low": "23369.3135558", + "4. close": "24712.8461076", + "5. adjusted close": "24712.8461076", + "6. volume": "65429", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-24": { + "1. open": "23202.1681977", + "2. high": "23763.2358559", + "3. low": "22703.919786", + "4. close": "23692.8750505", + "5. adjusted close": "23692.8750505", + "6. volume": "85305", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-23": { + "1. open": "23811.718754", + "2. high": "24103.3978928", + "3. low": "22604.5895392", + "4. close": "23215.9711664", + "5. adjusted close": "23215.9711664", + "6. volume": "147378", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-22": { + "1. open": "22720.6132532", + "2. high": "23835.6742175", + "3. low": "22355.240811", + "4. close": "23811.9879766", + "5. adjusted close": "23811.9879766", + "6. volume": "107721", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-21": { + "1. open": "23447.7072437", + "2. high": "24087.9848499", + "3. low": "21879.5630301", + "4. close": "22721.5875263", + "5. adjusted close": "22721.5875263", + "6. volume": "112749", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-20": { + "1. open": "23826.4625126", + "2. high": "24300.1173723", + "3. low": "23066.8296369", + "4. close": "23459.0461101", + "5. adjusted close": "23459.0461101", + "6. volume": "92467", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-19": { + "1. open": "23121.866995", + "2. high": "24178.7228552", + "3. low": "22755.474714", + "4. close": "23826.389249", + "5. adjusted close": "23826.389249", + "6. volume": "111503", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-18": { + "1. open": "22987.7362434", + "2. high": "23280.5819132", + "3. low": "22348.7461333", + "4. close": "23086.2993795", + "5. adjusted close": "23086.2993795", + "6. volume": "91202", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-17": { + "1. open": "21339.3727898", + "2. high": "23798.377448", + "3. low": "21231.6796189", + "4. close": "22806.2016266", + "5. adjusted close": "22806.2016266", + "6. volume": "213595", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-16": { + "1. open": "19429.3503252", + "2. high": "21526.6610787", + "3. low": "19269.207813", + "4. close": "21303.7935425", + "5. adjusted close": "21303.7935425", + "6. volume": "149040", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-15": { + "1. open": "19273.2913858", + "2. high": "19568.7926302", + "3. low": "19049.6452503", + "4. close": "19429.2831173", + "5. adjusted close": "19429.2831173", + "6. volume": "88448", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-14": { + "1. open": "19172.7439935", + "2. high": "19349.10504", + "3. low": "18999.7950546", + "4. close": "19271.4492523", + "5. adjusted close": "19271.4492523", + "6. volume": "60262", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-13": { + "1. open": "18811.2583739", + "2. high": "19412.8619937", + "3. low": "18714.17974", + "4. close": "19174.0408912", + "5. adjusted close": "19174.0408912", + "6. volume": "72483", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-12": { + "1. open": "18071.4831837", + "2. high": "18949.3242723", + "3. low": "18049.7772597", + "4. close": "18810.6694762", + "5. adjusted close": "18810.6694762", + "6. volume": "60000", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-11": { + "1. open": "18236.9512891", + "2. high": "18282.9093458", + "3. low": "17577.4278828", + "4. close": "18040.1936034", + "5. adjusted close": "18040.1936034", + "6. volume": "93063", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-10": { + "1. open": "18527.717914", + "2. high": "18548.1436624", + "3. low": "17911.2798372", + "4. close": "18259.0342371", + "5. adjusted close": "18259.0342371", + "6. volume": "70518", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-09": { + "1. open": "18323.2169636", + "2. high": "18640.0869401", + "3. low": "17655.8937678", + "4. close": "18547.0178783", + "5. adjusted close": "18547.0178783", + "6. volume": "107738", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-08": { + "1. open": "19172.0558042", + "2. high": "19298.7368734", + "3. low": "18207.0449993", + "4. close": "18323.4118665", + "5. adjusted close": "18323.4118665", + "6. volume": "85473", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-07": { + "1. open": "19363.9428501", + "2. high": "19424.9722957", + "3. low": "18903.6307713", + "4. close": "19170.3084987", + "5. adjusted close": "19170.3084987", + "6. volume": "53729", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-06": { + "1. open": "19150.5205875", + "2. high": "19426.4548665", + "3. low": "18864.5703084", + "4. close": "19365.245244", + "5. adjusted close": "19365.245244", + "6. volume": "48949", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-05": { + "1. open": "18649.6694277", + "2. high": "19179.106006", + "3. low": "18514.1183887", + "4. close": "19149.9592807", + "5. adjusted close": "19149.9592807", + "6. volume": "53857", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-04": { + "1. open": "19428.6009794", + "2. high": "19532.3171112", + "3. low": "18588.699197", + "4. close": "18675.3240198", + "5. adjusted close": "18675.3240198", + "6. volume": "95732", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-03": { + "1. open": "19208.7642101", + "2. high": "19604.5220368", + "3. low": "18872.2655116", + "4. close": "19428.2042744", + "5. adjusted close": "19428.2042744", + "6. volume": "87118", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-02": { + "1. open": "18780.0319096", + "2. high": "19339.7218446", + "3. low": "18333.281531", + "4. close": "19208.8930937", + "5. adjusted close": "19208.8930937", + "6. volume": "99301", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-12-01": { + "1. open": "19690.2166708", + "2. high": "19902.9173576", + "3. low": "18062.8981203", + "4. close": "18793.8939164", + "5. adjusted close": "18793.8939164", + "6. volume": "172122", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-30": { + "1. open": "18188.9255735", + "2. high": "19864.1080492", + "3. low": "18188.5294947", + "4. close": "19698.7997637", + "5. adjusted close": "19698.7997637", + "6. volume": "156466", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-29": { + "1. open": "17723.6001046", + "2. high": "18359.5847107", + "3. low": "17521.8156245", + "4. close": "18187.9299081", + "5. adjusted close": "18187.9299081", + "6. volume": "68832", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-28": { + "1. open": "17144.2810691", + "2. high": "17891.144818", + "3. low": "16870.737154", + "4. close": "17724.928247", + "5. adjusted close": "17724.928247", + "6. volume": "84730", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-27": { + "1. open": "17158.9011562", + "2. high": "17468.3760943", + "3. low": "16380.5160856", + "4. close": "17144.8103376", + "5. adjusted close": "17144.8103376", + "6. volume": "114547", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-26": { + "1. open": "18719.6429688", + "2. high": "18917.492098", + "3. low": "16207.476033", + "4. close": "17161.0291488", + "5. adjusted close": "17161.0291488", + "6. volume": "250915", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-25": { + "1. open": "19159.140365", + "2. high": "19487.9742822", + "3. low": "18500.2397977", + "4. close": "18720.6918513", + "5. adjusted close": "18720.6918513", + "6. volume": "125525", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-24": { + "1. open": "18385.8507777", + "2. high": "19423.9856518", + "3. low": "18033.9584205", + "4. close": "19158.8929688", + "5. adjusted close": "19158.8929688", + "6. volume": "149271", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-23": { + "1. open": "18415.6880172", + "2. high": "18764.82606", + "3. low": "18000.275946", + "4. close": "18373.5526524", + "5. adjusted close": "18373.5526524", + "6. volume": "108983", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-22": { + "1. open": "18680.8161078", + "2. high": "18743.7795648", + "3. low": "17613.851111", + "4. close": "18415.9707485", + "5. adjusted close": "18415.9707485", + "6. volume": "105941", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-21": { + "1. open": "18659.1891082", + "2. high": "18967.2006466", + "3. low": "18315.5507445", + "4. close": "18706.2689505", + "5. adjusted close": "18706.2689505", + "6. volume": "96065", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-20": { + "1. open": "17806.5974199", + "2. high": "18818.0336148", + "3. low": "17744.9407315", + "4. close": "18659.335794", + "5. adjusted close": "18659.335794", + "6. volume": "114986", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-19": { + "1. open": "17779.4926731", + "2. high": "18181.1789946", + "3. low": "17339.0434321", + "4. close": "17806.3611675", + "5. adjusted close": "17806.3611675", + "6. volume": "116235", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-18": { + "1. open": "17664.0967365", + "2. high": "18476.7041369", + "3. low": "17204.5964654", + "4. close": "17777.1129543", + "5. adjusted close": "17777.1129543", + "6. volume": "199473", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-17": { + "1. open": "16715.7197046", + "2. high": "17860.3614325", + "3. low": "16543.6832289", + "4. close": "17664.6364222", + "5. adjusted close": "17664.6364222", + "6. volume": "152401", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-16": { + "1. open": "15959.4090724", + "2. high": "16882.3160571", + "3. low": "15866.2863495", + "4. close": "16715.7591217", + "5. adjusted close": "16715.7591217", + "6. volume": "102392", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-15": { + "1. open": "16070.5778865", + "2. high": "16178.0384555", + "3. low": "15777.3270033", + "4. close": "15958.6409481", + "5. adjusted close": "15958.6409481", + "6. volume": "52727", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-14": { + "1. open": "16321.8380188", + "2. high": "16328.7099621", + "3. low": "15678.184264", + "4. close": "16069.6675223", + "5. adjusted close": "16069.6675223", + "6. volume": "74004", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-13": { + "1. open": "16295.6047964", + "2. high": "16482.4343153", + "3. low": "15950.4382886", + "4. close": "16322.4886622", + "5. adjusted close": "16322.4886622", + "6. volume": "99004", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-12": { + "1. open": "15690.4227832", + "2. high": "16344.9885672", + "3. low": "15443.2569748", + "4. close": "16295.8023695", + "5. adjusted close": "16295.8023695", + "6. volume": "140216", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-11": { + "1. open": "15301.388662", + "2. high": "15972.1621584", + "3. low": "15277.3682924", + "4. close": "15689.7462323", + "5. adjusted close": "15689.7462323", + "6. volume": "103416", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-10": { + "1. open": "15331.6460828", + "2. high": "15464.7472623", + "3. low": "15076.3594924", + "4. close": "15301.4461545", + "5. adjusted close": "15301.4461545", + "6. volume": "81545", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-09": { + "1. open": "15478.1213781", + "2. high": "15845.7961823", + "3. low": "14807.803926", + "4. close": "15331.2558082", + "5. adjusted close": "15331.2558082", + "6. volume": "142247", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-08": { + "1. open": "14823.2717292", + "2. high": "15653.9594359", + "3. low": "14710.4618626", + "4. close": "15478.2766597", + "5. adjusted close": "15478.2766597", + "6. volume": "85849", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-07": { + "1. open": "15583.1459551", + "2. high": "15756.9007479", + "3. low": "14324.2638644", + "4. close": "14824.105818", + "5. adjusted close": "14824.105818", + "6. volume": "142174", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-06": { + "1. open": "15588.5404482", + "2. high": "15957.3365256", + "3. low": "15175.3233291", + "4. close": "15579.9966459", + "5. adjusted close": "15579.9966459", + "6. volume": "165939", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-05": { + "1. open": "14148.7439833", + "2. high": "15747.2992129", + "3. low": "14098.4310149", + "4. close": "15582.7363925", + "5. adjusted close": "15582.7363925", + "6. volume": "209434", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-04": { + "1. open": "14025.6260144", + "2. high": "14263.748112", + "3. low": "13525.5205026", + "4. close": "14146.9476771", + "5. adjusted close": "14146.9476771", + "6. volume": "120776", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-03": { + "1. open": "13552.3499462", + "2. high": "14068.4973004", + "3. low": "13286.7700438", + "4. close": "14024.8522363", + "5. adjusted close": "14024.8522363", + "6. volume": "94572", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-02": { + "1. open": "13762.5633014", + "2. high": "13832.0034754", + "3. low": "13196.7779015", + "4. close": "13552.4916547", + "5. adjusted close": "13552.4916547", + "6. volume": "84020", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-11-01": { + "1. open": "13791.9395092", + "2. high": "13896.4339751", + "3. low": "13607.4173116", + "4. close": "13762.5272255", + "5. adjusted close": "13762.5272255", + "6. volume": "45961", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-31": { + "1. open": "13562.5088503", + "2. high": "14095.4492686", + "3. low": "13414.802757", + "4. close": "13792.6877245", + "5. adjusted close": "13792.6877245", + "6. volume": "89109", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-30": { + "1. open": "13456.158282", + "2. high": "13673.2685421", + "3. low": "13118.1377306", + "4. close": "13562.5593583", + "5. adjusted close": "13562.5593583", + "6. volume": "91448", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-29": { + "1. open": "13268.8928601", + "2. high": "13647.1317677", + "3. low": "12929.9556077", + "4. close": "13456.2374277", + "5. adjusted close": "13456.2374277", + "6. volume": "95355", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-28": { + "1. open": "13658.1020741", + "2. high": "13859.0473388", + "3. low": "12888.3558279", + "4. close": "13269.0063681", + "5. adjusted close": "13269.0063681", + "6. volume": "123086", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-27": { + "1. open": "13055.4782246", + "2. high": "13787.3976611", + "3. low": "13029.6295435", + "4. close": "13659.0179178", + "5. adjusted close": "13659.0179178", + "6. volume": "107811", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-26": { + "1. open": "13032.4575907", + "2. high": "13258.2996275", + "3. low": "12763.9983714", + "4. close": "13055.0362084", + "5. adjusted close": "13055.0362084", + "6. volume": "77518", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-25": { + "1. open": "13115.5355918", + "2. high": "13352.642333", + "3. low": "12877.0803803", + "4. close": "13033.6428948", + "5. adjusted close": "13033.6428948", + "6. volume": "51903", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-24": { + "1. open": "12925.9948938", + "2. high": "13168.6810037", + "3. low": "12873.0436244", + "4. close": "13114.9531255", + "5. adjusted close": "13114.9531255", + "6. volume": "46079", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-23": { + "1. open": "12972.654293", + "2. high": "13029.4346397", + "3. low": "12719.3409151", + "4. close": "12926.0199605", + "5. adjusted close": "12926.0199605", + "6. volume": "64104", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-22": { + "1. open": "12787.8497661", + "2. high": "13188.9921351", + "3. low": "12684.3396884", + "4. close": "12973.7363225", + "5. adjusted close": "12973.7363225", + "6. volume": "97287", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-21": { + "1. open": "11913.9503499", + "2. high": "13224.5052672", + "3. low": "11891.803755", + "4. close": "12789.2839959", + "5. adjusted close": "12789.2839959", + "6. volume": "163565", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-20": { + "1. open": "11753.3648667", + "2. high": "12040.8651419", + "3. low": "11680.1370453", + "4. close": "11913.4513609", + "5. adjusted close": "11913.4513609", + "6. volume": "82222", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-19": { + "1. open": "11505.942442", + "2. high": "11841.4899392", + "3. low": "11409.8079976", + "4. close": "11753.3633126", + "5. adjusted close": "11753.3633126", + "6. volume": "62091", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-18": { + "1. open": "11362.1542265", + "2. high": "11508.4750251", + "3. low": "11347.7163823", + "4. close": "11505.552238", + "5. adjusted close": "11505.552238", + "6. volume": "29147", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-17": { + "1. open": "11320.7021901", + "2. high": "11405.5621949", + "3. low": "11257.0682258", + "4. close": "11361.8444037", + "5. adjusted close": "11361.8444037", + "6. volume": "27221", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-16": { + "1. open": "11506.1326906", + "2. high": "11542.8711383", + "3. low": "11200.8251635", + "4. close": "11321.1378692", + "5. adjusted close": "11321.1378692", + "6. volume": "62278", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-15": { + "1. open": "11420.8138161", + "2. high": "11621.7273323", + "3. low": "11254.4359244", + "4. close": "11505.8916206", + "5. adjusted close": "11505.8916206", + "6. volume": "62105", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-14": { + "1. open": "11422.870052", + "2. high": "11550.0147559", + "3. low": "11283.077471", + "4. close": "11420.4336024", + "5. adjusted close": "11420.4336024", + "6. volume": "51638", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-13": { + "1. open": "11531.6989752", + "2. high": "11558.9919214", + "3. low": "11304.3980096", + "4. close": "11423.337925", + "5. adjusted close": "11423.337925", + "6. volume": "55236", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-12": { + "1. open": "11371.1900085", + "2. high": "11724.4306615", + "3. low": "11168.8780025", + "4. close": "11532.3151677", + "5. adjusted close": "11532.3151677", + "6. volume": "71116", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-11": { + "1. open": "11294.9449041", + "2. high": "11445.2237535", + "3. low": "11232.1294389", + "4. close": "11370.4552367", + "5. adjusted close": "11370.4552367", + "6. volume": "36296", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-10": { + "1. open": "11053.2240142", + "2. high": "11493.3549289", + "3. low": "11052.9005153", + "4. close": "11295.3519842", + "5. adjusted close": "11295.3519842", + "6. volume": "57828", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-09": { + "1. open": "10927.3829636", + "2. high": "11108.6855616", + "3. low": "10830.9812328", + "4. close": "11052.6455638", + "5. adjusted close": "11052.6455638", + "6. volume": "61604", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-08": { + "1. open": "10668.2406083", + "2. high": "10953.4194289", + "3. low": "10530.181201", + "4. close": "10927.8077266", + "5. adjusted close": "10927.8077266", + "6. volume": "67366", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-07": { + "1. open": "10600.3972369", + "2. high": "10682.5226353", + "3. low": "10546.9606706", + "4. close": "10667.8723195", + "5. adjusted close": "10667.8723195", + "6. volume": "40891", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-06": { + "1. open": "10793.6249323", + "2. high": "10801.4197843", + "3. low": "10525.5356824", + "4. close": "10600.6085445", + "5. adjusted close": "10600.6085445", + "6. volume": "59902", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-05": { + "1. open": "10667.9678676", + "2. high": "10799.5952753", + "3. low": "10617.3843534", + "4. close": "10793.3685418", + "5. adjusted close": "10793.3685418", + "6. volume": "42207", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-04": { + "1. open": "10544.0504852", + "2. high": "10697.7975136", + "3. low": "10519.7235279", + "4. close": "10668.8314138", + "5. adjusted close": "10668.8314138", + "6. volume": "28661", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-03": { + "1. open": "10571.2521551", + "2. high": "10604.3144547", + "3. low": "10497.1985999", + "4. close": "10543.7998501", + "5. adjusted close": "10543.7998501", + "6. volume": "26545", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-02": { + "1. open": "10619.418828", + "2. high": "10666.4977976", + "3. low": "10373.356877", + "4. close": "10571.746921", + "5. adjusted close": "10571.746921", + "6. volume": "64437", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-10-01": { + "1. open": "10778.5666828", + "2. high": "10925.894102", + "3. low": "10437.15412", + "4. close": "10619.8845875", + "5. adjusted close": "10619.8845875", + "6. volume": "80128", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-30": { + "1. open": "10841.0189693", + "2. high": "10849.5284914", + "3. low": "10662.1160547", + "4. close": "10777.565205", + "5. adjusted close": "10777.565205", + "6. volume": "47731", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-29": { + "1. open": "10696.3901313", + "2. high": "10867.7591819", + "3. low": "10636.5115122", + "4. close": "10841.0597673", + "5. adjusted close": "10841.0597673", + "6. volume": "52242", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-28": { + "1. open": "10776.2120472", + "2. high": "10953.0457294", + "3. low": "10627.1330439", + "4. close": "10697.1062234", + "5. adjusted close": "10697.1062234", + "6. volume": "63349", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-27": { + "1. open": "10729.8184754", + "2. high": "10801.013737", + "3. low": "10594.817634", + "4. close": "10776.0810384", + "5. adjusted close": "10776.0810384", + "6. volume": "36546", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-26": { + "1. open": "10687.6533143", + "2. high": "10822.3700117", + "3. low": "10646.5841147", + "4. close": "10729.3719517", + "5. adjusted close": "10729.3719517", + "6. volume": "33393", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-25": { + "1. open": "10737.5757617", + "2. high": "10761.805872", + "3. low": "10544.3619352", + "4. close": "10688.1403924", + "5. adjusted close": "10688.1403924", + "6. volume": "60810", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + }, + "2020-09-24": { + "1. open": "10241.9685007", + "2. high": "10797.4584255", + "3. low": "10192.5767381", + "4. close": "10737.6310446", + "5. adjusted close": "10737.6310446", + "6. volume": "71238", + "7. dividend amount": "0.0000", + "8. split coefficient": "1.0" + } + } +} \ No newline at end of file diff --git a/learning/requests/req.json b/learning/requests/req.json new file mode 100644 index 0000000..e5f86d3 --- /dev/null +++ b/learning/requests/req.json @@ -0,0 +1 @@ +{"Meta Data": {"1. Information": "Daily Time Series with Splits and Dividend Events", "2. Symbol": "BTCUSD", "3. Last Refreshed": "2021-01-01", "4. Output Size": "Compact", "5. Time Zone": "US/Eastern"}, "Time Series (Daily)": {"2021-01-01": {"1. open": "28927.210136", "2. high": "29020.3755355", "3. low": "28693.0762325", "4. close": "28916.0755335", "5. adjusted close": "28916.0755335", "6. volume": "2260", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-31": {"1. open": "28880.6876533", "2. high": "29310.7371088", "3. low": "27738.297271", "4. close": "28927.4768816", "5. adjusted close": "28927.4768816", "6. volume": "108475", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-30": {"1. open": "27371.4333907", "2. high": "28995.1467226", "3. low": "27341.8638384", "4. close": "28880.9190147", "5. adjusted close": "28880.9190147", "6. volume": "126424", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-29": {"1. open": "27069.3066769", "2. high": "27400.0426719", "3. low": "25869.2813135", "4. close": "27375.2986851", "5. adjusted close": "27375.2986851", "6. volume": "94267", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-28": {"1. open": "26431.618384", "2. high": "27491.9392603", "3. low": "26145.4484069", "4. close": "27070.9922045", "5. adjusted close": "27070.9922045", "6. volume": "104526", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-27": {"1. open": "26485.2849109", "2. high": "28408.3489921", "3. low": "25700.757443", "4. close": "26306.9624867", "5. adjusted close": "26306.9624867", "6. volume": "193809", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-26": {"1. open": "24712.0975159", "2. high": "26841.7352415", "3. low": "24494.6131659", "4. close": "26482.5141427", "5. adjusted close": "26482.5141427", "6. volume": "129978", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-25": {"1. open": "23712.729656", "2. high": "24788.8293163", "3. low": "23369.3135558", "4. close": "24712.8461076", "5. adjusted close": "24712.8461076", "6. volume": "65429", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-24": {"1. open": "23202.1681977", "2. high": "23763.2358559", "3. low": "22703.919786", "4. close": "23692.8750505", "5. adjusted close": "23692.8750505", "6. volume": "85305", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-23": {"1. open": "23811.718754", "2. high": "24103.3978928", "3. low": "22604.5895392", "4. close": "23215.9711664", "5. adjusted close": "23215.9711664", "6. volume": "147378", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-22": {"1. open": "22720.6132532", "2. high": "23835.6742175", "3. low": "22355.240811", "4. close": "23811.9879766", "5. adjusted close": "23811.9879766", "6. volume": "107721", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-21": {"1. open": "23447.7072437", "2. high": "24087.9848499", "3. low": "21879.5630301", "4. close": "22721.5875263", "5. adjusted close": "22721.5875263", "6. volume": "112749", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-20": {"1. open": "23826.4625126", "2. high": "24300.1173723", "3. low": "23066.8296369", "4. close": "23459.0461101", "5. adjusted close": "23459.0461101", "6. volume": "92467", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-19": {"1. open": "23121.866995", "2. high": "24178.7228552", "3. low": "22755.474714", "4. close": "23826.389249", "5. adjusted close": "23826.389249", "6. volume": "111503", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-18": {"1. open": "22987.7362434", "2. high": "23280.5819132", "3. low": "22348.7461333", "4. close": "23086.2993795", "5. adjusted close": "23086.2993795", "6. volume": "91202", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-17": {"1. open": "21339.3727898", "2. high": "23798.377448", "3. low": "21231.6796189", "4. close": "22806.2016266", "5. adjusted close": "22806.2016266", "6. volume": "213595", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-16": {"1. open": "19429.3503252", "2. high": "21526.6610787", "3. low": "19269.207813", "4. close": "21303.7935425", "5. adjusted close": "21303.7935425", "6. volume": "149040", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-15": {"1. open": "19273.2913858", "2. high": "19568.7926302", "3. low": "19049.6452503", "4. close": "19429.2831173", "5. adjusted close": "19429.2831173", "6. volume": "88448", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-14": {"1. open": "19172.7439935", "2. high": "19349.10504", "3. low": "18999.7950546", "4. close": "19271.4492523", "5. adjusted close": "19271.4492523", "6. volume": "60262", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-13": {"1. open": "18811.2583739", "2. high": "19412.8619937", "3. low": "18714.17974", "4. close": "19174.0408912", "5. adjusted close": "19174.0408912", "6. volume": "72483", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-12": {"1. open": "18071.4831837", "2. high": "18949.3242723", "3. low": "18049.7772597", "4. close": "18810.6694762", "5. adjusted close": "18810.6694762", "6. volume": "60000", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-11": {"1. open": "18236.9512891", "2. high": "18282.9093458", "3. low": "17577.4278828", "4. close": "18040.1936034", "5. adjusted close": "18040.1936034", "6. volume": "93063", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-10": {"1. open": "18527.717914", "2. high": "18548.1436624", "3. low": "17911.2798372", "4. close": "18259.0342371", "5. adjusted close": "18259.0342371", "6. volume": "70518", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-09": {"1. open": "18323.2169636", "2. high": "18640.0869401", "3. low": "17655.8937678", "4. close": "18547.0178783", "5. adjusted close": "18547.0178783", "6. volume": "107738", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-08": {"1. open": "19172.0558042", "2. high": "19298.7368734", "3. low": "18207.0449993", "4. close": "18323.4118665", "5. adjusted close": "18323.4118665", "6. volume": "85473", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-07": {"1. open": "19363.9428501", "2. high": "19424.9722957", "3. low": "18903.6307713", "4. close": "19170.3084987", "5. adjusted close": "19170.3084987", "6. volume": "53729", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-06": {"1. open": "19150.5205875", "2. high": "19426.4548665", "3. low": "18864.5703084", "4. close": "19365.245244", "5. adjusted close": "19365.245244", "6. volume": "48949", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-05": {"1. open": "18649.6694277", "2. high": "19179.106006", "3. low": "18514.1183887", "4. close": "19149.9592807", "5. adjusted close": "19149.9592807", "6. volume": "53857", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-04": {"1. open": "19428.6009794", "2. high": "19532.3171112", "3. low": "18588.699197", "4. close": "18675.3240198", "5. adjusted close": "18675.3240198", "6. volume": "95732", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-03": {"1. open": "19208.7642101", "2. high": "19604.5220368", "3. low": "18872.2655116", "4. close": "19428.2042744", "5. adjusted close": "19428.2042744", "6. volume": "87118", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-02": {"1. open": "18780.0319096", "2. high": "19339.7218446", "3. low": "18333.281531", "4. close": "19208.8930937", "5. adjusted close": "19208.8930937", "6. volume": "99301", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-12-01": {"1. open": "19690.2166708", "2. high": "19902.9173576", "3. low": "18062.8981203", "4. close": "18793.8939164", "5. adjusted close": "18793.8939164", "6. volume": "172122", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-30": {"1. open": "18188.9255735", "2. high": "19864.1080492", "3. low": "18188.5294947", "4. close": "19698.7997637", "5. adjusted close": "19698.7997637", "6. volume": "156466", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-29": {"1. open": "17723.6001046", "2. high": "18359.5847107", "3. low": "17521.8156245", "4. close": "18187.9299081", "5. adjusted close": "18187.9299081", "6. volume": "68832", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-28": {"1. open": "17144.2810691", "2. high": "17891.144818", "3. low": "16870.737154", "4. close": "17724.928247", "5. adjusted close": "17724.928247", "6. volume": "84730", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-27": {"1. open": "17158.9011562", "2. high": "17468.3760943", "3. low": "16380.5160856", "4. close": "17144.8103376", "5. adjusted close": "17144.8103376", "6. volume": "114547", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-26": {"1. open": "18719.6429688", "2. high": "18917.492098", "3. low": "16207.476033", "4. close": "17161.0291488", "5. adjusted close": "17161.0291488", "6. volume": "250915", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-25": {"1. open": "19159.140365", "2. high": "19487.9742822", "3. low": "18500.2397977", "4. close": "18720.6918513", "5. adjusted close": "18720.6918513", "6. volume": "125525", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-24": {"1. open": "18385.8507777", "2. high": "19423.9856518", "3. low": "18033.9584205", "4. close": "19158.8929688", "5. adjusted close": "19158.8929688", "6. volume": "149271", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-23": {"1. open": "18415.6880172", "2. high": "18764.82606", "3. low": "18000.275946", "4. close": "18373.5526524", "5. adjusted close": "18373.5526524", "6. volume": "108983", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-22": {"1. open": "18680.8161078", "2. high": "18743.7795648", "3. low": "17613.851111", "4. close": "18415.9707485", "5. adjusted close": "18415.9707485", "6. volume": "105941", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-21": {"1. open": "18659.1891082", "2. high": "18967.2006466", "3. low": "18315.5507445", "4. close": "18706.2689505", "5. adjusted close": "18706.2689505", "6. volume": "96065", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-20": {"1. open": "17806.5974199", "2. high": "18818.0336148", "3. low": "17744.9407315", "4. close": "18659.335794", "5. adjusted close": "18659.335794", "6. volume": "114986", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-19": {"1. open": "17779.4926731", "2. high": "18181.1789946", "3. low": "17339.0434321", "4. close": "17806.3611675", "5. adjusted close": "17806.3611675", "6. volume": "116235", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-18": {"1. open": "17664.0967365", "2. high": "18476.7041369", "3. low": "17204.5964654", "4. close": "17777.1129543", "5. adjusted close": "17777.1129543", "6. volume": "199473", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-17": {"1. open": "16715.7197046", "2. high": "17860.3614325", "3. low": "16543.6832289", "4. close": "17664.6364222", "5. adjusted close": "17664.6364222", "6. volume": "152401", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-16": {"1. open": "15959.4090724", "2. high": "16882.3160571", "3. low": "15866.2863495", "4. close": "16715.7591217", "5. adjusted close": "16715.7591217", "6. volume": "102392", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-15": {"1. open": "16070.5778865", "2. high": "16178.0384555", "3. low": "15777.3270033", "4. close": "15958.6409481", "5. adjusted close": "15958.6409481", "6. volume": "52727", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-14": {"1. open": "16321.8380188", "2. high": "16328.7099621", "3. low": "15678.184264", "4. close": "16069.6675223", "5. adjusted close": "16069.6675223", "6. volume": "74004", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-13": {"1. open": "16295.6047964", "2. high": "16482.4343153", "3. low": "15950.4382886", "4. close": "16322.4886622", "5. adjusted close": "16322.4886622", "6. volume": "99004", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-12": {"1. open": "15690.4227832", "2. high": "16344.9885672", "3. low": "15443.2569748", "4. close": "16295.8023695", "5. adjusted close": "16295.8023695", "6. volume": "140216", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-11": {"1. open": "15301.388662", "2. high": "15972.1621584", "3. low": "15277.3682924", "4. close": "15689.7462323", "5. adjusted close": "15689.7462323", "6. volume": "103416", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-10": {"1. open": "15331.6460828", "2. high": "15464.7472623", "3. low": "15076.3594924", "4. close": "15301.4461545", "5. adjusted close": "15301.4461545", "6. volume": "81545", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-09": {"1. open": "15478.1213781", "2. high": "15845.7961823", "3. low": "14807.803926", "4. close": "15331.2558082", "5. adjusted close": "15331.2558082", "6. volume": "142247", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-08": {"1. open": "14823.2717292", "2. high": "15653.9594359", "3. low": "14710.4618626", "4. close": "15478.2766597", "5. adjusted close": "15478.2766597", "6. volume": "85849", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-07": {"1. open": "15583.1459551", "2. high": "15756.9007479", "3. low": "14324.2638644", "4. close": "14824.105818", "5. adjusted close": "14824.105818", "6. volume": "142174", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-06": {"1. open": "15588.5404482", "2. high": "15957.3365256", "3. low": "15175.3233291", "4. close": "15579.9966459", "5. adjusted close": "15579.9966459", "6. volume": "165939", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-05": {"1. open": "14148.7439833", "2. high": "15747.2992129", "3. low": "14098.4310149", "4. close": "15582.7363925", "5. adjusted close": "15582.7363925", "6. volume": "209434", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-04": {"1. open": "14025.6260144", "2. high": "14263.748112", "3. low": "13525.5205026", "4. close": "14146.9476771", "5. adjusted close": "14146.9476771", "6. volume": "120776", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-03": {"1. open": "13552.3499462", "2. high": "14068.4973004", "3. low": "13286.7700438", "4. close": "14024.8522363", "5. adjusted close": "14024.8522363", "6. volume": "94572", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-02": {"1. open": "13762.5633014", "2. high": "13832.0034754", "3. low": "13196.7779015", "4. close": "13552.4916547", "5. adjusted close": "13552.4916547", "6. volume": "84020", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-11-01": {"1. open": "13791.9395092", "2. high": "13896.4339751", "3. low": "13607.4173116", "4. close": "13762.5272255", "5. adjusted close": "13762.5272255", "6. volume": "45961", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-31": {"1. open": "13562.5088503", "2. high": "14095.4492686", "3. low": "13414.802757", "4. close": "13792.6877245", "5. adjusted close": "13792.6877245", "6. volume": "89109", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-30": {"1. open": "13456.158282", "2. high": "13673.2685421", "3. low": "13118.1377306", "4. close": "13562.5593583", "5. adjusted close": "13562.5593583", "6. volume": "91448", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-29": {"1. open": "13268.8928601", "2. high": "13647.1317677", "3. low": "12929.9556077", "4. close": "13456.2374277", "5. adjusted close": "13456.2374277", "6. volume": "95355", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-28": {"1. open": "13658.1020741", "2. high": "13859.0473388", "3. low": "12888.3558279", "4. close": "13269.0063681", "5. adjusted close": "13269.0063681", "6. volume": "123086", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-27": {"1. open": "13055.4782246", "2. high": "13787.3976611", "3. low": "13029.6295435", "4. close": "13659.0179178", "5. adjusted close": "13659.0179178", "6. volume": "107811", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-26": {"1. open": "13032.4575907", "2. high": "13258.2996275", "3. low": "12763.9983714", "4. close": "13055.0362084", "5. adjusted close": "13055.0362084", "6. volume": "77518", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-25": {"1. open": "13115.5355918", "2. high": "13352.642333", "3. low": "12877.0803803", "4. close": "13033.6428948", "5. adjusted close": "13033.6428948", "6. volume": "51903", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-24": {"1. open": "12925.9948938", "2. high": "13168.6810037", "3. low": "12873.0436244", "4. close": "13114.9531255", "5. adjusted close": "13114.9531255", "6. volume": "46079", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-23": {"1. open": "12972.654293", "2. high": "13029.4346397", "3. low": "12719.3409151", "4. close": "12926.0199605", "5. adjusted close": "12926.0199605", "6. volume": "64104", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-22": {"1. open": "12787.8497661", "2. high": "13188.9921351", "3. low": "12684.3396884", "4. close": "12973.7363225", "5. adjusted close": "12973.7363225", "6. volume": "97287", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-21": {"1. open": "11913.9503499", "2. high": "13224.5052672", "3. low": "11891.803755", "4. close": "12789.2839959", "5. adjusted close": "12789.2839959", "6. volume": "163565", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-20": {"1. open": "11753.3648667", "2. high": "12040.8651419", "3. low": "11680.1370453", "4. close": "11913.4513609", "5. adjusted close": "11913.4513609", "6. volume": "82222", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-19": {"1. open": "11505.942442", "2. high": "11841.4899392", "3. low": "11409.8079976", "4. close": "11753.3633126", "5. adjusted close": "11753.3633126", "6. volume": "62091", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-18": {"1. open": "11362.1542265", "2. high": "11508.4750251", "3. low": "11347.7163823", "4. close": "11505.552238", "5. adjusted close": "11505.552238", "6. volume": "29147", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-17": {"1. open": "11320.7021901", "2. high": "11405.5621949", "3. low": "11257.0682258", "4. close": "11361.8444037", "5. adjusted close": "11361.8444037", "6. volume": "27221", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-16": {"1. open": "11506.1326906", "2. high": "11542.8711383", "3. low": "11200.8251635", "4. close": "11321.1378692", "5. adjusted close": "11321.1378692", "6. volume": "62278", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-15": {"1. open": "11420.8138161", "2. high": "11621.7273323", "3. low": "11254.4359244", "4. close": "11505.8916206", "5. adjusted close": "11505.8916206", "6. volume": "62105", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-14": {"1. open": "11422.870052", "2. high": "11550.0147559", "3. low": "11283.077471", "4. close": "11420.4336024", "5. adjusted close": "11420.4336024", "6. volume": "51638", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-13": {"1. open": "11531.6989752", "2. high": "11558.9919214", "3. low": "11304.3980096", "4. close": "11423.337925", "5. adjusted close": "11423.337925", "6. volume": "55236", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-12": {"1. open": "11371.1900085", "2. high": "11724.4306615", "3. low": "11168.8780025", "4. close": "11532.3151677", "5. adjusted close": "11532.3151677", "6. volume": "71116", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-11": {"1. open": "11294.9449041", "2. high": "11445.2237535", "3. low": "11232.1294389", "4. close": "11370.4552367", "5. adjusted close": "11370.4552367", "6. volume": "36296", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-10": {"1. open": "11053.2240142", "2. high": "11493.3549289", "3. low": "11052.9005153", "4. close": "11295.3519842", "5. adjusted close": "11295.3519842", "6. volume": "57828", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-09": {"1. open": "10927.3829636", "2. high": "11108.6855616", "3. low": "10830.9812328", "4. close": "11052.6455638", "5. adjusted close": "11052.6455638", "6. volume": "61604", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-08": {"1. open": "10668.2406083", "2. high": "10953.4194289", "3. low": "10530.181201", "4. close": "10927.8077266", "5. adjusted close": "10927.8077266", "6. volume": "67366", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-07": {"1. open": "10600.3972369", "2. high": "10682.5226353", "3. low": "10546.9606706", "4. close": "10667.8723195", "5. adjusted close": "10667.8723195", "6. volume": "40891", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-06": {"1. open": "10793.6249323", "2. high": "10801.4197843", "3. low": "10525.5356824", "4. close": "10600.6085445", "5. adjusted close": "10600.6085445", "6. volume": "59902", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-05": {"1. open": "10667.9678676", "2. high": "10799.5952753", "3. low": "10617.3843534", "4. close": "10793.3685418", "5. adjusted close": "10793.3685418", "6. volume": "42207", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-04": {"1. open": "10544.0504852", "2. high": "10697.7975136", "3. low": "10519.7235279", "4. close": "10668.8314138", "5. adjusted close": "10668.8314138", "6. volume": "28661", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-03": {"1. open": "10571.2521551", "2. high": "10604.3144547", "3. low": "10497.1985999", "4. close": "10543.7998501", "5. adjusted close": "10543.7998501", "6. volume": "26545", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-02": {"1. open": "10619.418828", "2. high": "10666.4977976", "3. low": "10373.356877", "4. close": "10571.746921", "5. adjusted close": "10571.746921", "6. volume": "64437", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-10-01": {"1. open": "10778.5666828", "2. high": "10925.894102", "3. low": "10437.15412", "4. close": "10619.8845875", "5. adjusted close": "10619.8845875", "6. volume": "80128", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-30": {"1. open": "10841.0189693", "2. high": "10849.5284914", "3. low": "10662.1160547", "4. close": "10777.565205", "5. adjusted close": "10777.565205", "6. volume": "47731", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-29": {"1. open": "10696.3901313", "2. high": "10867.7591819", "3. low": "10636.5115122", "4. close": "10841.0597673", "5. adjusted close": "10841.0597673", "6. volume": "52242", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-28": {"1. open": "10776.2120472", "2. high": "10953.0457294", "3. low": "10627.1330439", "4. close": "10697.1062234", "5. adjusted close": "10697.1062234", "6. volume": "63349", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-27": {"1. open": "10729.8184754", "2. high": "10801.013737", "3. low": "10594.817634", "4. close": "10776.0810384", "5. adjusted close": "10776.0810384", "6. volume": "36546", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-26": {"1. open": "10687.6533143", "2. high": "10822.3700117", "3. low": "10646.5841147", "4. close": "10729.3719517", "5. adjusted close": "10729.3719517", "6. volume": "33393", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-25": {"1. open": "10737.5757617", "2. high": "10761.805872", "3. low": "10544.3619352", "4. close": "10688.1403924", "5. adjusted close": "10688.1403924", "6. volume": "60810", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}, "2020-09-24": {"1. open": "10241.9685007", "2. high": "10797.4584255", "3. low": "10192.5767381", "4. close": "10737.6310446", "5. adjusted close": "10737.6310446", "6. volume": "71238", "7. dividend amount": "0.0000", "8. split coefficient": "1.0"}}} \ No newline at end of file diff --git a/learning/requests/send-rest.py b/learning/requests/send-rest.py new file mode 100644 index 0000000..650b63a --- /dev/null +++ b/learning/requests/send-rest.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +################################################################ +# This is an example I have used to send a REST call to Azure Event Grid +################################################################ + +from datetime import datetime, timezone +import requests +import json +import collections + +def send_req(subject, payload, key): + + date1 = datetime.now(timezone.utc) + date2 = (str(date1).replace(" ", "T").replace("+00:00", "0")) + 'Z' + + # generate body + body = collections.OrderedDict() + body["body"] = payload + + # insert into main body + data = collections.OrderedDict() + data["id"] = "MyApp" + data["subject"] = subject + data["topic"] = "somePub" + data["eventType"] = "MyAppApproval" + data["eventTime"] = date2 + data["data"] = body + data["dataVersion"] = "1.0" + data["metadataVersion"] = None + #print("data: ", data) + + headers = collections.OrderedDict() + headers['Content-Type'] = 'application/json' + headers['aeg-sas-key'] = key + #print("headers: ", headers) + + data2 = "[" + json.dumps(data) + "]" + #print("data string: ", data2) + + url = "https://mycompany.southcentralus-1.eventgrid.azure.net/api/events" + r = requests.post(url, headers=headers, data=data2) + # print(r.status_code) + return r + + +s = '85d89569-5c9e-4fc6-a394-7bb9d724b614' +p = '{"AppName": "SomeApp", "PermName": "SomePermission", "ChangeRequestType": "ADD"}' +k = 'SbBjasdfasdf' +req = send_req(s, p, k) +print(req.status_code) + +if req.status_code == 200: + print("good") +else: + pass \ No newline at end of file diff --git a/learning/requests/wttr-in.py b/learning/requests/wttr-in.py new file mode 100644 index 0000000..b3b5f74 --- /dev/null +++ b/learning/requests/wttr-in.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using python to replace my powershell function at https://automationadmin.com/2017/12/use-powershell-to-get-weather-using-wttr-in/ + +# Powershell to convert: +# If ($City) +# { +# Write-Log "Getting weather for $City" +# (curl http://wttr.in/$City -UserAgent "curl" ).Content +# # Cannot substitue "curl" for "Invoke-WebRequest -URI" .... +# } +# ElseIf ($JustToday) +# { +# Write-Log "Getting weather for today's current location" +# (curl http://wttr.in/?0 -UserAgent "curl" ).Content +# } +# ElseIf ($TwoDays) +# { +# Write-Log "Getting two days weather forcast for current location" +# (curl http://wttr.in/?2 -UserAgent "curl" ).Content +# } +# ElseIf ($ByZip) +# { +# Write-Log "Getting weather for zipcode $ByZip" +# (curl http://wttr.in/$ByZip -UserAgent "curl" ).Content +# } +# ElseIf ($Moon) +# { +# Write-Log "Getting today's moon phase" +# (curl http://wttr.in/moon -UserAgent "curl" ).Content +# } +# ElseIf ($MoonOnDate) +# { +# Write-Log "Getting moon phase for $MoonOnDate" +# (curl http://wttr.in/moon@$MoonOnDate -UserAgent "curl" ).Content +# } +# Else +# { +# Write-Log "Getting weather for current location" +# (curl http://wttr.in -UserAgent "curl" ).Content +# } +################################################################ + +import requests + +url = "http://wttr.in/FortWorth.json" +payload = {} +headers = { +'Content-Type': 'application/json', +} +r = requests.request("GET", url, headers=headers, data=payload) + +## why is this not showing? +# print(r) +# print(r.json) +# print(r.content) + +# Lol, https://raw.githubusercontent.com/hasha2982/wttr.py/master/wttrpy/__init__.py shows this +print(r.text) + +#print(dir(r)) + diff --git a/learning/run-native-commands/run-native-commands.py b/learning/run-native-commands/run-native-commands.py new file mode 100644 index 0000000..7acb413 --- /dev/null +++ b/learning/run-native-commands/run-native-commands.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of subprocess and running native commands in python +# Have not tested extensively +################################################################ + +import subprocess +from os import system, name + +def run_this(): + if name == 'nt': + process_name = 'ipconfig' + print('Running: ', process_name) + st = subprocess.run(process_name, shell=True, stdout=subprocess.PIPE) + print(st.stdout) + print(type(st)) + else: + process_name = 'systemctl status firewalld' + print('Running: ', process_name) + st = subprocess.run(process_name, shell=True, stdout=subprocess.PIPE) + print(st) + return f"completed running: {process_name}" + +def run_this_2(): + if name == 'nt': + process_name = 'ipconfig' + print('Running: ', process_name) + st = subprocess.run([process_name, '/all'], shell=True, check=True) + print(st) + else: + process_name = 'systemctl status firewalld' + print('Running: ', process_name) + st = subprocess.run(process_name, shell=True, stdout=subprocess.PIPE) + print(st) + return f"completed running: {process_name}" + + +command = run_this() +print(command) + +command2 = run_this_2() +print(command2) \ No newline at end of file diff --git a/learning/send-email/send-email-gmail-2.py b/learning/send-email/send-email-gmail-2.py new file mode 100644 index 0000000..78d0189 --- /dev/null +++ b/learning/send-email/send-email-gmail-2.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of sending email from a '@gmail.com' account +# Tested and confirmed working 2021/01/10 +# This does not work since all the tabs and newlines are ignored +# see 3 for working copy +################################################################ + +import smtplib +from email.mime.text import MIMEText +import os +from dotenv import load_dotenv + +load_dotenv() + +try: + email_user = os.environ["GMAIL_USER"] + email_password = os.environ["GMAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +def send_email(user, password, to): + + text = 'Gerry\nPlease see the following:\n1\tJan\n2\tFeb' + + html = """\ + + + + +

{}

+ + + """.format(text) + msg = MIMEText(html, 'html') + msg['Subject'] = 'Test message' + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +send = send_email(email_user, email_password, email_to) +print(send) \ No newline at end of file diff --git a/learning/send-email/send-email-gmail-3.py b/learning/send-email/send-email-gmail-3.py new file mode 100644 index 0000000..ebb406d --- /dev/null +++ b/learning/send-email/send-email-gmail-3.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of sending email from a '@gmail.com' account +# Tested and confirmed working 2021/01/10 +################################################################ + +import smtplib +from email.mime.text import MIMEText +import os +from dotenv import load_dotenv + +load_dotenv() + +try: + email_user = os.environ["GMAIL_USER"] + email_password = os.environ["GMAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +def send_email(user, password, to): + + text = 'Dear Person
Please see the following month:
1 Jan
2 Feb' + + html = """\ + + + + +

{}

+ + + """.format(text) + msg = MIMEText(html, 'html') + msg['Subject'] = 'Test message' + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +send = send_email(email_user, email_password, email_to) +print(send) \ No newline at end of file diff --git a/learning/send-email/send-email-gmail.py b/learning/send-email/send-email-gmail.py new file mode 100644 index 0000000..94b8d35 --- /dev/null +++ b/learning/send-email/send-email-gmail.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of sending email from a '@gmail.com' account +# Tested and confirmed working 2021/01/10 +################################################################ + +import smtplib +from email.mime.text import MIMEText +import os +from dotenv import load_dotenv + +load_dotenv() + +try: + email_user = os.environ["GMAIL_USER"] + email_password = os.environ["GMAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +def send_email(user, password, to): + + html = """\ + + + + +

example body

+ + + """ + msg = MIMEText(html, 'html') + msg['Subject'] = 'Test message' + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +send = send_email(email_user, email_password, email_to) +print(send) \ No newline at end of file diff --git a/learning/send-email/send-email-office.py b/learning/send-email/send-email-office.py new file mode 100644 index 0000000..dae17da --- /dev/null +++ b/learning/send-email/send-email-office.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of sending email from an '@outlook.com' account +# Tested and confirmed working 2021/01/10 +################################################################ + + +import smtplib +from email.mime.text import MIMEText +import os +from dotenv import load_dotenv + +load_dotenv() + +try: + email_user = os.environ["MAIL_USER"] + email_password = os.environ["MAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + print("Unable to get environmental variables") +except Exception as e: + print("Generic catch: Unable to get environmental variables") + print("Generic catch: " + str(e)) + +def send_email(user, password, to): + + html = """\ + + + + + This is a sample body + + + """ + msg = MIMEText(html, 'html') + msg['Subject'] = 'Test message' + + mailserver = smtplib.SMTP('smtp.office365.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +send = send_email(email_user, email_password, email_to) +print(send) \ No newline at end of file diff --git a/learning/vscode-black-formatter.py b/learning/vscode-black-formatter.py new file mode 100644 index 0000000..565b9ee --- /dev/null +++ b/learning/vscode-black-formatter.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 + +################################################################ +# Using the vscode 'black' formatter for python files +# https://www.geeksforgeeks.org/python-code-formatting-using-black/ +################################################################ + +''' +Before: +def function(name, default=None, *args, variable="1123", a, b, c, employee, office, d, e, f, **kwargs): + """This is function is created to demonstrate black""" + + +string = 'GeeksforGeeks' + +j = [1, + 2, + 3] + +''' + + +def function( + name, + default=None, + *args, + variable="1123", + a, + b, + c, + employee, + office, + d, + e, + f, + **kwargs +): + """This is function is created to demonstrate black""" + + +string = "GeeksforGeeks" + +j = [1, 2, 3] + + +''' + + +Before: +def is_unique( + s + ): + s = list(s + ) + s.sort() + + + for i in range(len(s) - 1): + if s[i] == s[i + 1]: + return 0 + else: + return 1 + + +if __name__ == "__main__": + print( + is_unique(input()) + ) + + +''' + + +def is_unique(s): + s = list(s) + s.sort() + + for i in range(len(s) - 1): + if s[i] == s[i + 1]: + return 0 + else: + return 1 + + +if __name__ == "__main__": + print(is_unique(input())) diff --git a/scripts/azure-durable-functions-basic/.funcignore b/scripts/azure-durable-functions-basic/.funcignore new file mode 100644 index 0000000..0678ea2 --- /dev/null +++ b/scripts/azure-durable-functions-basic/.funcignore @@ -0,0 +1,5 @@ +.git* +.vscode +local.settings.json +test +.venv \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/.gitignore b/scripts/azure-durable-functions-basic/.gitignore new file mode 100644 index 0000000..a10127b --- /dev/null +++ b/scripts/azure-durable-functions-basic/.gitignore @@ -0,0 +1,130 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don’t work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json +.python_packages \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/MyActivity/__init__.py b/scripts/azure-durable-functions-basic/MyActivity/__init__.py new file mode 100644 index 0000000..a900d98 --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyActivity/__init__.py @@ -0,0 +1,92 @@ + +################################################## +# This function is not intended to be invoked directly. Instead it will be +# triggered by an orchestrator function. +# Before running this sample, please: +# - create a Durable orchestration function +# - create a Durable HTTP starter function +# - add azure-functions-durable to requirements.txt +# - run pip install -r requirements.txt + + +# This function is called when a request is sent from Service Now from a catalog item. +# This function follows the template: One permission per request. +# It expects a response in the format of: {"response": "SUCCESS: current_date"} +# It will parse the response into separate values for each side of the semicolon + +# Example payload: +# { +# "sysid": "489e0ab2dbc5a0500acb38f0ad961988", +# "number": "REQ0269517", +# "description": "some text" +# } + +################################################## + +import sys +import os +import logging +import azure.functions as func +import requests +import json +from datetime import datetime, timedelta +import time +from azure.cosmosdb.table.tableservice import TableService +from azure.cosmosdb.table.models import Entity +from shared import helpers + + +def main(payload: dict) -> dict: + + data = dict(payload) + + #region => Parse the input + + sys_id = data["sysid"] + logging.info(f"Extracted Service Now SysID: {sys_id}") + + req_number = data["number"] + logging.info(f"Extracted Request Number: {req_number}") + + description = data["description"] + logging.info(f"Extracted Description: {description}") + + # Check to ensure each are populated with something + try: + if all(v is not None for v in [sys_id, req_number, description]): + pass + else: + e = helpers.err_response( + "One of the variables populated at this point has a value of None" + ) + return e + + except NameError as name: + e = helpers.err_response( + f"One of the variables is missing which should be populated at this point => Error details: {name}" + ) + return e + except Exception as e: + e = helpers.err_response( + f"Unhandled exception: {str(e)}" + ) + return e + + #endregion => Parse the input + + #region => Start the main function + try: + + logging.info("==========Starting Step 1: Do something with these variables==========") + + # Script removed + + pastdate = datetime.now() + timedelta(days=0, hours=-6, minutes=0) + date = pastdate.strftime("%Y-%m-%d-%r") + response = f"SUCCESS: {date}" + rspJson = json.dumps({"response": response }) + logging.info(f"Sending Response: {rspJson}") + return rspJson + #endregion => Response + except Exception as e: + logging.info(f"Generic Catch: {str(e)}") \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/MyActivity/function.json b/scripts/azure-durable-functions-basic/MyActivity/function.json new file mode 100644 index 0000000..531987a --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyActivity/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "payload", + "type": "activityTrigger", + "direction": "in" + } + ] +} diff --git a/scripts/azure-durable-functions-basic/MyHTTP/__init__.py b/scripts/azure-durable-functions-basic/MyHTTP/__init__.py new file mode 100644 index 0000000..face8e9 --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyHTTP/__init__.py @@ -0,0 +1,39 @@ +# This function an HTTP starter function for Durable Functions. +# Before running this sample, please: +# - create a Durable orchestration function +# - create a Durable activity function (default name is "Hello") +# - add azure-functions-durable to requirements.txt +# - run pip install -r requirements.txt + +import logging +import azure.functions as func +import azure.durable_functions as df + +async def main(req: func.HttpRequest, starter: str) -> func.HttpResponse: + + req_body = req.get_json() + + description = req_body["description"] + logging.info(f"Extracted Description: {description}") + + req_number = req_body["number"] + logging.info(f"Extracted Request Number: {req_number}") + + sys_id = req_body["sysid"] + logging.info(f"Extracted Service Now SysID: {sys_id}") + + payload = { + "description": description, + "number": req_number, + "sysid": sys_id + } + + logging.info("Starting Async Orchestration for MyOrch...") + logging.info(f"Sending payload to Orchestrator Function extracted from input: {payload}") + + client = df.DurableOrchestrationClient(starter) + instance_id = await client.start_new('MyOrch', None, payload ) + + logging.info(f"Started orchestration with ID = '{instance_id}'.") + + return client.create_check_status_response(req, instance_id) \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/MyHTTP/function.json b/scripts/azure-durable-functions-basic/MyHTTP/function.json new file mode 100644 index 0000000..36d803d --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyHTTP/function.json @@ -0,0 +1,26 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "name": "req", + "type": "httpTrigger", + "direction": "in", + "route": "orchestrators/test_mymav-access-orch", + "methods": [ + "post", + "get" + ] + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "name": "starter", + "type": "orchestrationClient", + "direction": "in" + } + ] +} diff --git a/scripts/azure-durable-functions-basic/MyOrch/__init__.py b/scripts/azure-durable-functions-basic/MyOrch/__init__.py new file mode 100644 index 0000000..0a74eac --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyOrch/__init__.py @@ -0,0 +1,29 @@ +# This function is not intended to be invoked directly. Instead it will be +# triggered by an HTTP starter function. +# Before running this sample, please: +# - create a Durable activity function (default name is "Hello") +# - create a Durable HTTP starter function +# - add azure-functions-durable to requirements.txt +# - run pip install -r requirements.txt + +import logging +import json + +import azure.functions as func +import azure.durable_functions as df + +def orchestrator_function(context: df.DurableOrchestrationContext): + + input = context.get_input() + payload = dict(input) + + logging.info("Extracting information from payload for MyActivity....") + logging.info(f"Sending payload to Activity Function extracted from HTTP: {payload}") + + result = yield context.call_activity('MyActivity', payload) + + logging.info("Completed Async Orchestration") + + return [result] + +main = df.Orchestrator.create(orchestrator_function) \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/MyOrch/function.json b/scripts/azure-durable-functions-basic/MyOrch/function.json new file mode 100644 index 0000000..83baac6 --- /dev/null +++ b/scripts/azure-durable-functions-basic/MyOrch/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/scripts/azure-durable-functions-basic/host.json b/scripts/azure-durable-functions-basic/host.json new file mode 100644 index 0000000..c094115 --- /dev/null +++ b/scripts/azure-durable-functions-basic/host.json @@ -0,0 +1,22 @@ +{ + "version": "2.0", + "logging": { + "fileLoggingMode": "debugOnly", + "logLevel": { + "Function.MyHttp": "Information", + "Function.MyOrch": "Information", + "Function.MyActivity": "Information", + "default": "None" + }, + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[2.*, 3.0.0)" + } +} \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/proxies.json b/scripts/azure-durable-functions-basic/proxies.json new file mode 100644 index 0000000..b385252 --- /dev/null +++ b/scripts/azure-durable-functions-basic/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/scripts/azure-durable-functions-basic/requirements.txt b/scripts/azure-durable-functions-basic/requirements.txt new file mode 100644 index 0000000..7d68809 --- /dev/null +++ b/scripts/azure-durable-functions-basic/requirements.txt @@ -0,0 +1,6 @@ +# DO NOT include azure-functions-worker in this file +# The Python Worker is managed by Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +azure-functions +azure-functions-durable \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/shared/__init__.py b/scripts/azure-durable-functions-basic/shared/__init__.py new file mode 100644 index 0000000..41d7bd8 --- /dev/null +++ b/scripts/azure-durable-functions-basic/shared/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/python3 + +################################################## +# Helper function entry file +################################################## \ No newline at end of file diff --git a/scripts/azure-durable-functions-basic/shared/helpers.py b/scripts/azure-durable-functions-basic/shared/helpers.py new file mode 100644 index 0000000..3b88b60 --- /dev/null +++ b/scripts/azure-durable-functions-basic/shared/helpers.py @@ -0,0 +1,158 @@ +#!/usr/bin/python3 + +################################################## +# Helper functions +# +# Ctrl+Shift+[ Fold (collapse) region editor.fold +# Ctrl+Shift+] Unfold (uncollapse) region editor.unfold +# Ctrl+K Ctrl+[ Fold (collapse) all subregions editor.foldRecursively +# Ctrl+K Ctrl+] Unfold (uncollapse) all subregions editor.unfoldRecursively +# Ctrl+K Ctrl+0 Fold (collapse) all regions editor.foldAll +# Ctrl+K Ctrl+J Unfold (uncollapse) all regions +################################################## + +### Request Module Functions Template: +# credentials +# \n +# headers = {} +# \n +# url +# \n +# payload +# \n +# r = requests.request("PUT", url, auth=(u, p), headers=headers, data=json.dumps(payload)) +# if r.status_code != 200: +# err_resp = f"Status Code Details:\n" \ +# f"Status code returned: {r.status_code}\n" \ +# f"Headers sent: {r.headers}\n" \ +# f"Body sent: {payload}\n" \ +# f"API response: {r.json()}" +# logging.error(err_resp) + +# return r + +#local testing +# from dotenv import load_dotenv +# load_dotenv() + +import sys +import os +import logging +import requests +import json +from datetime import datetime, timedelta +import time + +def err_response(log_msg): + ''' + Formats an error string to be sent as a response to the main function. + + Example of how to call from main(): + e = helpers.err_response("Posting Request to IDG resulted in ERROR_STARTING_APPROVAL") + return func.HttpResponse(e, status_code=400) + + Older way without function in main(): + err_response = f"ERROR: An error occurred while attempting to verify table record => {str(e)}" + errRspJson = json.dumps({"response": err_response }) + logging.error(f"Sending Response: {errRspJson}") + return func.HttpResponse(errRspJson, status_code=400) + ''' + err_msg = f"ERROR: {log_msg}" + errRspJson = json.dumps({"response": err_msg }) + logging.error(f"Sending Response: {errRspJson}") + return errRspJson + +def import_creds(): + ''' + Gets secrets from Azure Keyvault as an application + Secret values are stored in the Function App under Configuration + + Example of how to call from main(): + keyvault_creds = import_creds() + logging.info(f"client_id: { keyvault_creds['client_id'] }") + logging.info(f"client_secret: { keyvault_creds['client_secret'] }") + logging.info(f"tenant: { keyvault_creds['tenant'] }") + logging.info(f"vault_name: { keyvault_creds['vault_name'] }") + ''' + + try: + creds = {} + creds["client_id"] = os.environ["client_id"] + creds["client_secret"] = os.environ["client_secret"] + creds["tenant"] = os.environ["tenant"] + creds["vault_name"] = os.environ["vault_name"] + except KeyError: + logging.error("Unable to get env vars") + except Exception as e: + logging.error(f"Generic Catch: {str(e)}") + + return creds + + +def get_oauth(tenant, client_id, client_secret): + ''' + Gets an Oauth token from Graph API as an application + See the scope in the payload where the application is scoped to just Key Vault + + Example of how to call from main(): + token = get_oauth( + tenant = keyvault_creds['tenant'], + client_id = keyvault_creds['client_id'], + client_secret = keyvault_creds['client_secret'] + ) + ''' + url = f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" + payload = "grant_type=client_credentials"\ + f"&client_id={client_id}"\ + f"&client_secret={client_secret}"\ + "&scope=https%3A%2F%2Fvault.azure.net%2F.default" + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + response = requests.request("POST", url, headers=headers, data=payload) + r = response.json() + return r + + +def get_secret(token, vault_name, secret_name): + ''' + Using an Oauth token, gets the latest version of a secret + + Example of how to call from main(): + secret = get_secret( + token = token["access_token"], + vault_name = keyvault_creds['vault_name'], + secret_name = 'test' + ) + logging.info(f"Secret value is : {secret['value']}") + ''' + # Gets the latest version of a secret + url = f"https://{vault_name}.vault.azure.net/secrets/{secret_name}?api-version=7.1" + + # for a specific version of the secret, replace {version} + # version = "aadsfasdfasdfasdf" + # url = f"https://{vault_name}.vault.azure.net/secrets/test/{version}?api-version=7.1" + + payload = {} + headers = {"Authorization": f"Bearer {token}"} + response = requests.request("GET", url, headers=headers, data=payload) + r = response.json() + return r + + +def get_sn_username(): + ''' + Using a combination of get_oauth() and get_secret() functions, + This will get an oauth token and retrieve the latest version of a secret + + Example of how to call from main(): + sn_username = get_sn_username() + logging.info(f"Secret value is : {sn_username}") + ''' + keyvault_creds = import_creds() + token = get_oauth(tenant=keyvault_creds['tenant'], + client_id=keyvault_creds['client_id'], + client_secret=keyvault_creds['client_secret']) + secret = get_secret(token=token["access_token"], + vault_name=keyvault_creds['vault_name'], + secret_name='Service-Now-User') + return secret['value'] + diff --git a/scripts/azure-function-template-basic/.funcignore b/scripts/azure-function-template-basic/.funcignore new file mode 100644 index 0000000..0678ea2 --- /dev/null +++ b/scripts/azure-function-template-basic/.funcignore @@ -0,0 +1,5 @@ +.git* +.vscode +local.settings.json +test +.venv \ No newline at end of file diff --git a/scripts/azure-function-template-basic/.gitignore b/scripts/azure-function-template-basic/.gitignore new file mode 100644 index 0000000..dab6202 --- /dev/null +++ b/scripts/azure-function-template-basic/.gitignore @@ -0,0 +1,133 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don’t work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json +.python_packages + +# custom vscode settings +.vscdoe \ No newline at end of file diff --git a/scripts/azure-function-template-basic/README.md b/scripts/azure-function-template-basic/README.md new file mode 100644 index 0000000..e42354e --- /dev/null +++ b/scripts/azure-function-template-basic/README.md @@ -0,0 +1,10 @@ +### Why Function Apps? + +1. Speed - Time will be reduced in half or more moving from "half serverless" (Azure Automation) to "full serverless" automation technologies. + - It takes 30+ seconds for an Azure Automation runbook to start, it takes Function Apps a fraction of a second. + +2. Support - More team members know python than powershell. + +3. Scale - unlimited concurrent runs. + +4. Maintenance - Azure Automation requires dedicated servers which means patching and security concerns, Function Apps are completely serverless. \ No newline at end of file diff --git a/scripts/azure-function-template-basic/get_ip/__init__.py b/scripts/azure-function-template-basic/get_ip/__init__.py new file mode 100644 index 0000000..ae160b8 --- /dev/null +++ b/scripts/azure-function-template-basic/get_ip/__init__.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 + +################################################## +# This function will return the public ip of the function app +# No input is needed for this function + +################################################## + +import sys +import logging +import azure.functions as func +import os +import time +from datetime import datetime, timedelta +import json +import requests + +def main(req: func.HttpRequest) -> func.HttpResponse: + + url = "https://ifconfig.me/ip" + payload = {} + headers = { + 'Content-Type': 'application/json', + } + r = requests.request("GET", url, headers=headers, data=payload) + + response = r.text + logging.info(f"Response: {response}") + + ### Response + pastdate = datetime.now() + timedelta(days=0, hours=-6, minutes=0) + date = pastdate.strftime("%Y-%m-%d-%r") + + rspJson = json.dumps([{ + "response": response, + "date_time": date + }]) + return func.HttpResponse(rspJson, status_code=200) + +if __name__ == '__main__': + main(req) +else: + logging.error("Function not called correctly, please try again.") \ No newline at end of file diff --git a/scripts/azure-function-template-basic/get_ip/function.json b/scripts/azure-function-template-basic/get_ip/function.json new file mode 100644 index 0000000..b8dc650 --- /dev/null +++ b/scripts/azure-function-template-basic/get_ip/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/scripts/azure-function-template-basic/host.json b/scripts/azure-function-template-basic/host.json new file mode 100644 index 0000000..c3d6e34 --- /dev/null +++ b/scripts/azure-function-template-basic/host.json @@ -0,0 +1,18 @@ +{ + "version": "2.0", + "logging": { + "fileLoggingMode": "debugOnly", + "logLevel": { + "Function.get_ip": "Information", + "Function.parse_desc": "Information", + "default": "Error" + }, + "applicationInsights": { + "httpAutoCollectionOptions": { + "enableHttpTriggerExtendedInfoCollection": true, + "enableW3CDistributedTracing": true, + "enableResponseHeaderInjection": true + } + } + } +} \ No newline at end of file diff --git a/scripts/azure-function-template-basic/parse_desc/__init__.py b/scripts/azure-function-template-basic/parse_desc/__init__.py new file mode 100644 index 0000000..6000b1e --- /dev/null +++ b/scripts/azure-function-template-basic/parse_desc/__init__.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 + +################################################## +# This function will return a parsed description from a passed in string +# { +# "description": "something" +# } +################################################## + +import sys +import logging +import azure.functions as func +import os +import time +from datetime import datetime, timedelta +import json +import requests + +def main(req: func.HttpRequest) -> func.HttpResponse: + + req_body = req.get_json() + + description = req_body["description"] + logging.info(f"Extracted description: {description}") + + splitDesc = description.split("\n") + + split_list = [x.strip() for x in splitDesc[8].split(":")[1].split(',')] + logging.info(f"Extracted split list: {split_list}") + + department = splitDesc[3].split(":")[1].strip() + logging.info(f"Extracted department: {department}") + + split_list_string = ','.join(split_list) + + ### Response + pastdate = datetime.now() + timedelta(days=0, hours=-6, minutes=0) + date = pastdate.strftime("%Y-%m-%d-%r") + + + rspJson = json.dumps([{ + "serial_numbers": split_list_string, + "department": department, + "date_time": date + }]) + return func.HttpResponse(rspJson, status_code=200) + +if __name__ == '__main__': + main(req) +else: + logging.error("Function not called correctly, please try again.") \ No newline at end of file diff --git a/scripts/azure-function-template-basic/parse_desc/function.json b/scripts/azure-function-template-basic/parse_desc/function.json new file mode 100644 index 0000000..b8dc650 --- /dev/null +++ b/scripts/azure-function-template-basic/parse_desc/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/scripts/azure-function-template-basic/proxies.json b/scripts/azure-function-template-basic/proxies.json new file mode 100644 index 0000000..b385252 --- /dev/null +++ b/scripts/azure-function-template-basic/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/scripts/azure-function-template-basic/requirements.txt b/scripts/azure-function-template-basic/requirements.txt new file mode 100644 index 0000000..5d05b8d --- /dev/null +++ b/scripts/azure-function-template-basic/requirements.txt @@ -0,0 +1,6 @@ +# DO NOT include azure-functions-worker in this file +# The Python Worker is managed by Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +azure-functions +requests==2.25.0 \ No newline at end of file diff --git a/scripts/azure-function-template-basic/shared/__init__.py b/scripts/azure-function-template-basic/shared/__init__.py new file mode 100644 index 0000000..70a59bc --- /dev/null +++ b/scripts/azure-function-template-basic/shared/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/python3 + +################################################## +# Helper function entry file +################################################## diff --git a/scripts/azure-function-template-basic/shared/helpers.py b/scripts/azure-function-template-basic/shared/helpers.py new file mode 100644 index 0000000..9c2fbb3 --- /dev/null +++ b/scripts/azure-function-template-basic/shared/helpers.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 + +################################################## +# Helper functions +################################################## + +import sys +import os +import logging +import requests +import json +from datetime import datetime +import time + + +def err_response(log_msg): + ''' + Formats an error string to be sent as a response to the main function. + + Example of how to call from main(): + e = helpers.err_response("Posting Request to IDG resulted in ERROR_STARTING_APPROVAL") + return func.HttpResponse(e, status_code=400) + + Older way without function in main(): + err_response = f"ERROR: An error occurred while attempting to verify table record => {str(e)}" + errRspJson = json.dumps({"response": err_response }) + logging.error(f"Sending Response: {errRspJson}") + return func.HttpResponse(errRspJson, status_code=400) + ''' + err_msg = f"ERROR: {log_msg}" + errRspJson = json.dumps({"response": err_msg }) + logging.error(f"Sending Response: {errRspJson}") + return errRspJson + + +def import_creds(): + ''' + Gets secrets from Azure Keyvault as an application + Secret values are stored in the Function App under Configuration + + Example of how to call from main(): + keyvault_creds = import_creds() + logging.info(f"client_id: { keyvault_creds['client_id'] }") + logging.info(f"client_secret: { keyvault_creds['client_secret'] }") + logging.info(f"tenant: { keyvault_creds['tenant'] }") + logging.info(f"vault_name: { keyvault_creds['vault_name'] }") + ''' + + try: + creds = {} + creds["client_id"] = os.environ["client_id"] + creds["client_secret"] = os.environ["client_secret"] + creds["tenant"] = os.environ["tenant"] + creds["vault_name"] = os.environ["vault_name"] + except KeyError: + logging.error("Unable to get env vars") + except Exception as e: + logging.error(f"Generic Catch: {str(e)}") + + return creds + +def get_oauth(tenant, client_id, client_secret): + ''' + Gets an Oauth token from Graph API as an application + See the scope where the application is scoped to just KeyVault + + Example of how to call from main(): + token = get_oauth( + tenant = keyvault_creds['tenant'], + client_id = keyvault_creds['client_id'], + client_secret = keyvault_creds['client_secret'] + ) + ''' + url = f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" + payload = "grant_type=client_credentials"\ + f"&client_id={client_id}"\ + f"&client_secret={client_secret}"\ + "&scope=https%3A%2F%2Fvault.azure.net%2F.default" + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + response = requests.request("POST", url, headers=headers, data=payload) + r = response.json() + return r + +def get_secret(token, vault_name, secret_name): + ''' + Using an Oauth token, gets the latest version of a secret + + Example of how to call from main(): + secret = get_secret( + token = token["access_token"], + vault_name = keyvault_creds['vault_name'], + secret_name = 'test' + ) + logging.info(f"Secret value is : {secret['value']}") + ''' + # Gets the latest version of a secret + url = f"https://{vault_name}.vault.azure.net/secrets/{secret_name}?api-version=7.1" + + # for a specific version of the secret, replace {version} + # version = "aadsfasdfasdfasdf" + # url = f"https://{vault_name}.vault.azure.net/secrets/test/{version}?api-version=7.1" + + payload={} + headers = { "Authorization": f"Bearer {token}" } + response = requests.request("GET", url, headers=headers, data=payload) + r = response.json() + return r + +def get_sn_username(): + ''' + Using a combination of get_oauth() and get_secret() functions, + This will get an oauth token and retrieve the latest version of a secret + + Example of how to call from main(): + sn_username = get_sn_username() + logging.info(f"Secret value is : {sn_username}") + ''' + keyvault_creds = import_creds() + token = get_oauth(tenant=keyvault_creds['tenant'], + client_id=keyvault_creds['client_id'], + client_secret=keyvault_creds['client_secret']) + secret = get_secret(token=token["access_token"], + vault_name=keyvault_creds['vault_name'], + secret_name='Service-Now-User') + return secret['value'] \ No newline at end of file diff --git a/scripts/azure-function-template/.funcignore b/scripts/azure-function-template/.funcignore new file mode 100644 index 0000000..0678ea2 --- /dev/null +++ b/scripts/azure-function-template/.funcignore @@ -0,0 +1,5 @@ +.git* +.vscode +local.settings.json +test +.venv \ No newline at end of file diff --git a/scripts/azure-function-template/.gitignore b/scripts/azure-function-template/.gitignore new file mode 100644 index 0000000..a10127b --- /dev/null +++ b/scripts/azure-function-template/.gitignore @@ -0,0 +1,130 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don’t work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json +.python_packages \ No newline at end of file diff --git a/scripts/azure-function-template/.vscode/settings.json b/scripts/azure-function-template/.vscode/settings.json new file mode 100644 index 0000000..cc67606 --- /dev/null +++ b/scripts/azure-function-template/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/scripts/azure-function-template/README.md b/scripts/azure-function-template/README.md new file mode 100644 index 0000000..877800b --- /dev/null +++ b/scripts/azure-function-template/README.md @@ -0,0 +1,46 @@ +### Setup + +Add yourself as collaborator to this repo. Then make pushes to production. + +### ReadJSON + +This is a HTTP triggered python app running in Azure Functions that: + +1. You pass a two level json payload and it will put them in variables + + ```json + { + "name": "gerry", + "car": "mustang", + "birth": { + "city": "fort worth", + "state": "Texas" + } + } + ``` + + - Response: + + ``` + Hello, gerry. + + You chose mustang. + + Your birth city is fort worth in the state of Texas + ``` + +2. You can call any of the three functions `ReadJSON`, `ReadJSON2`, or `ReadJSON3` + - Inside function apps, find the `App Key` blade in Azure and copy the `_master` key + - Paste it on the end of each URL: `https://{yourApp}.azurewebsites.net/api/ReadJSON?code={_masterKey}` + +3. Each of them can import a `helpers.py` function so you can put functions in their own files :) + +4. For logging, I tried to modify the `host.json` so that most things are dropped except output. + +5. Added a `shared` folder that all functions within the app can use. See `ReadJSON` for calling first function in `shared/shared.py` and `ReadJSON2` for calling second function in `shared/shared.py` + +6. Added a way for it to return a json response instead of text (see `ReadJSON`). + + ```json + [{"response": "Hello, gerry.\n\nYou chose mustang.\n\nYour birth city is fort worth in the state of Texas", "date_time": "2020-11-30-22-48-21"}] + ``` diff --git a/scripts/azure-function-template/ReadJSON/.vscode/settings.json b/scripts/azure-function-template/ReadJSON/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON/__init__.py b/scripts/azure-function-template/ReadJSON/__init__.py new file mode 100644 index 0000000..a8f8d61 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON/__init__.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 + +import sys +import logging +import azure.functions as func +from . import helpers +from shared import shared +import json +from datetime import datetime + +date = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + +def main(req: func.HttpRequest) -> func.HttpResponse: + logging.info("Python HTTP trigger function processed a request.") + logging.info("Called function ReadXML") + + # Show request info + logging.info("Request body: {}".format(req)) + + req_body = req.get_json() + logging.info("Request body as json: {}".format(req_body)) + + # Assuming the information is passed in payload: + + #name = req.params.get("name") + name = req_body.get("name") + logging.info("Extracted name: {}".format(name)) + + #car = req.params.get("car") + car = req_body.get("car") + logging.info("Extracted car: {}".format(car)) + + city = req_body.get('birth', {}).get('city') + logging.info("Extracted city: {}".format(city)) + + state = req_body.get('birth', {}).get('state') + logging.info("Extracted state: {}".format(state)) + + # Test calling other modules in the same dir + one = helpers.helpers_test("one") + logging.info("imported module test - one: {}".format(one)) + + two = helpers.helpers_test("") + logging.info("imported module test - two: {}".format(two)) + + three = shared.shared_helpers_test("import shared one") + logging.info("imported module shared - three: {}".format(three)) + + # Finally, return the response + + response = "Hello, {}.\n\nYou chose {}.\n\nYour birth city is {} in the state of {}".format( + name, car, city, state) + + rspJson = json.dumps([ { + "response": response, + "date_time": date + } ]) + + err_response = "Please pass a name on the query string or in the request body" + + errRspJson = json.dumps([ { + "response": err_response, + "date_time": date + } ]) + + if name and car and city and state: + return func.HttpResponse(rspJson, status_code=200) + else: + return func.HttpResponse(errRspJson, status_code=400) + +if __name__ == '__main__': + main(req) +else: + print("function not called correctly") \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON/function.json b/scripts/azure-function-template/ReadJSON/function.json new file mode 100644 index 0000000..b8dc650 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/scripts/azure-function-template/ReadJSON/helpers.py b/scripts/azure-function-template/ReadJSON/helpers.py new file mode 100644 index 0000000..bee8f48 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON/helpers.py @@ -0,0 +1,25 @@ + +def helpers_test(text): + + ''' + one = test("one") + print(one) + + two = test("") + print(two) + + Returns + one + text was blank + ''' + if text and text.strip(): + ftext = text + else: + ftext = "text was blank" + return ftext + +# one = helpers_test("one") +# print("imported module test - one: {}".format(one)) + +# two = helpers_test("") +# print("imported module test - two: {}".format(two)) \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON/sample.dat b/scripts/azure-function-template/ReadJSON/sample.dat new file mode 100644 index 0000000..26aac46 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON2/.vscode/settings.json b/scripts/azure-function-template/ReadJSON2/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON2/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON2/__init__.py b/scripts/azure-function-template/ReadJSON2/__init__.py new file mode 100644 index 0000000..51df34e --- /dev/null +++ b/scripts/azure-function-template/ReadJSON2/__init__.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 + +import sys +import logging +import azure.functions as func +from . import helpers +from shared import shared + +def main(req: func.HttpRequest) -> func.HttpResponse: + logging.info("Python HTTP trigger function processed a request.") + logging.info("Called function ReadXML2") + + # Show request info + logging.info("Request body: {}".format(req)) + + req_body = req.get_json() + logging.info("Request body as json: {}".format(req_body)) + + # Assuming the information is passed in payload: + + #name = req.params.get("name") + name = req_body.get("name") + logging.info("Extracted name: {}".format(name)) + + #car = req.params.get("car") + car = req_body.get("car") + logging.info("Extracted car: {}".format(car)) + + city = req_body.get('birth', {}).get('city') + logging.info("Extracted city: {}".format(city)) + + state = req_body.get('birth', {}).get('state') + logging.info("Extracted state: {}".format(state)) + + # Test calling other modules in the same dir + one = helpers.helpers_test("one") + logging.info("imported module test - one: {}".format(one)) + + two = helpers.helpers_test("") + logging.info("imported module test - two: {}".format(two)) + + four = shared.shared_helpers_test_2("import shared two") + logging.info("imported module shared - three: {}".format(four)) + + # Finally, return the response + response = "Hello, {}.\n\nYou chose {}.\n\nYour birth city is {} in the state of {}".format( + name, car, city, state) + return func.HttpResponse(response, status_code=200) + +if __name__ == '__main__': + main(req) +else: + print("function not called correctly") \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON2/function.json b/scripts/azure-function-template/ReadJSON2/function.json new file mode 100644 index 0000000..b8dc650 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON2/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/scripts/azure-function-template/ReadJSON2/helpers.py b/scripts/azure-function-template/ReadJSON2/helpers.py new file mode 100644 index 0000000..bee8f48 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON2/helpers.py @@ -0,0 +1,25 @@ + +def helpers_test(text): + + ''' + one = test("one") + print(one) + + two = test("") + print(two) + + Returns + one + text was blank + ''' + if text and text.strip(): + ftext = text + else: + ftext = "text was blank" + return ftext + +# one = helpers_test("one") +# print("imported module test - one: {}".format(one)) + +# two = helpers_test("") +# print("imported module test - two: {}".format(two)) \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON2/sample.dat b/scripts/azure-function-template/ReadJSON2/sample.dat new file mode 100644 index 0000000..26aac46 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON2/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON3/.vscode/settings.json b/scripts/azure-function-template/ReadJSON3/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON3/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON3/__init__.py b/scripts/azure-function-template/ReadJSON3/__init__.py new file mode 100644 index 0000000..8de80c4 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON3/__init__.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 + +import sys +import logging +import azure.functions as func +from . import helpers + +def main(req: func.HttpRequest) -> func.HttpResponse: + logging.info("Python HTTP trigger function processed a request.") + + logging.info("Called function ReadXML3") + # Show request info + logging.info("Request body: {}".format(req)) + + req_body = req.get_json() + logging.info("Request body as json: {}".format(req_body)) + + # Assuming the information is passed in payload: + + #name = req.params.get("name") + name = req_body.get("name") + logging.info("Extracted name: {}".format(name)) + + #car = req.params.get("car") + car = req_body.get("car") + logging.info("Extracted car: {}".format(car)) + + city = req_body.get('birth', {}).get('city') + logging.info("Extracted city: {}".format(city)) + + state = req_body.get('birth', {}).get('state') + logging.info("Extracted state: {}".format(state)) + + # Test calling other modules in the same dir + one = helpers.helpers_test("one") + logging.info("imported module test - one: {}".format(one)) + + two = helpers.helpers_test("") + logging.info("imported module test - two: {}".format(two)) + + # Finally, return the response + response = "Hello, {}.\n\nYou chose {}.\n\nYour birth city is {} in the state of {}".format( + name, car, city, state) + return func.HttpResponse(response, status_code=200) + +if __name__ == '__main__': + main(req) +else: + print("function not called correctly") \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON3/function.json b/scripts/azure-function-template/ReadJSON3/function.json new file mode 100644 index 0000000..b8dc650 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON3/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/scripts/azure-function-template/ReadJSON3/helpers.py b/scripts/azure-function-template/ReadJSON3/helpers.py new file mode 100644 index 0000000..bee8f48 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON3/helpers.py @@ -0,0 +1,25 @@ + +def helpers_test(text): + + ''' + one = test("one") + print(one) + + two = test("") + print(two) + + Returns + one + text was blank + ''' + if text and text.strip(): + ftext = text + else: + ftext = "text was blank" + return ftext + +# one = helpers_test("one") +# print("imported module test - one: {}".format(one)) + +# two = helpers_test("") +# print("imported module test - two: {}".format(two)) \ No newline at end of file diff --git a/scripts/azure-function-template/ReadJSON3/sample.dat b/scripts/azure-function-template/ReadJSON3/sample.dat new file mode 100644 index 0000000..26aac46 --- /dev/null +++ b/scripts/azure-function-template/ReadJSON3/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/scripts/azure-function-template/host.json b/scripts/azure-function-template/host.json new file mode 100644 index 0000000..e08b0e7 --- /dev/null +++ b/scripts/azure-function-template/host.json @@ -0,0 +1,18 @@ +{ + "version": "2.0", + "logging": { + "logLevel": { + "Function.ReadJSON.User": "Warning", + "Function.ReadJSON2.User": "Warning", + "Function.ReadJSON3.User": "Warning", + "default": "None" + }, + "applicationInsights": { + "httpAutoCollectionOptions": { + "enableHttpTriggerExtendedInfoCollection": true, + "enableW3CDistributedTracing": true, + "enableResponseHeaderInjection": true + } + } + } +} \ No newline at end of file diff --git a/scripts/azure-function-template/proxies.json b/scripts/azure-function-template/proxies.json new file mode 100644 index 0000000..b385252 --- /dev/null +++ b/scripts/azure-function-template/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/scripts/azure-function-template/requirements.txt b/scripts/azure-function-template/requirements.txt new file mode 100644 index 0000000..bdb8fc5 --- /dev/null +++ b/scripts/azure-function-template/requirements.txt @@ -0,0 +1,5 @@ +# DO NOT include azure-functions-worker in this file +# The Python Worker is managed by Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +azure-functions diff --git a/scripts/azure-function-template/shared/shared.py b/scripts/azure-function-template/shared/shared.py new file mode 100644 index 0000000..e2b9534 --- /dev/null +++ b/scripts/azure-function-template/shared/shared.py @@ -0,0 +1,49 @@ +def shared_helpers_test(text): + + ''' + one = test("one") + print(one) + + two = test("") + print(two) + + Returns + one + text was blank + ''' + if text and text.strip(): + ftext = text + else: + ftext = "text was blank" + return ftext + +# one = helpers_test("one") +# print("imported module test - one: {}".format(one)) + +# two = helpers_test("") +# print("imported module test - two: {}".format(two)) + +def shared_helpers_test_2(text): + + ''' + one = test("one") + print(one) + + two = test("") + print(two) + + Returns + one + text was blank + ''' + if text and text.strip(): + ftext = text + else: + ftext = "text was blank" + return ftext + +# one = helpers_test("one") +# print("imported module test - one: {}".format(one)) + +# two = helpers_test("") +# print("imported module test - two: {}".format(two)) \ No newline at end of file diff --git a/scripts/check_bitcoin/.gitignore b/scripts/check_bitcoin/.gitignore new file mode 100644 index 0000000..597fe46 --- /dev/null +++ b/scripts/check_bitcoin/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +logs +.env +venv \ No newline at end of file diff --git a/scripts/check_bitcoin/check_bitcoin.bat b/scripts/check_bitcoin/check_bitcoin.bat new file mode 100644 index 0000000..c224730 --- /dev/null +++ b/scripts/check_bitcoin/check_bitcoin.bat @@ -0,0 +1,4 @@ +pushd "%~dp0" +@ECHO OFF +PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" +popd diff --git a/scripts/check_bitcoin/check_bitcoin.ps1 b/scripts/check_bitcoin/check_bitcoin.ps1 new file mode 100644 index 0000000..2d182f5 --- /dev/null +++ b/scripts/check_bitcoin/check_bitcoin.ps1 @@ -0,0 +1,7 @@ + +Set-Location "c:\scripts\schedtasks\check_bitcoin" +.\venv\Scripts\activate +python main.py +# if the script had arguments +# $arg = "-f " + $currentFileName +# Start-Process -FilePath .\main.py -ArgumentList $arg -Wait \ No newline at end of file diff --git a/scripts/check_bitcoin/helpers.py b/scripts/check_bitcoin/helpers.py new file mode 100644 index 0000000..5d71d99 --- /dev/null +++ b/scripts/check_bitcoin/helpers.py @@ -0,0 +1,64 @@ + +import logging +import logging.handlers +from datetime import datetime +import sys +import os +import smtplib +from email.mime.text import MIMEText + +def send_email(user, password, to): + + text = 'Records indicate this fund is on decline for the last 3 days' + + html = """\ + + + + +

{}

+ + + """.format(text) + msg = MIMEText(html, 'html') + msg['Subject'] = 'Buy Bitcoin' + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +def load_logging(): + + # create path to log file, create logs dir if it doesn't exist + current_path = os.path.dirname(os.path.realpath(__file__)) + + date = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + logname = "check_bitcoin_" + date + ".log" + + log_filename = f"{current_path}/logs/{logname}" + + if not os.path.exists(f"{current_path}/logs"): + os.makedirs(f"{current_path}/logs") + + # import base logging module + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + + # add handler + handler = logging.handlers.RotatingFileHandler( + log_filename, maxBytes=(1048576*5), backupCount=7 + ) + + # set formatting + formatter = logging.Formatter("%(asctime)s => %(levelname)s : %(message)s", datefmt='%Y-%m-%d %I:%M:%S %p') + handler.setFormatter(formatter) + + # apply handler to default logger + logger.addHandler(handler) + logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + + return None \ No newline at end of file diff --git a/scripts/check_bitcoin/main.py b/scripts/check_bitcoin/main.py new file mode 100644 index 0000000..b15a16f --- /dev/null +++ b/scripts/check_bitcoin/main.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using AlphaVantage API +# Sign up to get an API key and import it +# This script currently just gets the 5 latest values for bitcoin but can do others as well +# will eventually replace my powershell script at https://automationadmin.com/2020/09/ps-send-email-bitcoin +################################################################ + +import requests +from dotenv import load_dotenv +import os +import json +import math +from time import sleep +import logging +import logging.handlers +from datetime import datetime +import helpers + +helpers.load_logging() +load_dotenv() +logging.info("Starting...") + +logging.info("Loading environment vars...") +try: + api_key = os.environ["API_KEY"] + email_user = os.environ["GMAIL_USER"] + email_password = os.environ["GMAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + logging.error("Unable to get environmental variables") +except Exception as e: + logging.error("Generic catch: Unable to get environmental variables") + logging.error("Generic catch: " + str(e)) +logging.info("Loading environment vars...completed") + +def main(): + + logging.info("Sending API Request...") + + url = f"https://www.alphavantage.co/query?function=DIGITAL_CURRENCY_DAILY&symbol=BTC&market=USD&apikey={api_key}" + payload = {} + headers = { + 'Content-Type': 'application/json', + } + r = requests.request("GET", url, headers=headers, data=payload) + req = r.json() + logging.info("Sending API Request...completed") + + ## sort the responses + keylist = list(req['Time Series (Digital Currency Daily)'].keys()) + keylist.sort(reverse=True) + + ## give me just the top 5 + first_five_list = keylist[0:5] + + # now that we see the top five, store in variables for comparisons + first_key = first_five_list[0] + first_value = req['Time Series (Digital Currency Daily)'][first_key]['4b. close (USD)'] + first_value = math.floor(float(first_value)) + + second_key = first_five_list[1] + second_value = req['Time Series (Digital Currency Daily)'][second_key]['4b. close (USD)'] + second_value = math.floor(float(second_value)) + + + third_key = first_five_list[2] + third_value = req['Time Series (Digital Currency Daily)'][third_key]['4b. close (USD)'] + third_value = math.floor(float(third_value)) + + fourth_key = first_five_list[3] + fourth_value = req['Time Series (Digital Currency Daily)'][fourth_key]['4b. close (USD)'] + fourth_value = math.floor(float(fourth_value)) + + fifth_key = first_five_list[4] + fifth_value = req['Time Series (Digital Currency Daily)'][fifth_key]['4b. close (USD)'] + fifth_value = math.floor(float(fifth_value)) + + day1_diff = ( (second_value - first_value) / first_value ) * 100 + day2_diff = ( (third_value - second_value) / second_value ) * 100 + + logging.info("Values for the last five days:") + logging.info(f"{first_key}: {first_value}") + logging.info(f"{second_key}: {second_value}") + logging.info(f"{third_key}: {third_value}") + logging.info(f"{fourth_key}: {fourth_value}") + logging.info(f"{fifth_key}: {fifth_value}") + + logging.info(f"Day 1 to Day 2 Percent increase/decrease: {day1_diff}") + logging.info(f"Day 1 to Day 2 Percent increase/decrease: {day2_diff}") + + # I will actually just grab the first 3 days + if first_value < second_value < third_value : + logging.info("Trend is downwards for three days straight") + + # Change the subject depending on if over 10% decrease + subject = f"Buy BTC - {first_value}" + if day1_diff > 9.9: + subject = f"Strong Buy BTC (10% decrease from yesterday) - {first_value}" + else: + pass + if day2_diff > 9.9: + subject = f"Strong Buy BTC (10% decrease from two days ago) - {first_value}" + else: + pass + if day1_diff > 9.9 and day2_diff > 9.9: + subject = f"Strong Buy BTC (10% decrease each day for 3 days) - {first_value}" + else: + pass + + logging.info(f"Subject: {subject}") + logging.info("Sending email...") + send = helpers.send_email(email_user, email_password, email_to) + logging.info(f"Sending email status: {send}") + else: + logging.info("Trend is not downwards for three days straight") + + logging.info("Completed") + +if __name__ == '__main__': + main() +else: + pass \ No newline at end of file diff --git a/scripts/check_bitcoin/readme.md b/scripts/check_bitcoin/readme.md new file mode 100644 index 0000000..78a91b9 --- /dev/null +++ b/scripts/check_bitcoin/readme.md @@ -0,0 +1,64 @@ +### Bitcoin + +This script will use Alpha Vantage API to get the current price of bitcion each day. + +#### To create: + +1. Type the following: + + ```powershell + + cd C:\scripts\schedtasks + mkdir check_bitcoin + + # create an active venv + python -m venv ./venv + .\venv\Scripts\activate + + # upgrade pip + C:\scripts\schedtasks\check_bitcoin\venv\scripts\python.exe -m pip install --upgrade pip + + # install stuff + pip install requests + pip install python-dotenv + + # deactivate venv + deactivate + + ``` + +2. Create a `.gitignore` with `.env`, `venv`, `__pycache`, and `logs` so that it is not tracked with git.t. + +3. Develop script and test with `python main.py` + +4. Once done, create `requirements.txt` by typing: + + ```powershell + cd C:\scripts\schedtasks\check_bitcoin + .\venv\Scripts\activate + C:\scripts\schedtasks\check_bitcoin\venv\scripts\python.exe -m pip freeze > requirements.txt + # deactivate venv + deactivate + ``` + +5. Next create `.bat` and `.ps1` scripts and name them exactly the same + + - Batch file: + + ```bash + pushd "%~dp0" + @ECHO OFF + PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" + popd + ``` + + - Powershell file: + + ```powershell + Set-Location "C:\scripts\schedtasks\check_bitcoin" + .\venv\Scripts\activate + python main.py + ``` + +6. Open up powershell as administrator and type `control schedtasks` to open Windows Task Scheduler. + - Create a task in the Task Scheduler that runs as the administrator since we need `logon as batch` rights and have it point to the batch script at whatever interval you want. diff --git a/scripts/check_bitcoin/requirements.txt b/scripts/check_bitcoin/requirements.txt new file mode 100644 index 0000000..b01330d Binary files /dev/null and b/scripts/check_bitcoin/requirements.txt differ diff --git a/scripts/check_mutual_funds/.gitignore b/scripts/check_mutual_funds/.gitignore new file mode 100644 index 0000000..597fe46 --- /dev/null +++ b/scripts/check_mutual_funds/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +logs +.env +venv \ No newline at end of file diff --git a/scripts/check_mutual_funds/check_mutual_funds.bat b/scripts/check_mutual_funds/check_mutual_funds.bat new file mode 100644 index 0000000..c224730 --- /dev/null +++ b/scripts/check_mutual_funds/check_mutual_funds.bat @@ -0,0 +1,4 @@ +pushd "%~dp0" +@ECHO OFF +PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" +popd diff --git a/scripts/check_mutual_funds/check_mutual_funds.ps1 b/scripts/check_mutual_funds/check_mutual_funds.ps1 new file mode 100644 index 0000000..54a5c26 --- /dev/null +++ b/scripts/check_mutual_funds/check_mutual_funds.ps1 @@ -0,0 +1,7 @@ + +Set-Location "c:\scripts\schedtasks\check_mutual_funds" +.\venv\Scripts\activate +python main.py +# if the script had arguments +# $arg = "-f " + $currentFileName +# Start-Process -FilePath .\main.py -ArgumentList $arg -Wait \ No newline at end of file diff --git a/scripts/check_mutual_funds/helpers.py b/scripts/check_mutual_funds/helpers.py new file mode 100644 index 0000000..d8e7e56 --- /dev/null +++ b/scripts/check_mutual_funds/helpers.py @@ -0,0 +1,64 @@ + +import logging +import logging.handlers +from datetime import datetime +import sys +import os +import smtplib +from email.mime.text import MIMEText + +def send_email(user, password, to, subject): + + text = 'Records indicate this fund is on decline for the last 3 days' + + html = """\ + + + + +

{}

+ + + """.format(text) + msg = MIMEText(html, 'html') + msg['Subject'] = subject + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + return "completed" + +def load_logging(): + + # create path to log file, create logs dir if it doesn't exist + current_path = os.path.dirname(os.path.realpath(__file__)) + + date = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + logname = "check_mutual_funds_" + date + ".log" + + log_filename = f"{current_path}/logs/{logname}" + + if not os.path.exists(f"{current_path}/logs"): + os.makedirs(f"{current_path}/logs") + + # import base logging module + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + + # add handler + handler = logging.handlers.RotatingFileHandler( + log_filename, maxBytes=(1048576*5), backupCount=7 + ) + + # set formatting + formatter = logging.Formatter("%(asctime)s => %(levelname)s : %(message)s", datefmt='%Y-%m-%d %I:%M:%S %p') + handler.setFormatter(formatter) + + # apply handler to default logger + logger.addHandler(handler) + logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + + return None \ No newline at end of file diff --git a/scripts/check_mutual_funds/main.py b/scripts/check_mutual_funds/main.py new file mode 100644 index 0000000..a22876c --- /dev/null +++ b/scripts/check_mutual_funds/main.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using AlphaVantage API +# Sign up to get an API key and import it +# This script currently just gets the 5 latest values for bitcoin but can do others as well +# will eventually replace my powershell script at https://automationadmin.com/2020/09/ps-send-email-bitcoin +################################################################ + +import requests +from dotenv import load_dotenv +import os +import json +import math +from time import sleep +import logging +import logging.handlers +from datetime import datetime +import helpers + +helpers.load_logging() +load_dotenv() +logging.info("Starting...") + +logging.info("Loading environment vars...") +try: + api_key = os.environ["API_KEY"] + email_user = os.environ["GMAIL_USER"] + email_password = os.environ["GMAIL_PASSWORD"] + email_to = os.environ["MAIL_RECEIVER"] +except KeyError: + logging.error("Unable to get environmental variables") +except Exception as e: + logging.error("Generic catch: Unable to get environmental variables") + logging.error("Generic catch: " + str(e)) +logging.info("Loading environment vars...completed") + +def main(): + + logging.info("Sending API Request for each fund...") + + funds = ["ICLN", "BB", "VTSAX", "VWUSX"] + + for fund in funds: + + logging.info(f"Starting loop for: {fund}") + + url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={fund}&apikey={api_key}" + payload = {} + headers = { + 'Content-Type': 'application/json', + } + r = requests.request("GET", url, headers=headers, data=payload) + req = r.json() + logging.info("Sending API Request...completed") + + ## sort the responses + keylist = list(req['Time Series (Daily)'].keys()) + keylist.sort(reverse=True) + + ## give me just the top 5 + first_five_list = keylist[0:5] + + # now that we see the top five, store in variables for comparisons + first_key = first_five_list[0] + first_value = req['Time Series (Daily)'][first_key]['5. adjusted close'] + first_value = math.floor(float(first_value)) + + second_key = first_five_list[1] + second_value = req['Time Series (Daily)'][second_key]['5. adjusted close'] + second_value = math.floor(float(second_value)) + + third_key = first_five_list[2] + third_value = req['Time Series (Daily)'][third_key]['5. adjusted close'] + third_value = math.floor(float(third_value)) + + fourth_key = first_five_list[3] + fourth_value = req['Time Series (Daily)'][fourth_key]['5. adjusted close'] + fourth_value = math.floor(float(fourth_value)) + + fifth_key = first_five_list[4] + fifth_value = req['Time Series (Daily)'][fifth_key]['5. adjusted close'] + fifth_value = math.floor(float(fifth_value)) + + day1_diff = ( (second_value - first_value) / first_value ) * 100 + day2_diff = ( (third_value - second_value) / second_value ) * 100 + + logging.info("Values for the last five days:") + logging.info(f"{first_key}: {first_value}") + logging.info(f"{second_key}: {second_value}") + logging.info(f"{third_key}: {third_value}") + logging.info(f"{fourth_key}: {fourth_value}") + logging.info(f"{fifth_key}: {fifth_value}") + logging.info(f"Day 1 to Day 2 Percent increase/decrease: {day1_diff}") + logging.info(f"Day 1 to Day 2 Percent increase/decrease: {day2_diff}") + + # I will actually just grab the first 3 days + if first_value < second_value < third_value : + logging.info("Trend is downwards for three days straight") + + # Change the subject depending on if over 10% decrease + subject = f"Buy {fund} - {first_value}" + if day1_diff > 9.9: + subject = f"Strong Buy {fund} (10% decrease from yesterday) - {first_value}" + else: + pass + if day2_diff > 9.9: + subject = f"Strong Buy {fund} (10% decrease from two days ago) - {first_value}" + else: + pass + if day1_diff > 9.9 and day2_diff > 9.9: + subject = f"Strong Buy {fund} (10% decrease each day for 3 days) - {first_value}" + else: + pass + + logging.info(f"Subject: {subject}") + logging.info("Sending email...") + send = helpers.send_email(email_user, email_password, email_to, subject) + logging.info(f"Sending email status: {send}") + else: + logging.info("Trend is not downwards for three days straight") + + logging.info(f"Completed loop for: {fund}") + +if __name__ == '__main__': + main() +else: + pass \ No newline at end of file diff --git a/scripts/check_mutual_funds/readme.md b/scripts/check_mutual_funds/readme.md new file mode 100644 index 0000000..8c15d61 --- /dev/null +++ b/scripts/check_mutual_funds/readme.md @@ -0,0 +1,64 @@ +### Check Mutual Funds + +This script will use Alpha Vantage API to get the current price of Mutual funds each day. + +#### To create: + +1. Type the following: + + ```powershell + + cd C:\scripts\schedtasks + mkdir check_mutual_funds + + # create an active venv + python -m venv ./venv + .\venv\Scripts\activate + + # upgrade pip + C:\scripts\schedtasks\check_mutual_funds\venv\scripts\python.exe -m pip install --upgrade pip + + # install stuff + pip install requests + pip install python-dotenv + + # deactivate venv + deactivate + + ``` + +2. Create a `.gitignore` with `.env`, `venv`, `__pycache`, and `logs` so that it is not tracked with git. + +3. Develop script and test with `python main.py` + +4. Once done, create `requirements.txt` by typing: + + ```powershell + cd C:\scripts\schedtasks\check_mutual_funds + .\venv\Scripts\activate + C:\scripts\schedtasks\check_mutual_funds\venv\scripts\python.exe -m pip freeze > requirements.txt + # deactivate venv + deactivate + ``` + +5. Next create `.bat` and `.ps1` scripts and name them exactly the same + + - Batch file: + + ```bash + pushd "%~dp0" + @ECHO OFF + PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" + popd + ``` + + - Powershell file: + + ```powershell + Set-Location "C:\scripts\schedtasks\check_mutual_funds" + .\venv\Scripts\activate + python main.py + ``` + +6. Open up powershell as administrator and type `control schedtasks` to open Windows Task Scheduler. + - Create a task in the Task Scheduler that runs as the administrator since we need `logon as batch` rights and have it point to the batch script at whatever interval you want. diff --git a/scripts/check_mutual_funds/requirements.txt b/scripts/check_mutual_funds/requirements.txt new file mode 100644 index 0000000..b01330d Binary files /dev/null and b/scripts/check_mutual_funds/requirements.txt differ diff --git a/scripts/example-run-python-script-on-approval/.github/scripts/api-main-branch.py b/scripts/example-run-python-script-on-approval/.github/scripts/api-main-branch.py new file mode 100644 index 0000000..0a925f3 --- /dev/null +++ b/scripts/example-run-python-script-on-approval/.github/scripts/api-main-branch.py @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +################################################## +# This function will call API Mgmt to run a script on a remote server +################################################## + +import sys +import os +import json +import requests + + +def main(): + + try: + api_key = os.environ["api_key"] + except KeyError: + print("unable to get env vars") + except Exception as e: + print("Generic Catch: unable to get env vars") + print(str(e)) + + url = "https://my-api.azure-api.net/my-prod-function-app/my-prod-function" + + payload = json.dumps( + { + "hostname": "myserver.domain.com", + "script_path": "/home/me/scripts/sync-repo.sh", + } + ) + + headers = { + "Content-Type": "application/json", + "Ocp-Apim-Subscription-Key": api_key, + "Ocp-Apim-Trace": "true", + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +if __name__ == "__main__": + main() +else: + print("Function not called correctly, please try again.") \ No newline at end of file diff --git a/scripts/example-run-python-script-on-approval/.github/scripts/api-testing-branch.py b/scripts/example-run-python-script-on-approval/.github/scripts/api-testing-branch.py new file mode 100644 index 0000000..dffb5bc --- /dev/null +++ b/scripts/example-run-python-script-on-approval/.github/scripts/api-testing-branch.py @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +################################################## +# This function will call API Mgmt to run a script on a remote server +################################################## + +import sys +import os +import json +import requests + + +def main(): + + try: + api_key = os.environ["api_key"] + except KeyError: + print("unable to get env vars") + except Exception as e: + print("Generic Catch: unable to get env vars") + print(str(e)) + + url = "https://my-api.azure-api.net/my-testing-function-app/my-testing-function" + + payload = json.dumps( + { + "hostname": "myserver.domain.com", + "script_path": "/home/me/scripts/sync-repo.sh", + } + ) + + headers = { + "Content-Type": "application/json", + "Ocp-Apim-Subscription-Key": api_key, + "Ocp-Apim-Trace": "true", + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +if __name__ == "__main__": + main() +else: + print("Function not called correctly, please try again.") \ No newline at end of file diff --git a/scripts/example-run-python-script-on-approval/.github/workflows/main.yml b/scripts/example-run-python-script-on-approval/.github/workflows/main.yml new file mode 100644 index 0000000..6ea0d00 --- /dev/null +++ b/scripts/example-run-python-script-on-approval/.github/workflows/main.yml @@ -0,0 +1,45 @@ +name: post-pull-request-main-branch + +on: + pull_request: + branches: + - main + + types: [ closed ] + +jobs: + branch_main_pull_request_accepted: + # this job will only run if the PR has been merged + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest requests + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Run a python script + run: | + python ./.github/scripts/api-main-branch.py + env: + api_key: ${{ secrets.API_MGMT_KEY }} + + branch_main_pull_request_rejected: + # this job will only run if the PR has been closed without being merged + if: github.event.pull_request.merged == false + runs-on: ubuntu-latest + steps: + - run: | + echo PR #${{ github.event.number }} has been closed without being merged - main \ No newline at end of file diff --git a/scripts/example-run-python-script-on-approval/.github/workflows/testing.yml b/scripts/example-run-python-script-on-approval/.github/workflows/testing.yml new file mode 100644 index 0000000..de46d25 --- /dev/null +++ b/scripts/example-run-python-script-on-approval/.github/workflows/testing.yml @@ -0,0 +1,47 @@ +name: post-pull-request-testing-branch + +on: + pull_request: + branches: + - testing + + types: [ closed ] + +jobs: + branch_testing_pull_request_accepted: + # this job will only run if the PR has been merged + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest requests + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Run a python script + run: | + python ./.github/scripts/api-testing-branch.py + env: + api_key: ${{ secrets.API_MGMT_KEY }} + + branch_testing_pull_request_rejected: + # this job will only run if the PR has been closed without being merged + if: github.event.pull_request.merged == false + runs-on: ubuntu-latest + steps: + - run: | + echo PR #${{ github.event.number }} has been closed without being merged - branch testing + + \ No newline at end of file diff --git a/scripts/plex-backup.py b/scripts/plex-backup.py new file mode 100644 index 0000000..d9e99d5 --- /dev/null +++ b/scripts/plex-backup.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 + +################################################################ +# Name: Backup Plex +# Copyright: Gerry Williams (https://automationadmin.com) +# License: MIT License (https://opensource.org/licenses/mit) +# Script Modified from: n/a +# Last Update: 2020-05-24 +# Description: Backups my Plex Media Server database to /mnt/vids/linux/plex/backup using rsync +################################################################ + +import logging +import logging.handlers +import sys +from datetime import datetime +import re +import shutil +import subprocess + +# require Python interpreter > v.3.5 +assert sys.version_info >= (3, 5) + + +def main(): + + # set user functions + date = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + log = "/home/gerry/scripts/logs/plexbackup_" + date + ".log" + + # build rsync command + options = "-zavh --exclude='Cache' --exclude='Crash Reports' --exclude='Logs' --exclude='Plug-in Support'" + source = '/var/lib/plexmediaserver/Library/Application\ Support/Plex\ Media\ Server/' + destination = '/mnt/vids/linux/plex/backup' + seq = "rsync" + ' ' + options + ' ' + source + ' ' + destination + + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + handler = logging.handlers.RotatingFileHandler( + log, maxBytes=(1048576*5), backupCount=7 + ) + formatter = logging.Formatter( + "%(asctime)s => %(levelname)s : %(message)s", datefmt='%Y-%m-%d %I:%M:%S %p') + handler.setFormatter(formatter) + logger.addHandler(handler) + logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + + if sys.platform == 'win32': + logging.error("This script is incompatible with the Windows native" + "environment.\nIt should be run under a Unix-like" + "environment such as Linux or Cygwin.\n.") + exit(1) + + # exit with error if rsync not found + if not shutil.which('rsync'): + logging.error("rsync executable not found in PATH\n") + exit(1) + + logging.info('==========================') + logging.info('Starting function...') + logging.info('Logfile fullname: %s', log) + + logging.info('Stopping Plex Media Server') + logging.info('--------------------------') + stop = 'systemctl stop plexmediaserver.service' + logging.info('Running: %s', stop) + stop_process = subprocess.run(stop, shell=True, stdout=subprocess.PIPE) + logging.info(stop_process) + + logging.info('Starting Backup') + logging.info('--------------------------') + logging.info('Running: %s', seq) + #seq_process = subprocess.run(seq, shell=True, stdout=subprocess.PIPE) + # logging.info(seq_process) + + logging.info('Starting Plex Media Server') + logging.info('--------------------------') + start = 'systemctl start plexmediaserver.service' + logging.info('Running: %s', start) + start_process = subprocess.run(start, shell=True, stdout=subprocess.PIPE) + logging.info(start_process) + + logging.info('Backup Complete') + logging.info('==========================') + + +if __name__ == "__main__": + main() diff --git a/scripts/send_credit_balance/.gitignore b/scripts/send_credit_balance/.gitignore new file mode 100644 index 0000000..597fe46 --- /dev/null +++ b/scripts/send_credit_balance/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +logs +.env +venv \ No newline at end of file diff --git a/scripts/send_credit_balance/__init__.py b/scripts/send_credit_balance/__init__.py new file mode 100644 index 0000000..4778ddd --- /dev/null +++ b/scripts/send_credit_balance/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +################################################################ +# Placed to help find the gmail module +################################################################ \ No newline at end of file diff --git a/scripts/send_credit_balance/helpers.py b/scripts/send_credit_balance/helpers.py new file mode 100644 index 0000000..7b5e384 --- /dev/null +++ b/scripts/send_credit_balance/helpers.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 + +################################################################ +# Helpers to support Gmail API script +################################################################ + +import datetime +from email.mime.text import MIMEText +import base64 +import json +import logging +import logging.handlers +import os +import requests +import smtplib +import sys +import time +import urllib.parse +import urllib.request + +def send_email(user, password, to, subject, body): + + msg = MIMEText(body, 'html') + msg['Subject'] = subject + + mailserver = smtplib.SMTP('smtp.gmail.com',587) + mailserver.ehlo() + mailserver.starttls() + mailserver.login(user, password) + mailserver.sendmail(user, to, msg.as_string()) + mailserver.quit() + + return "completed" + +def load_logging(): + + # create path to log file, create logs dir if it doesn't exist + current_path = os.path.dirname(os.path.realpath(__file__)) + + date = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + logname = "check_gmail_" + date + ".log" + + log_filename = f"{current_path}/logs/{logname}" + + if not os.path.exists(f"{current_path}/logs"): + os.makedirs(f"{current_path}/logs") + + # import base logging module + logger = logging.getLogger("") + logger.setLevel(logging.DEBUG) + + # add handler + handler = logging.handlers.RotatingFileHandler( + log_filename, maxBytes=(1048576*5), backupCount=7 + ) + + # set formatting + formatter = logging.Formatter("%(asctime)s => %(levelname)s : %(message)s", datefmt='%Y-%m-%d %I:%M:%S %p') + handler.setFormatter(formatter) + + # apply handler to default logger + logger.addHandler(handler) + logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + + return None + + +def get_access_token(client_id, client_secret, refresh_token): + """ + Uses a refresh token to get an access token + if refresh token expires, follow https://blog.macuyiko.com/post/2016/how-to-send-html-mails-with-oauth2-and-gmail-in-python.html to set up again + """ + + request_url = "https://accounts.google.com/o/oauth2/token" + payload = {} + payload["client_id"] = client_id + payload["client_secret"] = client_secret + payload["refresh_token"] = refresh_token + payload["grant_type"] = "refresh_token" + r = ( + urllib.request.urlopen( + request_url, urllib.parse.urlencode(payload).encode("UTF-8") + ) + .read() + .decode("UTF-8") + ) + return json.loads(r) + + +def get_messages(access_token): + """ + Will get messages for a user + """ + url = f"https://www.googleapis.com/gmail/v1/users/me/messages?access_token={access_token}" + headers = {"Content-Type": "application/json"} + payload = {} + r = requests.request("GET", url, headers=headers, data=payload) + if r.status_code != 200: + err_resp = ( + f"Status Code Details:\n" + f"Status code returned: {r.status_code}\n" + f"Headers sent: {r.headers}\n" + f"Body sent: {payload}\n" + f"API response: {r.json()}" + ) + logging.error(err_resp) + return r.json() + +def get_single_email(id, access_token): + """ + Will get messages for a user + """ + url = f"https://www.googleapis.com/gmail/v1/users/me/messages/{id}?access_token={access_token}" + headers = {"Content-Type": "application/json"} + payload = {} + r = requests.request("GET", url, headers=headers, data=payload) + if r.status_code != 200: + err_resp = ( + f"Status Code Details:\n" + f"Status code returned: {r.status_code}\n" + f"Headers sent: {r.headers}\n" + f"Body sent: {payload}\n" + f"API response: {r.json()}" + ) + logging.error(err_resp) + return r.json() + +def trash_single_email(id, access_token): + """ + Will get messages for a user + """ + url = f"https://www.googleapis.com/gmail/v1/users/me/messages/{id}/trash?access_token={access_token}" + headers = {"Content-Type": "application/json"} + payload = {} + r = requests.request("POST", url, headers=headers, data=payload) + if r.status_code != 200: + err_resp = ( + f"Status Code Details:\n" + f"Status code returned: {r.status_code}\n" + f"Headers sent: {r.headers}\n" + f"Body sent: {payload}\n" + f"API response: {r.json()}" + ) + logging.error(err_resp) + return r.json() + +def read_message(message): + + return_data = {} + payload = message['payload'] + parts = payload['parts'] + + for part in parts: + msg_parts = part['parts'] + + for msg_part in msg_parts: + part_id = msg_part['partId'] + + if part_id == "0.0": + data = msg_part['body']['data'] + email_bytes = bytes(str(data),encoding='utf-8') + decoded = base64.urlsafe_b64decode(email_bytes).decode('utf-8') + decoded_email = str(decoded) + + # Hypothetically, I have two credit cards, one for purchasing stuff and another for bills + # The one for purchasing stuff ends in 9999 so we test for that + # if it is the other one, it will match 'bills_cc' + if decoded_email.find("9999") != -1: + return_data['card'] = 'buying_cc' + else: + return_data['card'] = 'bills_cc' + + m_index = decoded_email.find("$") + amount = decoded_email[(m_index + 1): (m_index + 8)] + + # will match $9900.65 or $10321.87 + if amount.find("\n") != -1: + return_data['amount'] = amount.strip() + + else: + return_data['amount'] = amount + + else: + pass + + return return_data + +def get_days_until_payday(): + ''' + Get the number of days until the first of the month using only the datetime module + ''' + + today = datetime.datetime.today() + #today = datetime.date(2021,2,28) + year = today.year + month = today.month + day = today.day + + if day == 1: + days_until_payday = 0 + else: + date1 = datetime.date(year, month, day) + + next_month = int(month) + 1 + + # if Dec, bump to next year + if next_month > 12: + next_month = 1 + year = int(year) + 1 + else: + pass + + date2 = datetime.date(year, next_month, 1) + + days_til_first = (date1 - date2).days + days_til_first = abs(days_til_first) + days_until_payday = days_til_first + + return days_until_payday \ No newline at end of file diff --git a/scripts/send_credit_balance/main.py b/scripts/send_credit_balance/main.py new file mode 100644 index 0000000..44438c0 --- /dev/null +++ b/scripts/send_credit_balance/main.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 + +################################################################ +# Example of using Gmail API +################################################################ + +from datetime import datetime +from dotenv import load_dotenv +import helpers +import logging +import os + +def load_env(): + + load_dotenv() + + return_data = {} + try: + return_data['client_id'] = os.environ["CLIENT_ID"] + return_data['client_secret'] = os.environ["CLIENT_SECRET"] + return_data['refresh_token'] = os.environ["REFRESH_TOKEN"] + return_data['email_user'] = os.environ["EMAIL_USER"] + return_data['email_password'] = os.environ["EMAIL_PASSWORD"] + return_data['email_to'] = os.environ["EMAIL_TO"] + except KeyError: + logging.error("Unable to get environmental variables") + except Exception as e: + logging.error("Generic catch: Unable to get environmental variables") + logging.error("Generic catch: " + str(e)) + return return_data + +def main(): + + helpers.load_logging() + + logging.info("Starting...") + + + total_budget = 2800 + + logging.info("Loading environment vars...") + env = load_env() + client_id = env['client_id'] + client_secret = env['client_secret'] + refresh_token = env['refresh_token'] + email_user = env['email_user'] + email_password = env['email_password'] + email_to = env['email_to'] + logging.info("Loading environment vars...completed") + + + # get messages + gmail_access_token = helpers.get_access_token(client_id, client_secret, refresh_token) + my_messages = helpers.get_messages(gmail_access_token['access_token']) + #logging.info(f"Messages: {my_messages}") + + if my_messages['resultSizeEstimate'] == 0: + logging.info("no emails") + else: + + email_count = my_messages['resultSizeEstimate'] + + if int(email_count) > 0: + logging.info(f"Found {email_count} emails to go through") + else: + logging.info("No messages to go through") + exit(0) + + results = 0 + counter = 0 + + emails = my_messages['messages'] + for email in emails: + + counter += 1 + + gmail_id = email['id'] + current_email = helpers.get_single_email(gmail_id, gmail_access_token['access_token']) + + snippet = current_email["snippet"] + #logging.info(f"Snippet: {snippet}") + logging.info("Seeing if this message matches the automated email we are looking for") + if snippet.find("Available Credit Alert") != -1: + logging.info("Email subject matched the phrase: Available Credit Alert") + + read = helpers.read_message(current_email) + logging.info(f"Card: {read['card']}") + logging.info(f"Amount: ${read['amount']}") + + if read['card'] == "buying_cc": + amount_spent = 5500 - float(read['amount']) + else: + amount_spent = 4500 - float(read['amount']) + amount_spent = round(amount_spent) + logging.info(f"Amount spent: ${amount_spent}") + results += amount_spent + + trash = helpers.trash_single_email(gmail_id, gmail_access_token['access_token']) + if trash == None: + logging.info("ERROR: Unable to delete email") + else: + logging.info("Email moved to trash so it won't confuse the script for the next day") + + else: + logging.info("Email subject did not have the phrase: Available Credit Alert") + + logging.info(f"Total spent on both credit cards combined: ${results}") + + days_until_payday = helpers.get_days_until_payday() + today = datetime.today() + day_of_month = today.day + + upcoming_bills = [] + + # This section will be dependent on what days your bills come out + # and what bills you have, but for an example, lets say you have the following bills: + + # 1 - Internet - $105 + # 6 - Groceries - $200 + # 13 - Groceries - $200 + # 16 - Water - $90 + # 16 - Cell_Phone - $45 + # 20 - Groceries - $200 + # 24 - Cable - $11 + # 27 - Electric - $200 + # 27 - Groceries - $200 + # 28 - Auto_Ins - $176 + + # What the script will do is tell you which ones are due before the first of the next month + # in conjunction with your current credit card usage + + + if day_of_month >=1 and day_of_month <= 5: + upcoming_bill_amount = 1455 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('06 - Groceries - $200
') + upcoming_bills.append('13 - Groceries - $200
') + upcoming_bills.append('16 - Water - $90
') + upcoming_bills.append('16 - Cell_Phone - $45
') + upcoming_bills.append('20 - Groceries - $200
') + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $1455
') + elif day_of_month >=6 and day_of_month <= 12: + upcoming_bill_amount = 1255 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('13 - Groceries - $200
') + upcoming_bills.append('16 - Water - $90
') + upcoming_bills.append('16 - Cell_Phone - $45
') + upcoming_bills.append('20 - Groceries - $200
') + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $1255
') + elif day_of_month ==13: + upcoming_bill_amount = 1055 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('16 - Water - $90
') + upcoming_bills.append('16 - Cell_Phone - $45
') + upcoming_bills.append('20 - Groceries - $200
') + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $1055
') + elif day_of_month >=14 and day_of_month <= 15: + upcoming_bill_amount = 1042 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('16 - Water - $90
') + upcoming_bills.append('16 - Cell_Phone - $45
') + upcoming_bills.append('20 - Groceries - $200
') + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $1042
') + elif day_of_month >=16 and day_of_month <= 19: + upcoming_bill_amount = 907 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('20 - Groceries - $200
') + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $907
') + elif day_of_month >=20 and day_of_month <= 23: + upcoming_bill_amount = 707 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('24 - Cable - $11
') + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $707
') + elif day_of_month >=24 and day_of_month <= 26: + upcoming_bill_amount = 696 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('27 - Electric - $200
') + upcoming_bills.append('27 - Groceries - $200
') + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $696
') + elif day_of_month ==27: + upcoming_bill_amount = 281 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('28 - Auto_Ins. - $176
') + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $281
') + elif day_of_month >=28 and day_of_month <= 31: + upcoming_bill_amount = 105 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('01 - Internet - $105
') + upcoming_bills.append('Total: $105
') + else: + upcoming_bill_amount = 0 + results_with_bills = results + upcoming_bill_amount + upcoming_bills.append('None!
') + + logging.info(f"Upcoming bill amount: ${upcoming_bill_amount}") + logging.info(f"Results with bills: ${results_with_bills}") + + budget = total_budget - results_with_bills + logging.info(f"Budget => ${total_budget} - (current credit card balance + upcoming bill amount): ${budget}") + + + + if counter >= 1: + + # Send an email + upcoming_bills_print= '\n'.join(upcoming_bills) + + subject = "Daily Budget Automated Task" + body = """ + + + + +

Days until payoff:

{}
+

Budget before payday: +

+ ${} Monthly budget
+ - ${} Current combined balance
+ - ${} Upcoming bills
+ =========================================
+ ${} Spending balance available +

Upcoming Bills:

+ {} + + + """.format(days_until_payday, total_budget, results, upcoming_bill_amount, budget, upcoming_bills_print) + email = helpers.send_email( + email_user, + email_password, + email_to, + subject, + body + ) + if email == "completed": + logging.info(f"Email sent from {email_user} to {email_to}") + else: + logging.info(f"ERROR: Email failed to send from {email_user} to {email_to}") + + # Send a SMS message + subject = "My_Budget" + body = f"Budget before payday: ${budget}" + email_to = "9999999999@txt.att.net" + + email = helpers.send_email( + email_user, + email_password, + email_to, + subject, + body + ) + if email == "completed": + logging.info(f"Email sent from {email_user} to {email_to}") + else: + logging.info(f"ERROR: Email failed to send from {email_user} to {email_to}") + + else: + + # Send an email + subject = "Daily Budget Automated Task" + body = "Email didn't arrive" + + email = helpers.send_email( + email_user, + email_password, + email_to, + subject, + body + ) + if email == "completed": + logging.info(f"Email sent from {email_user} to {email_to}") + else: + logging.info(f"ERROR: Email failed to send from {email_user} to {email_to}") + + # Send a SMS message + subject = "My_Budget" + body = "Email didn't arrive" + email_to = "9999999999@txt.att.net" + + email = helpers.send_email( + email_user, + email_password, + email_to, + subject, + body + ) + if email == "completed": + logging.info(f"Email sent from {email_user} to {email_to}") + else: + logging.info(f"ERROR: Email failed to send from {email_user} to {email_to}") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/send_credit_balance/readme.md b/scripts/send_credit_balance/readme.md new file mode 100644 index 0000000..ddd8908 --- /dev/null +++ b/scripts/send_credit_balance/readme.md @@ -0,0 +1,90 @@ +### Send Credit Balance + +This repo is an updated version (powershell => python) of my previous post about [Send me my credit balance](https://automationadmin.com/2018/02/ps-send-me-my-credit-balance/). This script will read messages using Gmail Rest API (no modules) from `me@gmail.com` sent from USAA automatically that tells you how much credit is available for each of your credit cards. + +- In this theoretical example, I have two credit cards: + - `buying_cc` which has a credit limit of `$5,500` which is defined around line 91 in main.py + - `buying_cc` has a number that ends in `9999` which is defined around line 168 in helpers.py + - We need this information because when parsing multiple emails that are similar, we need a unique value to filter on. Hopefully if you have two cards they have a different set of last four numbers. + - `bills_cc` which has a credit limit of `$4,500` which is defined around line 93 in main.py + +It parses the available credit amount and sends an email to `you@gmail.com` and a SMS message (optional) with the current budget. + +Budget is defined around line 39 and the script works like this: + +- Get the current day of the month +- If it is the first, there are 0 days until payday + - Send an email with your budget which is `your defined budget - (current credit card usage + upcoming bills)` + - On the first the upcoming bills amount is 0 +- If it is not the first: + - Send an email with your budget which is `your defined budget - (current credit card usage + upcoming bills)` + - Bills are a list object defined around lines 115 in main.py + +Pre-Requirements: + +- You must login to USAA and set a daily email alert of your credit card available balances. + - Unfortunately, this script is highly dependent on their automated email structure and will most likely break if anything changes. +- You must register an application with Gmail and ensure that you have a refresh token. See my previous post about this [here](https://automationadmin.com/2018/01/using-powershell-to-access-gmail-api/) + - If your token expires, follow [something like this](https://blog.macuyiko.com/post/2016/how-to-send-html-mails-with-oauth2-and-gmail-in-python.html) to get a new refresh token. +- This assumes you have python 3.6+ installed on Windows 10 client OS. + +#### To create: + +1. Type the following: + + ```powershell + + cd C:\scripts\schedtasks + mkdir send_credit_balance + + # create an active venv + python -m venv ./venv + .\venv\Scripts\activate + + # upgrade pip + C:\scripts\schedtasks\send_credit_balance\venv\scripts\python.exe -m pip install --upgrade pip + + # install stuff + pip install requests + pip install python-dotenv + + # deactivate venv + deactivate + + ``` + +2. Create a `.gitignore` with `.env`, `venv`, `__pycache`, and `logs` so that it is not tracked with git. + +3. Develop script and test with `python main.py` + +4. Once done, create `requirements.txt` by typing: + + ```powershell + cd C:\scripts\schedtasks\send_credit_balance + .\venv\Scripts\activate + C:\scripts\schedtasks\send_credit_balance\venv\scripts\python.exe -m pip freeze > requirements.txt + # deactivate venv + deactivate + ``` + +5. Next create `.bat` and `.ps1` scripts and name them exactly the same + + - Batch file: + + ```bash + pushd "%~dp0" + @ECHO OFF + PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" + popd + ``` + + - Powershell file: + + ```powershell + Set-Location "C:\scripts\schedtasks\send_credit_balance" + .\venv\Scripts\activate + python main.py + ``` + +6. Open up powershell as administrator and type `control schedtasks` to open Windows Task Scheduler. + - Create a task in the Task Scheduler that runs as the administrator since we need `logon as batch` rights and have it point to the batch script at whatever interval you want. diff --git a/scripts/send_credit_balance/requirements.txt b/scripts/send_credit_balance/requirements.txt new file mode 100644 index 0000000..d577b68 --- /dev/null +++ b/scripts/send_credit_balance/requirements.txt @@ -0,0 +1,6 @@ +certifi==2020.12.5 +chardet==4.0.0 +idna==2.10 +python-dotenv==0.15.0 +requests==2.25.1 +urllib3==1.26.5 diff --git a/scripts/send_credit_balance/send_credit_balance.bat b/scripts/send_credit_balance/send_credit_balance.bat new file mode 100644 index 0000000..c224730 --- /dev/null +++ b/scripts/send_credit_balance/send_credit_balance.bat @@ -0,0 +1,4 @@ +pushd "%~dp0" +@ECHO OFF +PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""'}" +popd diff --git a/scripts/send_credit_balance/send_credit_balance.ps1 b/scripts/send_credit_balance/send_credit_balance.ps1 new file mode 100644 index 0000000..f08f5ab --- /dev/null +++ b/scripts/send_credit_balance/send_credit_balance.ps1 @@ -0,0 +1,7 @@ + +Set-Location "c:\scripts\schedtasks\send_credit_balance" +.\venv\Scripts\activate +python main.py +# if the script had arguments +# $arg = "-f " + $currentFileName +# Start-Process -FilePath .\main.py -ArgumentList $arg -Wait \ No newline at end of file