08Aristodemus24 / project-alexander

A portfolio website of all my projects.

Home Page:https://project-alexander.vercel.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WEBSITE DEPLOYED AND IN PRODUCTION

My portfolio website for all my AI/ML projects. Built with Svelte, Vanilla CSS, Leonardo.AI, Manim and Flask

Frontend

Designing website template:

Prerequisites to do: 1.

To do:

  1. create designs for each section on paper for mobile and desktop a. about b. skills or technologies used b. projects c. experience d. contact me
  2. create background image in landing page a. could use AI generated images from leonardo b. could use photoshop to collage images from mediafiles
  3. figure out colors to be used
  4. figure out shape, border, texture, etc. of components like buttons, container, socials, navbar items, etc.
  5. uninstall bulma and switch to bootstrap for more components and community support
  6. uninstall dotenv since dotenv will just be handled by backend server proxy
  7. include only relevant repositories by maybe including a rules lookup/dictionary or iterable that contains only the repositories to be displayed or not displayed
  8. make svgs for python, tensorflow, sklearn, numpy, matplotlib, seaborn, pandas, django, c, c++, html, css, sass, bootstrap, javascript, photoshop, react, postgresql, firebase, git

a. keras:

b. bootstrap:

c. C:

d. C++:

e. numpy:

f. photoshop:

g. pytorch:

h. tensorflow:

i. scikit-learn:

j. CSS:

k. django:

l. firebase:

m. flask:

n. git:

o. html:

p. JS:

q. pandas:

r. PostgreSQL:

s. python:

t. react:

u. sass:

v. matplotlib:

w. svelte:

x. manim:

  1. make svgs for facebook, linkedin, github, kaggle, stackoverflow

a. github:

b. kaggle:

c. stackoverflow:

d. facebook:

e. linkedin:

Writing html, css, and svelte based on website template:

To do:

  1. timeline:

  2. Install tailwindcss via npm install -D tailwindcss in base directory, and create your tailwind.config.js file using npx tailwindcss init which will contain the code below:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}

initially the extend dictionary will be empty so we can populate it with our own styles e.g. 'font-family': ['Nunito] to let tailwind know that there are new utility classes we have added that we can use as value in the class attribute of html elements

  1. create a src/assets/stylesheets/<name that will contain tailwind directives e.g. custom>.css
  2. add directives in this src/assets/stylesheets/<name that will contain tailwind directives e.g. custom>.css file which when compiled to another .css file we will use, will finally contain the utility classes that will allow us to use it in our html elements
@tailwind base;
@tailwind components;
@tailwind utilities;
  1. create this another .css file src/app.css or just delete the contents of the app.css if it already exists in svelte app. This output file will basically contain the compiled src/assets/stylesheets/<name that will contain tailwind directives e.g. custom>.css file.
  2. in order to compile open the package.json file and add the key-value pair "build-css": "tailwindcss build src/assets/stylesheets/custom.css -o src/app.css" under the scripts dictionary along with all other commands that allows us to run and/or build our svelte app. Adding this will allow us to run npm run build-css custom.css whenever we add new directives in our custom.css file
  3. compile using npm run build-css custom.css

Tech used:

  • Svelte.js
  • Tailwind
  • Three.js (TBD)

References:

Side notes:

  1. Mixins in sass:
@mixin <function name>($<parameter name>){
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

main {
    @include flexCenter(row);
    width: 80%;
    margin: 0 auto;
    
    #{&}__paragraph {
        font-weight: weight(bold);

        &:hover { 
            color: pink;
        }
    }
}

Writing navbar

To do: 1.

Problems and solutions:

  1. Maybe I can fix the nav-menu-container being in a fixed position using z-index - resolved

  2. prevent scrolling when navbar modal is open - resolved

  3. for some reason adding sections causes x or horizontal overflow thus having a sidebar appear. However in running svelte app overflow does occur however html element seems to be the same width as navbar so maybe the screen is what is the problem - https://stackoverflow.com/questions/68363478/why-horizontal-scrollbar-appears#:~:text=You%20horizontal%20scrollbar%20is%20caused,with%20a%20vertical%20one)%3B - resolved. Problem was I as transalting an element out of the viewport

  4. yes nav-menu-container is now fixed but problem of getting it behind .navbar still occurs because .navbar is relative - solution could be to just set fixed nav-menu-container behind nav-brand-container. Resolved

  5. since opening and closing of navbar functionality is finished is codepen implement it in svelte since codepen uses pure javascript like the DOM api to interact with the modal

  6. when setting navbar container to sticky yes it works however button does not work anymore when scrolling on other. Fixed by just adding z-index since we know position of element is relative when inactive and fixed when scrolling so we wanted it to be on top of all static elements in the page but still contained within the body

  7. another problem about navbar is that when position is indeed sticky we don't want it to block the sections when we go to a section, we want it to do the ff: a. have black background when position is in white sections like projects and experience b. have maybe visibility and opacity of hidden and 0 if it reaches a threshold of idleness on parts of the page other than its original position c. when mouse or when user swipes down the view port show the navbar

Insights:

  • A stacking context is a group of elements that have a common parent and move up and down the z axis together.

Questions:

Conclusions:

Articles:

Writing form component

To do:

  1. make an api call that fetches country data from this endpoint: https://gist.githubusercontent.com/anubhavshrimal/75f6183458db8c453306f93521e93d37/raw/f77e7598a8503f1f70528ae1cbf9f66755698a16/CountryCodes.json, and then use it for providing the country codes for the form
  2. make form responsive without media query such that when it reaches a certain breakpoint it turns the fields into columns
  3. use the ff. to remove background of form auto-fill:
input:-webkit-autofill,
input:-webkit-autofill:hover, 
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
  border: transparent;
  -webkit-text-fill-color: transparent;
  /* -webkit-box-shadow: 0 0 0px 1000px #000 inset; */
  transition: background-color 5000s ease-in-out 0s;
}
  1. use some kind of min minmax, or clamp to change grid-column-end of email, message, and button without having to use media queries
  2. build functionality of form component with svelte by having form send the message to my email
  3. design css of form with invalid input

Problems and solutions:

  1. current problem is that button takes 50% of the grid area it was assigned to, it also has padding set to .5em 5em and font-size set to clamp(0.5rem, .75vw, .75rem). Solution could be to again think about the font-size on certain screens like 320 x 1600, 640 x 1600, and 1600 x 320, and what max size does it take when it gets to a certain viewport width and what min size does ti take when it gets to a certain viewport width again

Tech used: 1.

References:

Side notes:

  1. About Responses The return value from a view function is automatically converted into a response object for you. If the return value is a string it's converted into a response object with the string as response body, a 200 OK status code and a text/html mimetype. The logic that Flask applies to converting return values into response objects is as follows:

If a response object of the correct type is returned it's directly returned from the view. If it's a string, a response object is created with that data and the default parameters. If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form (response, status, headers) or (response, headers) where at least one item has to be in the tuple. The status value will override the status code and headers can be a list or dictionary of additional header values. If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object.

  1. reason why emailjs api didn't work the first time was because you were not passing privateKey and API Settings to Allow EmailJS API for non-browser applications was not set. If this is enabled according to emailjs "Non-Browser Applications Allow to use EmailJS API on the server side, in mobile apps, etc. If allowed, the API will be public and the whitelist will be ignored."

Writing accordion component

To do:

  1. on expansion of item remove teh project-id or fade it also
  2. fade project-title, and project-description also with link fork and star icons

Problems and solutions:

  1. because you are selecting right away .accordion-item you never give a chance for project-link to be selected or clicked on. Solved using cursor default on accordion item, since I was all along able to click the link to project but because of cursor pointer set to accordion item itself it was very hard to see if I was on top of link element

Tech used: 1.

References: *

Side notes:

  1. All I know is that I would have to use flex-basis to preserve height and width of accordion items in either a landscape or portrait orientation or screen sizes like 320 x 640, 320 x 1600, 640 x 1600
  2. Currently, an element with position: absolute is positioned relative to the nearest positioned ancestor (an element with position other than static) so not only do absolute elements acnhor themselves to relative elements but elements with position other than static

Writing about component

To do:

  1. Need to fix why gif file does not render in document
  2. position video container and video element

References:

Writing skills component

To do:

  1. instead of installing three.js itself we add threlte (svelte integrated with three.js) to our existing svelte project instead
  2. in our project install three and @threlte/core with npm e.g. npm install three @threlte/core
  3. in the vite.config.js and jsconfig.json (or vite.config.ts and tsconfig.json if you're using typescript) we add the following statements to both files vite.congfig.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [svelte()],
  ssr: {
    noExternal: ['three']
  }
})

and in tsconfig.json

{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "target": "ESNext",
    "module": "ESNext",
    /**
     * svelte-preprocess cannot figure out whether you have
     * a value or a type, so tell TypeScript to enforce using
     * `import type` instead of `import` for Types.
     */
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "resolveJsonModule": true,
    /**
     * To have warnings / errors of the Svelte compiler at the
     * correct position, enable source maps by default.
     */
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    /**
     * Typecheck JS in `.svelte` and `.js` files by default.
     * Disable this if you'd like to use dynamic types.
     */
    "checkJs": true
  },
  /**
   * Use global.d.ts instead of compilerOptions.types
   * to avoid limiting type declarations.
   */
  "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}
  1. use clamp in row gap of skills

Problems:

Articles:

  1. for gradients of icons: https://mycolor.space/gradient?ori=to+left&hex=%230056D8&hex2=%23EBEEE5&sub=1
  2. listening to resize events in react: https://www.pluralsight.com/guides/re-render-react-component-on-window-resize
  3. listening to scroll events in svelte and binding a state variable to scrollY property of an element: https://svelte.dev/repl/051cd352ce284d15b55c91c8b30fa32f?version=3.16.7
  4. here's how we can dynamically set the styles of our components css properties: https://svelte.dev/repl/8123d474edb04f198c3b83363716a709?version=4.2.1

Insights:

  1. viewBox is an attribute that is set to value "<min-x> <min-y> <width> <height>". You can think of view box as the scope in which we see the svg object we have
  2. use transform origin to change origin where transformation starts which is by default on the center
  3. like a bind:value in an input in svelte to change the state of our state variable which is just the equivalent of onClick={(event) => setState(event.target.value)} albeit longer, bind:scrollY of a svelte:window fragment will change the state of our state variable binded to this scrollY attribute of a window fragment everytime we scroll down across or page in svelte
  4. likewise we can use an on:resize event on the svelte:window fragment to change the state of our page or in our problem the css of our element everytime we resize the dimensions of our window
  5. we can use style directive to dynamically change an elements css e.g. <div class="carousel-inner" style:min-height={`${max_height}px`} style:width={`${max_width}px`}>
  6. In the carousel slider indces only ramge from 0 to 4.

Adding 0 to -1 would result in -1 which cannot be in this case we set the new index to 4 or the index of last element/item of the carousel, which is generalized to carousel.length.

And then adding 4 to offset of 1 would result in index 5 which is out of the ramge of our carousel items indeces or >= carousel length, in this case we want to loop back to 0 again everytime this is the case. So the new index will always be set to 0 every time this is the case 7. for staggered animations we cant place data attributes that will be assigned to the numbers representing the order of animations, because of its abysmal browser support. We can use however custom properties that are assigned a whole number and not a number with a unit

Writing experience component

To do:

  1. Because you have little experience show instead your github contributions profile
  2. Write the contributions in 3d using three js
  3. Scrape ther contributions using backend server proxy utilizing requests, beautiful soup, json to get, parse, and transform the raw html data respectively to be send back in json format to our client-side

Problems and solutions:

  1. because github skyline api was only until 2022 we would have to setup our own api to scrape and return our github contributions data. But because we cannot use our client site to fetch our contributions from our github profile we will have to set up a backend server proxy to make these requests for us instead of the client. This is because github has and will throw a cross origin resource sharing error due to our client side url being a foreign or being of a foreign origin and not recognized
  2. Make accolade across all timeline items must be of the same height as well as header, however main problem with this is what if content from accolades is now bigger then header would now be bigger too and have too much whitespace

Articles:

Insights:

  1. its better to use animation when you don't want to use a before state and an after state for your components
  2. its better to use transition when same components have different values of the same property of their before state since animation only applies the same property of the same values for the components before state and after state e.g. transform: translateX() of each grid item before states must be different from each other
  3. However we can still be able to stagger animations using scoped variables inside the declaration and definition of an animation
  4. an important thing to note when setting horizontal overflow to scroll is that if the parent container centers the overflowing content itself then the content will be hidden from the left and completely inaccessible. this is why its better to position the child overflowing to absolute instead of setting the parent to for instance display flex and then centering using justify content and align items, you can also use for the parent grid instead of flex box to prevent this 'eating content from the left' problem/behavior
  5. so using and updating the variable binded to the exp_button_container inside the afterUpdate () function is bad practice because it produces an infinite loop, since state of button container is updated through its style therefore afterUpdate is ran again and thus updates the state of the button container yet again. https://stackoverflow.com/questions/63736010/why-is-using-setstate-inside-componentdidupdate-seen-as-a-bad-practice-in-react
  6. It is good practice to instead set the function callback not in the afterUpdate() but precisely n seconds after setting the state by assignment in the handler function using some kind of function that delays the function callback by how many seconds

Backend

Setting up backend proxy server for accessing .env file

Prerequisities to do: 1.

To do:

  1. install flask
  2. set route's url pattern to https://:5000 in order to receive http requests from client requesting this url
  3. setup spam prevention in server to ensure email spamming in contact form is prevented a. ban ip forever if sent more than 20 requests, since individual may be malicious and might just do it again should ban be removed b. however user may send a requset 20times but have interval between those requests which may imply they may not be malicious. So somehow setup a way that ip ban retains same ip address for how many number of seconds before it expires and user can then send another request

Problems and solutions:

  1. http://127.0.0.1:5000 blocked by CORS policy when requesting to api endpoint http://127.0.0.1:5500/ - solution: https://medium.com/@mterrano1/cors-in-a-flask-api-38051388f8cc

Tech used:

  1. flask
  2. flask-cors

References:

  1. flask-ipban: https://pypi.org/project/flask-ipban/

server.py

...
app.secret_key = os.environ.get('SECRET_KEY', str(os.urandom(50)))
ban_count = 5
ban_seconds = 5

ip_ban = IpBan(app=app, ban_count=ban_count, ban_seconds=ban_seconds)
ip_ban.load_nuisances()
...
...
class IpBan:
    """
    Implements a simple list of ip addresses that
    seem to be trying credential stuffing.  Blocks items from that list
    once they exceed the ban count.

    Optional config by env variable
    ======
    IP_BAN_LIST_COUNT - number of observations before 403 exception
    IP_BAN_LIST_SECONDS - number of seconds to retain memory of IP

    """

    VERSION = '1.1.5'

    def __init__(self, app=None, ban_count=20, ban_seconds=3600 * 24, persist=False, record_dir=None, ipc=False,
                 secret_key=None, ip_header=None, abuse_IPDB_config=None):
        """
        start

        :param app: (optional when using init_app) flask application with logger defined
        :param ban_count: (optional) number of observations before ban
        :param ban_seconds: (optional) number minutes of silence before ban rescinded (0 is never rescind)
        :param persist: (optional) persists the ban records between app starts by storing in a temp folder
        :param record_dir: (optional default is flask-ip-ban in the temp folder) a record directory that stores ban records for ipc sync and persistence.
        :param ipc: enable ipc communication
        :param secret_key: optional secret key for signing ipc records.  Default is to use flask secret key
        :param ip_header: name of request header that contains the ip for use behind proxies when in docker/kube hosted env
        :param abuse_IPDB_config: config {key=,report=False,load=False} to a AbuseIPDB.com account.  Blocked ip addresses via url nuisance matching will be reported.
        """

        HERE IF THERE IS NO IP_BAN_LIST_COUNT SUPPLIED IN OUR ENVIRONMENT VARIABLES THEN BAN_COUNT 
        PASSED IN CLASS IS INSTEAD USED SAME GOES FOR IP_BAN_LIST_SECONDS
        self.ban_count = int(os.environ.get('IP_BAN_LIST_COUNT', ban_count))  # type: int
        self.ban_seconds = int(os.environ.get('IP_BAN_LIST_SECONDS', ban_seconds))  # type: int

        self._ip_whitelist = {'127.0.0.1': True}
        # self._ip_whitelist = {}
        self._ip_ban_list = {}
        self._cidr_entries = {}
        # initialise with well known search bot links
        self._url_whitelist_patterns = {}
        self._url_blocklist_patterns = {}
        self.load_allowed()
        self.app = None
        self._logger = None
        self.abuse_reporter = None
        self.ip_header = ip_header
        self.abuse_IPDB_config = abuse_IPDB_config or {}
        self.init = True

        self.ip_record = IpRecord(self, record_dir, persist, ipc, secret_key)

        if app:
            self.init_app(app)
...
...
# bans any ip address if request exceeds 20
ip_ban = IpBan(app, ban_count=20)
ip_ban.load_nuisances()
ip_ban.url_pattern_add(r'/', match_type='regex')
# use this for users who spam your site and pass to .blo9ck method of ip_ban
# request.remote_addr
...

Side notes:

  1. to run flask server use python server.py or flask --app server run

Deployment to Render

To do:

  1. activate conda environment and install gunicorn through pip install gunicorn for this will allow our local application to be deployed in a production server instead of a local server like our local machine
  2. create requirements.txt inside the server-side directory by pip list --format=freeze > requirements.txt

Side notes:

  1. gunicorn app:<name of flask file to be run by gunicorn e.g. server (note without the .py extension)> is the start command we will use to start our server
  2. specify python version used when isntalling the python packages in the environment variables settings of render e.g. PYTHON_VERSION=3.11.4

About

A portfolio website of all my projects.

https://project-alexander.vercel.app


Languages

Language:HTML 66.4%Language:Svelte 23.4%Language:CSS 8.7%Language:Python 1.2%Language:JavaScript 0.4%