Setting up Machines with Conda Scripts

GitHub

  • What is GitHub?: GitHub is a software company which allows for users to create their own programming projects and collaborate with others if they so desire.
  • Why do we use GitHub? Why not Google Drive or messages? GitHub is better for creating programming projects and storing code. Google drive is not very useful for storing code as the code will not be formatted correctly and can't be run.
  • What's the difference between Git and GitHub? Git is the version control system while GitHub is the service that stores code and makes it accessible to many people.
  • Name as many Git commands as you can: Git push, git merge, git pull, git fork, git clone

Two Main Machines

  • Which is better, MacOS or Windows? Both MacOs and Windows are better in their own ways. Mac is better that Windows in that it is easier to navigate to different applications and sites (in my opinion). I don't use Windows, so I don't have any reasons why it would be better. I prefer MacOS over Windows.

  • Give some differences between MacOS and Windows in terms of the development we use in APCSP:

I'm not sure about how things are done with Windows as I use mac, but I know that things are installed differently on windows

  • If you are on Windows, you want to skip the MacOS Setup instructions.

Our Tools:

  • What is the first tool you remember installing? Im not sure if this is the first tool I installed, but one of the first tools I installed was VSCode.

  • Why were installations so hard the first time? Installations were hard during the first few weeks of CSP as I didn't know what I was doing and wasn't used to installing new things.

  • Without looking back at previous notes, name three tools you remember installing. This can be kernels, extensions, any installation for APCSP, and also write why it is needed.

  1. Python and JavaScript Kernels - Needed so I can code in python and javascript
  2. VSCode - Allows me to write code in different files, work on many different repositories at a time, and create blog posts
  3. Docker - Allows me to local host my projects to quickly see what my changes do

Actual Installations:

Tool setup is a week 0 thing. You should already have the knowledge to set up your machine. There is also a high chance you had to remove your environments and set up your machine again due to errors. If, for some reason, these don't apply to you, go here to set up your machine, here to check everything working with Bash, and here for Docker setup, which are the main tools on our machine needed to develop in APCSP.

MacOS Conda Scripts

After installing Homebrew, VSCode, and Python2, you'll need to run these Homebrew commands:

brew list # list packages
brew update # update package list
brew upgrade # upgrade packages
brew install git  # install latest git
brew install python # install python3 for development
python --version # version of python3 installed
brew install java # openjdk install

Windows Conda Scripts

To get set up, run these commands:

wsl --install
wsl --list
wsl --install -d Ubuntu-20.04
# restart machine
wsl
cd ~
mkdir vscode
ls
cd ~/vscode  # changes the directory to path for vscode files
git clone https://github.com/nighthawkcoders/APCSP.git # clone repo
cd APCSP  # changes the directory to path for APCSP repos assets
code .  # opens APCSP in VSCode
cd ..    # changes the directory to the previous/parent directory
git config --global user.email mygmail@gmail.com  # tell git your email
git config --global user.name mygithub   # tell git your github id
shay@MSI:/mnt/c/Users/ShayM$ git config --global user.email your@email.here
shay@MSI:/mnt/c/Users/ShayM$ git config --global user.name yourusernamehere
# restart machine
PS C:\Users\UserName> wsl  # Windows prompt to WSL command
cd /tmp
wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh
chmod +x Anaconda3-2022.05-Linux-x86_64.sh
# Answer yes to all the prompts
./Anaconda3-2022.05-Linux-x86_64.sh
# run apt package commands now
sudo apt list # list packages
sudo apt update # update package list
sudo apt upgrade # upgrade packages
sudo apt install python2 # install python2 for package dependencies
sudo apt install python3 python3-pip # install python3 and pip3 for development
python --version  # version of python3 should be shown
sudo apt install default-jdk default-jre  # java install
java --version  # java runtime version
javac --version # java compiler version
sudo apt install unzip  # unzip utility

Setting Up Kernels

Now that you have everything installed on MacOS/Windows, we need to get kernels installed so that we can develop inside Fastpages notebooks. To do that, run these commands on both MacOS and Windows:

(base) id:~$ conda --version 
(base) id:~$ conda install jupyter # install jupyter
(base) id:~$ jupyter kernelspec list # list installed kernels
Available kernels:
  python3    /home/shay/.local/share/jupyter/kernels/python3

(base) id:~$ # start in home directory
(base) id:~$ pip install bash_kernel # download bash kernel
Collecting bash_kernel
  Downloading bash_kernel-0.7.2-py2.py3-none-any.whl (15 kB)
Requirement already satisfied: pexpect>=4.0 in ./anaconda3/lib/python3.9/site-packages (from bash_kernel) (4.8.0)
Requirement already satisfied: ptyprocess>=0.5 in ./anaconda3/lib/python3.9/site-packages (from pexpect>=4.0->bash_kernel) (0.7.0)
Installing collected packages: bash-kernel
Successfully installed bash-kernel-0.7.2
(base) id:~$ python -m bash_kernel.install # install kernel
Installing IPython kernel spec
(base) id:~$ jupyter kernelspec list # list kernels
Available kernels:
  bash       /home/shay/.local/share/jupyter/kernels/bash
  python3    /home/shay/.local/share/jupyter/kernels/python3

(base) id:~$ conda install nodejs # node is framework for JavaScript kernel
(base) id:~$ npm -version  # node package manager comes with nodejs
(base) id:~$ npm install -g ijavascript  # get the kernel
(base) id:~$ ijsinstall # install javascript kernel
(base) id:~$ jupyter kernelspec list # list kernels
Available kernels:
  bash          /home/shay/.local/share/jupyter/kernels/bash
  javascript    /home/shay/.local/share/jupyter/kernels/javascript
  python3       /home/shay/.local/share/jupyter/kernels/python3

By now, you should already know how to clone Git repositories into your VSCode directory. Once you do that, you're all set for developing with GitHub Pages and Fastpages!

Before We Set Up Pages, A Guide to Git

As we've discussed, Git is different from GitHub. Because GitHub is merely the place where we store Git repos, we use Git's commands to help us get, open, and configure these repositories. Here are some of the Git commands you should be using a lot (In the comments, tell what each Git command does):

git clone {repos-name-here.git} # what does it do? --> allows you to make a copy of a repository that you can edit and modify
git checkout [branch] # what does it do? --> allows you to create a local branch
git fork {repos-name-here.git} # what does it do? --> similar to git clone, but this copy is not affected by changed made to the original repository
git commit -m {"commit-msg"} # what does it do? --> allows you to save changes to the repository locally
git pull # what does it do? --> allows you to pull changes that have been made by others to the remote repository
git push # what does it do? --> allows you to sync your changes with the remote repository

# After this line, name other commands that you can use and what they do. This should be easy, as you've already answered the qeue
git merge # --> used to merge your local changes with what is the the remote repository
git log # --> allows you to view the history of changes of the repository

Setting Up GitHub Pages

Some of you may have come to know that GitHub Pages is starting to become outdated. So why do we still use it? The answer is that we are in a class, and following a curriculum with something like GitHub Pages is much easier than creating portfolio content from scratch, which becomes quite unecessary. Therefore, we can use GitHub Pages to create this content instead. On the topic of unecessary vs necessary coding, we don't need to make GitHub Pages from scratch as opposed to using a template that our very own Mr. Mortensen created for us. To do that, we can go to the Leuck Reunion repository and use the template to make our own GitHub Pages. Then, in Ubuntu, we can git clone our repository and open it in VSCode. After we have it open, the last thing we want to do is set up local hosting for this website, so that we can preview it and make changes in real time. To do that, head here to install Jekyll for Ubuntu, here to install Ruby next, and here to finalize the process by installing Bundler.

Setting Up FastPages

In Setting Up Github Pages, we talked about how it is easier to use a template to create portfolio content. It is also easier to use a template when creating the portfolio itself. To do that, we can use Fastpages, which is what we have been using to show our blogs, code, and projects. However, Fastpages has been deprecated for some time now, so the instructions in Week 0 won't be effective. So, we need to fork the APCSP Fastpages. To do that, follow this video to get started developing with Fastpages.

Hacks

  • Show how you incorporate three tools that we have installed into your project. 0.1 points for each feature. (0.3). This can include code, but definitely blog about it.
  1. VSCode: I used VSCode as my code browsing tool to develop the code for my projects and local host it to find and fix errors in my code. VSCode is also very helpful with debugging.

  2. Python and JavaScript Kernels: The API and database part of my code is created with the python kernel. The JavaScript kernel was used to code the frontend JavaScript part of my code with which the user interacts with.

  3. Docker: I used docker to local host my project and find errors that may have arisen. With local hosting, it's very easy to find these errors and find out how they can be fixed. It's also useful to keep track of my progress and how functional the code is.

Video of Working Project

Project: Link

Frontend Code

This code will present the data stored in the database to the user. This will also take in the user's input, calculate the output, and present the output to the user which will then be stored in the database with the backend code.

<style>
    h1 {
        color: blue;
        margin-bottom: 60px;
        font-family; georgia;
        text-align: center;
    }

    iframe {
        width: 90%;
        height: fixed;
        filter: invert(75%);
    }

    .content{
        display: flex;
        justify-content: center;
        flex-direction: column;
    }

    .content>*{
        padding: 10px;
    }
</style>


<div id='content'>
<form id='nameForm'>
<div class='form-uname'>
    <label id='nameLable' for='nameField'>Input the City:</label>
    <input id='nameField' type='text' maxlength='25'>
</div>
<div class='form-sub'>
    <button id='subButton' type='button'>Submit!</button>
</div>
</form>

<div id="text"></div>

<div>
    <p id='result'></p></div>
</div>

<script type="text/javascript">

latitude = 0;
longitude = 0;

// add code to get city name from user

function getUserName() {
var nameField = document.getElementById('nameField').value;
var result = document.getElementById('result');

const options = {
	method: 'GET',
	headers: {
		'X-Access-Token': 'c203885d962780e0f71c0a1e65db31e3',
		'X-RapidAPI-Key': 'f56b20ef1cmsh82a127be1b400c6p1e21bcjsn093efcf02b8f',
		'X-RapidAPI-Host': 'travelpayouts-travelpayouts-flight-data-v1.p.rapidapi.com'
	}
};

fetch('https://travelpayouts-travelpayouts-flight-data-v1.p.rapidapi.com/data/en-GB/cities.json', options)
	.then(response => response.json())
	.then(response => {
        console.log(response)

var dictionary = 0

for (let i = 0; i < response.length; i++){
    if (response[i]['name'] == nameField){
        dictionary = response[i]
        console.log(dictionary) 
    }
}

if (dictionary == 0){
    const textDiv = document.getElementById('text');    
    const p = document.createElement('P');
    const pText = document.createTextNode("City not found.");
    textDiv.appendChild(p);
    p.appendChild(pText);

}

// add code to search for the user input city in the API response and fetch latitude and longitude for it

        latitude = dictionary.coordinates.lat
        longitude = dictionary.coordinates.lon

        url = 'https://aerodatabox.p.rapidapi.com/airports/search/location/' + latitude + '/' + longitude + '/km/50/16?withFlightInfoOnly=false'

        const textDiv = document.getElementById('text');
        
        const p = document.createElement('P');
        const pText = document.createTextNode('');

        textDiv.appendChild(p);
        p.appendChild(pText);

        const aerodataboxOptions = {
            method: 'GET',
            headers: {
                'X-RapidAPI-Key': 'f56b20ef1cmsh82a127be1b400c6p1e21bcjsn093efcf02b8f',
                'X-RapidAPI-Host': 'aerodatabox.p.rapidapi.com'
            }
        };

    fetch(url, aerodataboxOptions)
        .then(response => response.json())
        .then(response => {
            console.log(response)

var airport = response.items[0].name
var num = 0

for (let i = 0; i < airport.length; i++){
    if (airport[i] == ","){
        num += 2
        break
    }
    num += 1
}

airport = airport.slice(num)

// add code to extract airport name and print on the webpage

            const textDiv = document.getElementById('text');
            
            // const p = document.createElement('P');
            const pText = document.createTextNode("Nearest Airport to " + nameField + ": " + airport);

            // textDiv.appendChild(p);
            p.appendChild(pText);
            })
        .catch(err => console.error(err));
        })
	.catch(err => console.error(err));
}

var subButton = document.getElementById('subButton');
subButton.addEventListener('click', getUserName, false); 




</script>

Backend Code

The first code cell contains the API which is based on the database model which is in the second code cell. These work together to store data which will be presented to the user with the frontend code (previous code cell).

from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource # used for REST API building

from model.airportmodel import AirportPost

airport_api = Blueprint('airport_api', __name__,
                   url_prefix='/api/airport')

# API docs https://flask-restful.readthedocs.io/en/latest/api.html
api = Api(airport_api)

class AirportPostAPI(Resource):        
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            # validate name
            city = body.get('city')
            airport = body.get('airport')
   
            ''' #1: Key code block, setup USER OBJECT '''
            uo = AirportPost(city=city, airport=airport)
            
            ''' #2: Key Code block to add user to database '''
            # create entry in database
            user = uo.create()
            # success returns json of entry
            if user:
                return jsonify(user.read())
            # failure returns error
            return {'message': f'Processed {city}, format error'}, 210

    class _Read(Resource):
        def get(self):
            users = AirportPost.query.all()    # read/extract all entries from database
            json_ready = [user.read() for user in users]  # prepare output in json
            return jsonify(json_ready)  # jsonify creates Flask response object, more specific to APIs than json.dumps

    class _Delete(Resource):
        def delete(self):
            ''' Read data for json body '''
            body = request.get_json()
            city = body.get('city')

            airports = AirportPost.query.all()    # read/extract all users from database
            
            # Find the city in the database and call the delete
            for airport in airports:
                if airport.city == city:
                    airport.delete()
                    return
            
            # delete operation does not return any value
            return {'message': f' {city} is not in database'}, 210

        
    # building RESTapi endpoint
    api.add_resource(_Create, '/create')
    api.add_resource(_Read, '/')
    api.add_resource(_Delete, '/delete')
""" database dependencies to support sqliteDB examples """
import os
import json
from __init__ import app, db
from sqlalchemy.exc import IntegrityError

class AirportPost(db.Model):
    __tablename__ = 'airportposts'  # table name is plural, class name is singular

    # Define the User schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _city = db.Column(db.String(255), unique=True, nullable=False)
    _airport = db.Column(db.String(255), unique=False, nullable=False)
    
    # constructor of a User object, initializes the instance variables within object (self)
    def __init__(self, city, airport):
        self._city = city    # variables with self prefix become part of the object, 
        self._airport = airport

    # a name getter method, extracts name from object
    @property
    def city(self):
        return self._city
    
    # a setter function, allows name to be updated after initial object creation
    @city.setter
    def city(self, city):
        self._city = city
    
    # a getter method, extracts email from object
    @property
    def airport(self):
        return self._airport
    
    # a setter function, allows name to be updated after initial object creation
    @airport.setter
    def uid(self, airport):
        self._airport = airport
    
    # output content using str(object) in human readable form, uses getter
    # output content using json dumps, this is ready for API response
    def __str__(self):
        return json.dumps(self.read())

    # CRUD create/add a new record to the table
    # returns self or None on error
    def create(self):
        try:
            # creates a person object from User(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Users table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None

    # CRUD read converts self to dictionary
    # returns dictionary
    def read(self):
        return {
            "city": self.city,
            "airport": self.airport
        }
    

    # CRUD update: updates user name, password, phone
    # returns self
    def update(self, city="", airport=""):
        """only updates values with length"""
        if len(city) > 0:
            self.city = city
        if len(airport) > 0:
            self.airport = airport
        db.session.commit()
        return self

    # CRUD delete: remove self
    # None
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None


"""Database Creation and Testing """


# Builds working data for testing
def initAirports():
    with app.app_context():
        """Create database and tables"""
        
        db.create_all()
        """Tester data for table"""
        u1 = AirportPost(city="Paris", airport="Paris-Le Bourget")
        u2 = AirportPost(city="Berlin", airport="Berlin-Tegel")

        users = [u1, u2]
        users = []

        """Builds sample user/note(s) data"""
        for user in users:
            try:
                user.create()
            except IntegrityError:
                '''fails with bad or duplicate data'''
                db.session.remove()
                print(f"Records exist, duplicate email, or error: {user.uid}")