Setting up Machines with Conda Scripts

GitHub

  • What is GitHub?
  • Why do we use GitHub? Why not Google Drive or messages?
  • What's the difference between Git and GitHub?
  • Name as many Git commands as you can:

Two Main Machines

  • Which is better, MacOS or Windows?
  • Give some differences between MacOS and Windows in terms of the development we use in APCSP:
  • If you are on Windows, you want to skip the MacOS Setup instructions.

Our Tools:

  • What is the first tool you remember installing?
  • Why was installations so hard the first time?
  • 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.

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?
git checkout [branch] # what does it do?
git fork {repos-name-here.git} # what does it do?
git commit -m {"commit-msg"} # what does it do?
git pull # what does it do?
git push # what does it do?

# 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

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.
  • frontend hack goes here
  • backend hack goes here

NOTES

  • Github is a very poweful tool for coders to interact and store their code, also it facilitates a way to create websites collaborate, and to open source ones code
  • Fastpages is a part of github pages and used within APCSP, you can create it by forking the repo adn gitclone into your terminal
  • Kernals are a way to input a coding system into your computer by a set of commands completed within the terminal
  • Vscode is a code text editor allowing people to push pull and commit.

Incorporations

  • Fastpages
    • I used this in order to fetch and have an active frontend going allowing for my unique process.
  • VSCode
    • I used this in order to create code and push it front git, also when I am working with other collaborators I use it to pull their changes into my program.
  • Terminal/WSL
    • I used this in order to clone my repo and add it to my file, also I was able to delete files/repos and clean up my files on vscode. Also I could use the terminal to run commands to run server such as python3 main.py and bundle exec jekyll serve

CRUD

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

from model.houses import Houseadd

house_api = Blueprint('house_api', __name__,
                   url_prefix='/api/houses')

# Creation of class for the API
api = Api(house_api)

class HouseAPI:        
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            # These conditionals validate the api to make sure when an input is entered there are no keys that are left out and parameters are full
            ''' Avoid garbage in, error checking '''
           
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': f'Name is missing, or is less than 2 characters'}, 400
            
            uid = body.get('uid')
            if uid is None or len(uid) < 2:
                return {'message': f'User ID is missing, or is less than 2 characters'}, 400
         

            baths = body.get('baths')
            if baths is None or len(baths) < 0:
                return {'message': f'Baths is missing, or is less than 2 characters'}, 210
            beds = body.get('beds')
            if beds is None or len(beds) < 1:
                return {'message': f'Beds is missing, or is less than 2 characters'}, 210
            price = body.get('price')
            if price is None or len(price) < 1:
                return {'message': f'Price is missing, or is less than 2 characters'}, 210
            

            ''' #1: Key code block, setup USER OBJECT '''
            uo = Houseadd(name=name, uid=uid, beds=beds,baths=baths, price = price)
            
            ''' Additional garbage error checking '''
            
            
            
            ''' #2: Key Code block to add user to database '''
            # create user in database
            house = uo.create()
            # success returns json of user
            if house:
                return jsonify(house.read())
            # failure returns error
            return {'message': f'Processed {name}, either a format error or User ID {uid} is duplicate'}, 400

    class _Read(Resource):
        def get(self):
            users = Houseadd.query.all()    # read  all houses from database
            json_ready = [user.read() for user in users]  
            return jsonify(json_ready)  
    
    class _Security(Resource):

        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Get Data '''
            uid = body.get('uid')
            if uid is None or len(uid) < 2:
                return {'message': f'User ID is missing, or is less than 2 characters'}, 400
            
            
            ''' Find user '''
            
            
            ''' authenticated user '''
            

            

    # building RESTapi endpoint
    api.add_resource(_Create, '/create')
    api.add_resource(_Read, '/')
    api.add_resource(_Security, '/authenticate')

DATABASE

""" database dependencies to support sqliteDB examples """
from random import randrange
from datetime import date
import os, base64
import json

from __init__ import app, db
from sqlalchemy.exc import IntegrityError
from werkzeug.security import generate_password_hash, check_password_hash


''' Tutorial: https://www.sqlalchemy.org/library.html#tutorials, try to get into Python shell and follow along '''

# This is where the keys are within this table to identify for the SQL DATABASE!
class Update(db.Model):
    __tablename__ = 'posts'

    
    id = db.Column(db.Integer, primary_key=True)
    note = db.Column(db.Text, unique=False, nullable=False)
    image = db.Column(db.String, unique=False)
    beds = db.Column(db.String, unique=False)
    baths = db.Column(db.String, unique=False)
    price = db.Column(db.String, unique=False)
    
    userID = db.Column(db.Integer, db.ForeignKey('users.id'))

  
    def __init__(self, id, note, image, beds, baths, price):
        self.userID = id
        self.note = note
        self.image = image
        self.beds = beds
        self.baths = baths
        self.price = price

    
    def __repr__(self):
        return "Notes(" + str(self.id) + "," + self.note + "," + str(self.userID) + ")"

  
    def create(self):
        try:
            
            db.session.add(self)  
            db.session.commit()  
            return self
        except IntegrityError:
            db.session.remove()
            return None

    
    def read(self):
        # encode image
        path = app.config['UPLOAD_FOLDER']
        file = os.path.join(path, self.image)
        file_text = open(file, 'rb')
        file_read = file_text.read()
       # file_encode = base64.encodebytes(file_read)
        
        return {
            "id": self.id,
            "userID": self.userID,
            "note": self.note,
            "beds": self.beds,
            "price": self.price,
            "baths":self.baths,
            "image": self.image,
           # "base64": str(file_encode)
        }



class Houseadd(db.Model):
    __tablename__ = 'users' 

    # Define the House schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _name = db.Column(db.String(255), unique=False, nullable=False)
    _uid = db.Column(db.String(255), unique=True, nullable=False)
    _price = db.Column(db.String(255), unique=False, nullable=True)
    _beds = db.Column(db.String(255), unique=False, nullable=False)
    _baths = db.Column(db.String(255), unique=False, nullable=False)


    posts = db.relationship("Update", cascade='all, delete', backref='users', lazy=True)

    
    def __init__(self, name, uid, beds, baths, price):
        self._name = name    
        self._uid = uid
        self._beds = beds
        self._baths = baths
        self._price = price

   
    @property
    def name(self):
        return self._name
    
    
    @name.setter
    def name(self, name):
        self._name = name
    
   
    @property
    def uid(self):
        return self._uid
    
   
    @uid.setter
    def uid(self, uid):
        self._uid = uid
        
   
    def is_uid(self, uid):
        return self._uid == uid
   
    @property
    def beds(self):
       return self._beds
  
    @beds.setter
    def beds(self, beds):
       self._beds = beds


    def is_beds(self, beds):
       return self._beds == beds
    
    @property
    def baths(self):
       return self._baths
  
    @baths.setter
    def baths(self, baths):
       self._baths = baths


    def is_baths(self, baths):
        return self._baths == baths
    
    @property
    def price(self):
       return self._price
  
    @price.setter
    def price(self, price):
       self._price = price


    def is_baths(self, price):
       return self._price == price
  

    
  
    def __str__(self):
        return json.dumps(self.read())

    
    def create(self):
        try:
           
            db.session.add(self) 
            db.session.commit()  
            return self
        except IntegrityError:
            db.session.remove()
            return None

    
    def read(self):
        return {
            "id": self.id,
            "name": self.name,
            "uid": self.uid,
            "beds": self.beds,
            "baths": self.baths,
            "price": self.price,
          #  "posts": [post.read() for post in self.posts]
        }

    
    def update(self, name="", uid="", beds="", baths="", price=""):
        """only updates values with length"""
        if len(name) > 0:
            self.name = name
        if len(uid) > 0:
            self.uid = uid
        if len(beds) > 0:
           self.beds = beds
        if len (baths) > 0:
           self.baths = baths 
        if len(price) > 0:
           self.price = price    
        db.session.commit()
        return self

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


"""Database Creation and Testing """


# Builds working data for testing
def initHouses():
    with app.app_context():
        """Create database and tables"""
        db.init_app(app)
        db.create_all()
        db.session.commit()
        
        """Tester data for table"""
        u1 = Houseadd(name='house 1', uid='h1', beds= 'five', baths='three', price='500$/night')
        u2 = Houseadd(name='house 2', uid='h2', beds='two', baths='one', price='200$/night')
        u3 = Houseadd(name='house 3', uid='h3', beds='four', baths='two', price='300$/night')
        u4 = Houseadd(name='house 4 ', uid='h4', beds='four', baths='three', price='400$/night')
        u5 = Houseadd(name='house 5', uid='h5', beds='four', baths='three', price='100$/night')

        users = [u1, u2, u3, u4, u5]

        """Builds sample user/note(s) data"""
        for user in users:
            try:
                '''add a few 1 to 4 notes per user'''
                for num in range(randrange(1, 4)):
                     note = "#### " + user.name + " note " + str(num) + ". \n Generated by test data."
                user.posts.append(Update(id=user.id, note=note, beds=user._beds, price=user._price, baths=user._baths, image='ncs_logo.png'))
                '''add user/post data to table'''
                user.create()
            except IntegrityError:
                '''fails with bad or duplicate data'''
                db.session.remove()
                print(f"Records exist, duplicate email, or error: {user.uid}")

Fetch/FRONTEND

---
title: Add/Explore Houses
layout: base
permalink: /data/database
tags: [javascript, fetch, get, post, put]
---


<p>Add Your House/Find A House
  <br>1. Browse Available Houses 
  <br>2. Contact Us To Book In the review tabs<br>
  3. Add a house for rental with the form

</p>

<table>
  <thead>
  <tr>
    <th>Price</th>
    <th>Beds</th>
    <th>Baths</th>
    <th>Name</th>
    <th>House ID</th>
  </tr>
  </thead>
  <tbody id="result">
    <!-- javascript generated data -->
  </tbody>
</table>

<p>Housing</p>

<form action="javascript:create_users()">
    <p><label>
        Name:
        <input type="text" name="name" id="name" required>
    </label></p>
    <p><label>
        Price:
        <input type="text" name="price" id="price" required>
    </label></p>
    <p><label>
        Beds:
        <input type="text" name="beds" id="beds" required>
    </label></p>
    <p><label>
        Baths:
        <input type="text" name="baths" id="baths" required>
    </label></p>
    <p><label>
      uid:
      <input type="text" name="UID" id="uid" required>
  </label></p>
    <p>
      
        <button> Add House </button>
    </p>
</form>

<script>
  
  const resultContainer = document.getElementById("result");
  // fetch from the local host with the DATABASE!
  // var url = "https://finalcptperiod4.duckdns.org/api/houses/"
  const url = "http://127.0.0.1:8753/api/houses"

  

  const create_fetch = url + '/create';
  const read_fetch = url + '/';

  // Load houses on page entry
  read_users();


  // Display C in CRUD to create houses on the page 
  function read_users() {
    const read_options = {
      method: 'GET', 
      mode: 'cors', 
      cache: 'default', 
      credentials: 'omit', 
      headers: {
        'Content-Type': 'application/json'
      },
    };

   
    fetch(read_fetch, read_options)
     // This commands fact proofs the data for an error message that could happen through server or database
      .then(response => {
        // check for response errors
        if (response.status !== 200) {
            const errorMsg = 'Database read error: ' + response.status;
            console.log(errorMsg);
            const tr = document.createElement("tr");
            const td = document.createElement("td");
            td.innerHTML = errorMsg;
            tr.appendChild(td);
            resultContainer.appendChild(tr);
            return;
        }
        // valid response will have json data
        response.json().then(data => {
            console.log(data);
            for (let row in data) {
              console.log(data[row]);
              add_row(data[row]);
            }
        })
    })
    // catch server issues
    .catch(err => {
      console.error(err);
      const tr = document.createElement("tr");
      const td = document.createElement("td");
      td.innerHTML = err;
      tr.appendChild(td);
      resultContainer.appendChild(tr);
    });
  }

  function create_users(){
    // This reads though the data and make sues everything that is specific is created and double checked which was set within the .py file  through parameters and conditional.
    const body = {
        price: document.getElementById("price").value,
        beds: document.getElementById("beds").value,
       baths: document.getElementById("baths").value,
        name: document.getElementById("name").value,
        uid: document.getElementById("uid").value
  
    };
    const requestOptions = {
        method: 'POST',
        body: JSON.stringify(body),
        headers: {
            "content-type": "application/json",
            'Authorization': 'Bearer my-token',
        },
    };

    
  
    fetch(create_fetch, requestOptions)
      .then(response => {
        // more db error proofing
        if (response.status !== 200) {
          const errorMsg = 'Database create error: ' + response.status;
          console.log(errorMsg);
          const tr = document.createElement("tr");
          const td = document.createElement("td");
          td.innerHTML = errorMsg;
          tr.appendChild(td);
          resultContainer.appendChild(tr);
          return;
        }
        
        response.json().then(data => {
            console.log(data);
            
            add_row(data);
        })
    })
  }

  function add_row(data) {
    const tr = document.createElement("tr");
    const price = document.createElement("td");
    const beds = document.createElement("td");
    const baths = document.createElement("td");
    const name = document.createElement("td");
    const uid = document.createElement("td");
  

    // obtain data that is specific to the API
    price.innerHTML = data.price; 
    beds.innerHTML = data.beds; 
    baths.innerHTML = data.baths; 
    name.innerHTML = data.name; 
    uid.innerHTML = data.uid;

    // add HTML to container these functions also serve as C CRUD = creates ie creating the house data.
    tr.appendChild(price);
    tr.appendChild(beds);
    tr.appendChild(baths);
    tr.appendChild(name);
    tr.appendChild(uid);

    resultContainer.appendChild(tr);
  }

</script>

Link To The Video With The FRONTEND ON FAST PAGES

Video