Developing with Frontend & Backend
To get started with development, you need tools. You'll learn how to get the necessary tools here using Conda scripts.
- Setting up Machines with Conda Scripts
- Hacks
- NOTES
- Incorporations
- CRUD
- DATABASE
- Fetch/FRONTEND
- Link To The Video With The FRONTEND ON FAST PAGES
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.
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
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
(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.
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
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 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}")
---
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>