The Reflection
10/31/2023 Jonathan Liu
Nearing the end of the first trimester of this year, our final project was to create a project that utilizes a front end and back end. I figured one of the easiest ways to utilize a front end and back end would be to create a social media platform that was similar to the likes of Twitter. This is evident in the name of our project, Phoenix as the Phoenix is a bird; however, a Phoenix is a bird with fire, which is where the second part of the name comes from, Mars. I visualize Mars as a fire sand planet, and so the goal was to create social media with a theme revolving around space.
Key Commits
Detecting URLs
The main point of these other than just making the structure of the base website layout, was namely making posts more interactive. It was my intent while making this project for text styling to be possible, with syntax highlighting, embedded images, and links. This is done with a regex, or regular expression to split the message into sections and to run specific checks on chunks of text to detect links, code, etc. The main thing that we will use to link our project to space will be the NASA API's such as APOD and EPIC.
Firebase Work / Backend
What brings our work to life would be the backend of our website, or the storage. Due to the unfortunate nature of our Amazon Web Services account, we could only really use EC2 for backend purposes and we were locked out of DyanmoDB. I was the main lead on designing the layout and posting system of the project, and worked on the Firebase backend. The project uses the Realtime Database service.
Each database entry is linked to a timestamp of how many milliseconds that have passed since January 1st 1970, and each entry takes in the values of the message content, it's likes, and it's username value.
The project uses Firebase Authentication with support for Google, Email and Password, and Anonymous coming soon.
As of the writing of this our project is still being actively developed, and we're interested in seeing where this project will go.
Psuedocode
A simple psuedocode script that shows iteration, variables, and console output
This psuedocode script takes variables x and y, repeats until x is more than y, and then displays the values
Collegeboard Reflection
I got a 54/66 on the practice test
In the test some concepts I hadn't previously known beforehand were on
- Question 3: Citizen Science
- Question 6: Internet Engineering Task Force
- Question 9: Iterative Process
- Question 23: Redundant Routing
- Question 46: Undecidable Problem
- Question 51: Symmetric Encryption
Test Corrections:
- Question 13: D as average character length went down
- Question 16: D I misread the question as it is least likely
- Question 23: B as the amount of paths is the smallest
- Question 32: A I misread the question
- Question 39: A
- Question 42: D Math error
- Quesiton 43: A: n2is reasonable
- Question 44: B: 17 will overflow a 4 bit
- Question 47: D: Private key for a public system
- Question 56: D: Error with starting index
- Question 58: A: Misread
- Question 59: B: Misread
Trimester 1 Reflection
This trimester I had a lot of fun meeting people in my class and working on simple projects. I had worked with
The jekyll pages format for the first time, which proved interesting for me, although I still prefer the standard html,js,css format
I had a stronger connection with Linux from this class that I don't think I would have obtained without this class.
For the future I intend to learn more programming languages and different branches of computer science.
Various scripts from the project
login.js
let emailPrompt = document.getElementById("UsernamePrompt")
let pwPrompt = document.getElementById("PasswordPrompt")
const provide = new firebase.auth.GoogleAuthProvider();
function signInWithGoogle()
{
firebase.auth()
.signInWithPopup(provide)
.then((result) => {
window.location = "./index.html"
//console.log(firebase.auth().currentUser)
})
.catch((error) =>
{
console.log(error)
})
}
function signUpWithEmail(email,password)
{
firebase.auth().createUserWithEmailAndPassword(email, password)
.then((userCredential) => {
// Signed in
window.location.href = "./index.html"
// ...
}).catch((error) => {
logInWithEmail(email,password)
});
}
function logInWithEmail(email,password)
{
firebase.auth().signInWithEmailAndPassword(email, password)
.then((userCredential) => {
window.location.href = "./index.html"
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("i hate you")
});
}
function signInAnon()
{
firebase.auth().signInAnonymously()
.then((userCredential) => {
window.location.href = "./index.html"
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
}
//setTimeout(() => {
document.getElementById("loginMenu").style.filter = "blur(0px)"
//}, 1000);
function emPW() //email password
{
signUpWithEmail(emailPrompt.value, pwPrompt.value)
}
loadpost.js
let inputField = document.getElementById("PostBodyInput")
let postMenu = document.getElementById("CreatePost")
inputField.addEventListener("keydown",function(e)
{
if(e.key == "Tab")
{
/* e.preventDefault()
let doc = inputField.ownerDocument.defaultView;
let sel = doc.getSelection();
let range = sel.getRangeAt(0);
let tabNode = document.createTextNode("\u00a0\u00a0\u00a0\u00a0");
range.insertNode(tabNode);
range.setStartAfter(tabNode);
range.setEndAfter(tabNode);
sel.removeAllRanges();
sel.addRange(range);
*/
e.preventDefault();
let start = this.selectionStart;
let end = this.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
this.value = this.value.substring(0, start) +
"\t" + this.value.substring(end);
// put caret at right position again
this.selectionStart =
this.selectionEnd = start + 1;
}
})
function submitPost()
{
const timestamp = Date.now();
let message = document.getElementById("PostBodyInput").value
let likes = 0
let username = auth.currentUser.displayName
let pfp = auth.currentUser.photoURL
try{
let handle = auth.currentUser.email.split("@")
handle = handle[0]
if (!/\S/.test(message)) { //white space
return
}
db.ref("Posts/" + timestamp).set({
message,
timestamp,
likes,
username,
handle,
pfp
});
hidePostMenu()
navBar.style.transform = 'translateY(0%)'
//console.log("Post was submited")
document.getElementById("PostBodyInput").value = ""
} catch(e) {
handle = null
if (!/\S/.test(message)) { //white space
return
}
db.ref("Posts/" + timestamp).set({
message,
timestamp,
likes,
username,
handle
});
hidePostMenu()
navBar.style.transform = 'translateY(0%)'
//console.log("Post was submited")
document.getElementById("PostBodyInput").value = ""
}
}
function hidePostMenu()
{
postMenu.style.display = "none"
}
function showPostMenu()
{
postMenu.style.display = "none"
}
function closeMenu(){
var navbar = document.getElementById("navbar")
document.getElementById('CreatePost').style.display = 'none';
// document.getElementById("PostBodyInput").value = ""in
// navbar.style.display = 'in';
navBar.style.transform = 'translateY(0%)'
}
loadpost.js
const postsRef = db.ref("Posts/")
//console.log(env.API_KEY)
let urlRegex = /(https?:\/\/[^\s]+)/g; //just detects links
let tildaRegex = /(```)/; //code block opener regex
//let postRegex = /(https?:\/\/[^\s]+)|(```)/g
let PostCol = document.getElementById("Posts")
postsRef.on("child_added", function(snapshot) {
const dbPost = snapshot.val();
let Username = dbPost.username
let post = document.createElement("div")
post.className = "Post"
PostCol.insertBefore(post, PostCol.firstChild)
let userHold = document.createElement("div")
userHold.className = "UserHold"
post.appendChild(userHold)
let pfp = document.createElement("img")
pfp.src = "images/icon.png"
if(dbPost.pfp)
{
pfp.src = dbPost.pfp
}
pfp.className = "PostImg"
userHold.appendChild(pfp)
let usernameAnchor = document.createElement("a")
usernameAnchor.innerText = Username
usernameAnchor.href = `profile.html?Name=${Username}`
userHold.appendChild(usernameAnchor)
let handle = dbPost.handle
let handleSpan = document.createElement("span")
handleSpan.innerText = "@" + handle
usernameAnchor.appendChild(document.createElement("br"))
usernameAnchor.appendChild(handleSpan)
handleSpan.className = "handle"
let postContent = document.createElement("pre")
let postText = urlify(dbPost.message)//(dbPost.message).split(tildaRegex)
//console.log(dbPost.likes)
//console.log(postText)
//postText = urlify(postText)
let isCodeOpen = false
let codeSection = null
postText = postText.filter(elm => elm)
for(let i=0;i /g, ">").replace(/"/g, """).replace(/'/g, "'");
}
else if(isCodeOpen)
{
dontAppend = true
codeSection.append(postText[i]) //+= postText[i]
}
else if((postText[i] || '').split(urlRegex).length > 1)
{
textSection = document.createElement("a")
textSection.href = postText[i]
textSection.target = "_blank"
textSection.rel = "noopener"
if(postText[i].match(/^http[^\?]*.(jpg|jpeg|gif|png|tiff|bmp|webp)(\?(.*))?$/gmi) != null)
{
textSection = document.createElement("img")
textSection.src = postText[i]
textSection.className = "PostPhoto"
}
}
//console.log(postText[i].match(/\n|\r|\t|\s/g))
if(!dontAppend)
{
postContent.appendChild(textSection)
textSection.innerText = String(postText[i])
}
}
//console.log
post.appendChild(postContent)
let postDate = document.createElement("span")
let dateString = new Date(dbPost.timestamp)
const dd = String(dateString.getDate()).padStart(2, '0');
const mm = String(dateString.getMonth() + 1).padStart(2, '0'); //January is 0!
const yyyy = dateString.getFullYear();
let likeRow = document.createElement("div")
let likes = document.createElement("span")
likes.className = "likes"
let likeButton = document.createElement("button")
likeButton.innerText = "Like" //remember, .innerHTML should be avoided when possible
likeButton.onclick = function(){likePost(dbPost.timestamp, dbPost)}
likes.innerText = "Likes: " + String(dbPost.likes) + "\n"
let time = dateString.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
dateString = mm + '/' + dd + '/' + yyyy + " at " + time;
postDate.innerText = dateString
postDate.className = "PostDate"
likeRow.appendChild(postDate)
likeRow.appendChild(likes)
likeRow.appendChild(likeButton)
post.appendChild(likeRow)
post.id = dbPost.timestamp
})
function likePost(timeID, postData){
//console.log(postData.likes)
firebase.database().ref("Posts/" + String(timeID)).update({
//message: postData.message,
//timestamp: timeID,
likes: parseInt(postData.likes) + 1
})
}
function urlify(text) {
return text.split(urlRegex).flatMap(part => part.split(tildaRegex));
}
postsRef.on('child_changed', function(snapshot){
let dbPost = snapshot.val()
let timestamp = dbPost.timestamp
document.getElementById(timestamp).querySelector(".likes").innerText = `Likes: ${dbPost.likes}\n`
});
const firebaseConfig = {
apiKey: "no",
authDomain: "twitterclone-aff2f.firebaseapp.com",
databaseURL: "https://twitterclone-aff2f-default-rtdb.firebaseio.com",
projectId: "twitterclone-aff2f",
storageBucket: "twitterclone-aff2f.appspot.com",
messagingSenderId: "1089185872675",
appId: "1:1089185872675:web:350744b5c281b8998b2a96",
measurementId: "G-7J6C100Q36"
};
const app = firebase.initializeApp(firebaseConfig);
//const analytics = firebase.getAnalytics(app);
const db = firebase.database();
const auth = firebase.auth()
from flask import Flask
from dotenv import load_dotenv
import requests
from flask_cors import CORS
load_dotenv()
url = "https://api.nasa.gov/planetary/apod?api_key="
import os
key = os.environ["api_key"]
app = Flask(__name__)
CORS(app)
@app.route("/api/nasa", methods=["GET"])
def get_photo():
response = requests.get(url + key)
response.raise_for_status
#print("API response received")
data = response.json()
return data
@app.route("/")
def index():
return "no"
app.run(host="0.0.0.0", port=8005, debug=True)
Week 0: I intially thought the live review was a one time event however I came to learn I would be very wrong
Week 1-3: I spent most of my time learning how to use WSL, forking the student repo, and using python with IPYNB
Week 4-6: I spent most of these weeks just styling my website to have the blue theme and spinning icon It was more writing ipynb files and live review work
Week 7-12: These weeks were filled with work on Phoenix as seen above. I had prior experience with the database given my project, Crescent which was a faulty predecessor to my modern site