Tag: portfolio

  • Sveltekit Auth and Crud

    As an older developer that is used to the LAMP stack getting used to the new Javascript Frameworks was a bit challenging. One thing that I had found difficult was finding documentation on building out Auth and Crud functionality for the Sveltekit Framework, which is the latest fast kid on the block that is smashing it as far as page load goes compared to more established JS Frameworks.

    (source: https://www.softermii.com/blog/why-sveltejs-is-the-most-in-demand-framework-for-web-development )

    Do to sveltekit being a new kid on the block there aren’t many established code examples or tutorials on how to do such simple things as CRUD and Auth. I found a nice Auth code base on github.com but it didn’t have a walk through of the code base or any CRUD, so the following article is on how to add CRUD to this very nice Auth open source codebase using sveltekit.

    To begin you will need to have some things set up on your development computer, such as Postgresql for the database. Here are the following steps to get things set up.

    How to extend Sveltekit-Auth

    1. download the code base from https://github.com/delay/sveltekit-auth
    2. install postgres on your system if you don’t have it yet, for osx

              

    > brew install postgresql
    
    > brew services start postgresql@14 or psql postgres

      for windows see https://www.postgresql.org/download/windows/

    3. In the postgres cli:

     CREATE USER your_new_username WITH PASSWORD 'your_password';

      4. CREATE DATABASE auth_demo;

      5. add permissions to the database for your postgres username:

    GRANT ALL PRIVILEGES ON DATABASE auth_demo TO auth_user;

      6. open a new cl window or terminal and go to the directory you downloaded sveltekit-auth

    >cd to svelte-kit-master dir

       7. get the db ready, run the migration

               > npx drizzle-kit generate:pg

       8.  push to the db

                 > npx drizzle-kit push:pg


    If you need to make changes to the schema, you can do so and then run the generate and push commands again to update the database.

      9. in the root of the codebase directory copy sample.env to .env file

      10. update settings in .env file

    # rename to .env and put your values in
    # General
    # I used postgresql for the PRISMA_URL for this project 
    # but you should be able to use any DB prisma supports.
    # https://www.prisma.io/docs/reference/database-reference/supported-databases
    DATABASE_URL="postgresql://postgres_user:user_password@localhost:5432/db_name"
    
    # Good video on how to set up OAuth with Google https://www.youtube.com/watch?v=KfezTtt2GsA
    GOOGLE_CLIENT_ID=
    GOOGLE_CLIENT_SECRET=
    
    # Email
    FROM_EMAIL = 'first last <user@domain.com>'
    # use blank values in AWS variables if you want to use SMTP
    #AWS SES KEYS
    AWS_ACCESS_KEY_ID= ''
    AWS_SECRET_ACCESS_KEY= ''
    AWS_REGION= '' # us-east-1
    AWS_API_VERSION= '' # 2010-12-01
    # if AWS SES not set the SMTP will be a fallback
    SMTP_HOST=localhost
    SMTP_PORT=1025
    SMTP_SECURE=0 # use 1 for secure
    SMTP_USER=somethinghere
    SMTP_PASS=somepassword
    
    # Logging
    # Clear these to fallback to console.log
    AXIOM_TOKEN=your-axiom-token
    AXIOM_ORG_ID=your-axiom-org-id
    AXIOM_DATASET = your-axiom-dataset
    

      11. sign up at axiom, npm install @axiomhq/js, create a dataset at axiom, and an api token then add to .env file variables for axiom. see https://jeffmcmorris.medium.com/awesome-logging-in-sveltekit-6afa29c5892c

      12. install svelte app

    > npm install

      13. start the sveltekit server

     > npm run dev

    you should now see a user interface when you open http://localhost:5173 in my case with branding added for my project it looks like this:

    Extending the Codebase and adding CRUD Functionality

    now that the app is up and running we can start to add functionality that allows one to add db tables and add, edit and delete records to these new tables, essential to any application.

    As an example I am going to use creating a settings module for my project. One may want to review the ORM used for db related management, in this case we are using drizzle, https://orm.drizzle.team/docs/get-started-postgresql

    there is a drizzle.config.ts file that defines where drizzle schemas are kept. The schema defines the table for the ORM.

    see /lib/server/database/drizzle.schemas.ts

    add a settings schema to the existing schemas already in that file for user and sessions, in this case one for settings:

    export const settingsTable = pgTable('settings', {
    
        id: text('id').notNull().primaryKey(),
    
        user_id: text('user_id')
    
            .notNull()
    
            .references(() => userTable.id),
    
        key: text('key').notNull(),
    
        secret: text('secret').notNull(),
    
        createdAt: timestamp('created_at', {
    
                withTimezone: true,
    
                mode: 'date'
    
            }).notNull(),
    
        updatedAt: timestamp('updated_at', {
    
                withTimezone: true,
    
                mode: 'date'
    
            }).notNull()
    
    });

    we are going to be working primarily with the key/secret pair which is used for any standard api calls, in this case an api for getting stock data.

    then add a type for settings in the same file and an update type:

    export type Settings = typeof settingsTable.$inferInsert;
    
    export type UpdateSettings = Partial<typeof settingsTable.$inferInsert>;

    then you need to generate and migrate the changes:

    > npx drizzle-kit generate:pg

    after you generate there will be a new migration file that is numbered sequentially in ascending order in /lib/server/database/0000_snapshot.json, 0001_snapshot.json

    then  push changes using:

    > npx drizzle-kit push:pg

    in the cl or terminal window you should see this: 

    > auth % npx drizzle-kit generate:pg

    drizzle-kit: v0.20.14

    drizzle-orm: v0.29.3

    No config path provided, using default ‘drizzle.config.ts’

    Reading config file ‘/Users/michaelmccarron/Desktop/dev/auth/drizzle.config.ts’

    3 tables

    sessions 3 columns 0 indexes 1 fks

    settings 6 columns 0 indexes 1 fks

    users 13 columns 0 indexes 0 fks

    [✓] Your SQL migration file ➜ src/lib/server/database/migrations/0001_gigantic_quasimodo.sql 🚀

    > auth % npx drizzle-kit push:pg

    drizzle-kit: v0.20.14

    drizzle-orm: v0.29.3

    No config path provided, using default path

    Reading config file ‘/Users/michaelmccarron/Desktop/dev/auth/drizzle.config.ts’

    [✓] Changes applied

    before you run this if you went to the postgres cl and did a list tables you see this, 

    auth_demo=# \dt

     public | sessions | table | auth_user

     public | users    | table | auth_user

    then after you do generate and push you see

    auth_demo=# \dt

     public | sessions | table | auth_user

     public | settings | table | auth_user

     public | users    | table | auth_user

    also note that you have a new sql file generated with the additional table information in /src/lib/server/database/migrations/0001_gigantic_quasimodo.sql

    CRUD functions are handled in src/lib/server/database/settings-model.ts, this is where you add the functionality for the database model.

    export const updateSetting = async (id: string, settings: UpdateSettings) => {
    
        const result = await db.update(settingsTable).set(settings).where(eq(settingsTable.id, id)).returning();
    
        if (result.length === 0) {
    
            return null;
    
        } else {
    
            return result[0];
    
        }
    
    };
    
    export const createSetting = async (settings: Settings) => {
    
        console.log("settings in createSetting of settings-model.ts");
    
        console.log(settings);
    
        const result = await db.insert(settingsTable).values(settings).onConflictDoNothing().returning();
    
        if (result.length === 0) {
    
            return null;
    
        } else {
    
            return result[0];
    
        }
    
    };

    User Interface for Settings

    settings is manipulated in the UI at localhost:port/profile/settings/

    you can download the files for the user interface at:

    https://feirmeoirsonrai.me/settings.zip

    this should be copied into the /src/routes/(protected)/profile directory so we get /src/routes/(protected)/profile/settings/…

    in this example we are going to add a typical key and api string for any api type saas setting. 

    the eventual directory structure will be like this:

    src

    — routes

       — (protected) 

          — profile

              +page.svelte

              +page.server.ts

    — settings

    +page.svelte

    +page.server.ts

              — editor

                 +page.svelte

                 +page.server.ts

              — lister

                 +page.svele

                 +page.server.ts

    you place files you do not want to be exposed to the user without a valid session id and their session id matching their user.session information, i.e. they own the content in the interface. these parts of your UI are placed in the (protected) directory. 

    step one is to create a profile directory under (protected) 

    past settings dir into (protected)/profile/

    create file /lib/config/zod-schemas-settings.js

    paste into file: 

    mport { z } from 'zod';
    
    export const settingsSchema = z.object({
        id: z.string().optional(),
        user_id: z.string().optional(),
        key: z
            .string({ required_error: 'key is required' })
            .min(1, { message: 'key is required' })
            .trim(),
        secret: z
            .string({ required_error: 'secret is required' })
            .min(1, { message: 'secret is required' })
            .trim(),
        createdAt: z.date().optional(),
        updatedAt: z.date().optional()
    });
    
    export type SettingsSchema = typeof settingsSchema;
    

    after registering as a user update db since not setting up email protocol yet: 

     UPDATE users SET verified = ‘t’ where email = ‘reg_user@domain.com’;

    restart server after updating

    npm run dev

    When we go to the dashboard section of the site:

    work with two files dashboard/+page.server.ts which initializes things for the view.

    important files to require are:

    import { db } from '$lib/server/db';
    import { settingsTable } from '$lib/server/database/drizzle-schemas';

    these files are used for accessing the database. because in this view we are performing a data check to see if there is a record for the user we need the drizzle-schemas. db  is the equivalent of the DB object in older systems based on PHP, or other backend code bases. 

    to perform a query run:

    const results = await db.select().from(settingsTable).where(eq(settingsTable.user_id, user_id));


    +page.svelte is the User interface file. One important require file is that of a zod schema, which is used for data validattion in the form that is included in the file. 

      import { settingsSchema } from '$lib/config/zod-schemas-settings';
    
      const deleteSettingsSchema = settingsSchema.pick({
        id: true,
        key: true,
        secret: true
      });

    section: working with editing data and updating the db

    if you don’t have any keys then the dashboard provides a link to add a key, this brings up profile/settings/

    in +page.server.ts make sure you have any db and db.schema requirements met. in this case since not reading any data or accessing the db, they are not. 

    in +page.svelte we then include the db schema for writing and the validation zod schemas:

        import * as Form from '$lib/components/ui/form';
        import { settingsSchema } from '$lib/config/zod-schemas-settings';
        import type { SuperValidated } from 'sveltekit-superforms';


    once the reqs are loaded then in the ui we see a form with this code:

    
    <div class="flex items-center justify-center mx-auto max-w-2xl">
        <Form.Root let:submitting let:errors method="POST" {form} schema={createSettingsSchema} let:config>
            <Card.Root>
                <Card.Header class="space-y-1">
                    <Card.Title class="text-2xl">Create Alpaca Trading Keys</Card.Title>
                    <Card.Description
                        >You need a API key/secret from https://alpaca.markets</Card.Description>
                </Card.Header>
                <Card.Content class="grid gap-4">
                    {#if errors?._errors?.length}
                        <Alert.Root variant="destructive">
                            <AlertCircle class="h-4 w-4" />
                            <Alert.Title>Error</Alert.Title>
                            <Alert.Description>
                                {#each errors._errors as error}
                                    {error}
                                {/each}
                            </Alert.Description>
                        </Alert.Root>
                    {/if}
                    <Form.Field {config} name="key">
                        <Form.Item>
                            <Form.Label>Alpaca Key</Form.Label>
                            <Form.Input />
                            <Form.Validation />
                        </Form.Item>
                    </Form.Field>
                    <Form.Field {config} name="secret">
                        <Form.Item>
                            <Form.Label>Alpaca Secret</Form.Label>
                            <Form.Input />
                            <Form.Validation />
                        </Form.Item>
                    </Form.Field>
    
                </Card.Content>
                <Card.Footer>
                    <Form.Button class="w-full" disabled={submitting}
                        >{#if submitting}
                            <Loader2 class="mr-2 h-4 w-4 animate-spin" />
                            Please wait{:else}Update Keys{/if}
                    </Form.Button>
                </Card.Footer>
            </Card.Root>
        </Form.Root>
    </div>
    


    zod tutorial video: https://www.youtube.com/watch?v=L6BE-U3oy80

    once the submit button is pushed after the data is validated using zod schema then the data is inserted into the database as long as the settings schema is defined correctly. 

    Once we have a setting submitted we can then list the settings.

    Listing the Settings and Editing

    in dir /profile/settings/lister

    we do the usual requirements with the addition of one important new piece of code that handles form actions, such as update, or delete. Here we are only concerned with deleting so we add this to the file +server.page.ts:

    export const actions = {
        
    
        delete: async ({request}) => {
            const formData = await request.formData();
            const rec_id = formData.get('id');
            console.log("rec id "+rec_id);
            
    
            console.log('deleting key');
    
            const deletedSetting = await deleteSetting(rec_id);
            console.log("deletedSetting "+deletedSetting);
            if (deletedSetting) {
                console.log("deleted succeessful");
                setFlash(
                    {
                        type: 'success',
                        message: 'keys deleted.'
                    },
                    request
                );
                redirect(302, '/dashboard');
                //message(request, { text: 'deleted!' });
            }
                    
        }
        
    };
    

    this is called from +page.svelte

    we need to add some new code to the file:

    const deleteSettingsSchema = settingsSchema.pick({
        id: true,
        key: true,
        secret: true
      });
    
      type DeleteSettingsSchema = typeof deleteSettingsSchema;
    
      export let form: SuperValidated<DeleteSettingsSchema>;
    


    then in the user interface we can iterate through the settings record rows, although here we are limited to only one:

    <ul>
      {#each data.json_results as alpaca}
        <li>{alpaca.key} - {alpaca.secret} <a href="/profile/settings/editor/{alpaca.id}">[edit]</a>
    
        <form method="POST"  onsubmit="return confirm('sure you want to delete this key?');" action="?/delete">
          <input type="hidden" id="id" name="id" value="{alpaca.id}" />
            <button>Delete</button>
          
          </form>
          
        </li>
      --------- <br />
      {/each}
    </ul>
    
    

    Editing the Settings

    Next we move onto editing. If one were to see in the previous code that there is a link to editing.  This is where we use a new directory [id] which is at /profile/settings/editor/[id]

    see https://svelte.dev/docs/kit/advanced-routing for more information on how this works but basically we are passing in a dynamic ‘id’ variable to display a user interface based on the dynamic argument.  

    for editing we are working with the files in the /profile/settings/editor/[id]/

    in page.server.ts we need to make sure we require the schemas for settings update and other functionality: 

    import { editSetting } from '$lib/server/database/settings-model';
    import { updateSetting } from '$lib/server/database/settings-model';
    import { settingsTable } from '$lib/server/database/drizzle-schemas';


    and again we need to add the actions for editing:

    export const actions = {
        default: async (event) => {
            const form = await superValidate(event, keySchema);
    
    
            if (!form.valid) {
                return fail(400, {
                    form
                });
            }
    
            //add user to db
            try {
                console.log('updating profile');
                const user = event.locals.user;
                if (user) {
                    await updateSetting(event.params.id, {
                        key: form.data.key,
                        secret: form.data.secret
                    });
                    setFlash({ type: 'success', message: 'Keys update successful.' }, event);
                }
    
    
            } catch (e) {
                console.error(e);
                return setError(form, 'There was a problem updating your trading keys.');
            }
            console.log('keys updated successfully');
            return message(form, 'keys updated successfully.');
        }
    };
    
    

    in +page.svelte we make sure we have schema for validation:

    import { settingsSchema } from '$lib/config/zod-schemas-settings';
    
    import type { SuperValidated } from 'sveltekit-superforms';

    and we need:

    const keySchema = settingsSchema.pick({
            key: true,
            secret: true,
        
        });
    
        
    
        type keySchema = typeof keySchema;
    
        export let form: SuperValidated<keySchema>;
        form = data.form;


    and then we have the form itself:

    <div class="flex items-center justify-center mx-auto max-w-2xl">
        <Form.Root let:submitting let:errors method="POST" {form} schema={keySchema} let:config>
            <Card.Root>
                <Card.Header class="space-y-1">
                    <Card.Title class="text-2xl">Edit Alpaca Trading Keys</Card.Title>
                    <Card.Description
                        >update your Alpaca Keys</Card.Description>
                </Card.Header>
                <Card.Content class="grid gap-4">
                    {#if errors?._errors?.length}
                        <Alert.Root variant="destructive">
                            <AlertCircle class="h-4 w-4" />
                            <Alert.Title>Error</Alert.Title>
                            <Alert.Description>
                                {#each errors._errors as error}
                                    {error}
                                {/each}
                            </Alert.Description>
                        </Alert.Root>
                    {/if}
                    <Form.Field {config} name="key">
                        <Form.Item>
                            <Form.Label>Alpaca Key</Form.Label>
                            <Form.Input />
                            <Form.Validation />
                        </Form.Item>
                    </Form.Field>
                    <Form.Field {config} name="secret">
                        <Form.Item>
                            <Form.Label>Alpaca Secret</Form.Label>
                            <Form.Input />
                            <Form.Validation />
                        </Form.Item>
                    </Form.Field>
    
                </Card.Content>
                <Card.Footer>
                    <Form.Button class="w-full" disabled={submitting}
                        >{#if submitting}
                            <Loader2 class="mr-2 h-4 w-4 animate-spin" />
                            Please wait{:else}Update Key/Secret{/if}
                    </Form.Button>
                </Card.Footer>
            </Card.Root>
        </Form.Root>
    </div>
    

    the values for the fields, is from form.data object. note that the name of the form field matches the value of the form.data.field_name

    for example:

    form.data.key should be referenced <Form.Field {config} name=”key”></Form.Field>

    And that is how one adds CRUD to sveltekit code base for this project: sveltekit-auth, as seen adapted for my project Céillí.

  • Building and Coding a Medical Device with C++ and Arduino Controller

    A personal confession up front, I am an highly functioning person with Autistic Spectrum Disorder, otherwise known as Asperger’s Syndrome. Science refers to us as being Neurodivergent which the Oxford Languages Dictionary defines as “differing in mental or neurological function from what is considered typical or normal (frequently used with reference to autistic spectrum disorders)”. Part of my neurological differences is that I am extra sensitive to Electro-Magnetic Fields (EMF), which helped me in my duties as a Signals Intelligence technical analyst on USN Submarines but not so much in everyday life. To provide relief from this extra sensitivity I researched different ways to relieve this ‘pain’. This article is about the creation of my medical device and how I built it using Arduino microcontroller and C++ to regulate the circuit power for the device.

    I will not be covering the science behind the medical device, this is covered in Ch. 9 of my book Battlespace of Mind: AI, Cybernetics and Information Warfare [link] [pdf]

    A brief overview of Arduino microcontrollers is related by Wikipedia:

    Arduino (/ɑːrˈdwiːnoʊ/) is an Italian open-source hardware and software company, project, and user community that designs and manufactures single-board microcontrollers and microcontroller kits for building digital devices. Its hardware products are licensed under a CC BY-SA license, while the software is licensed under the GNU Lesser General Public License (LGPL) or the GNU General Public License (GPL),[1] permitting the manufacture of Arduino boards and software distribution by anyone. Arduino boards are available commercially from the official website or through authorized distributors.[2]

    Arduino board designs use a variety of microprocessors and controllers. The boards are equipped with sets of digital and analog input/output (I/O) pins that may be interfaced to various expansion boards (‘shields’) or breadboards (for prototyping) and other circuits. The boards feature serial communications interfaces, including Universal Serial Bus (USB) on some models, which are also used for loading programs. The microcontrollers can be programmed using the C and C++ programming languages (Embedded C), using a standard API which is also known as the Arduino Programming Language, inspired by the Processing language and used with a modified version of the Processing IDE. In addition to using traditional compiler toolchains, the Arduino project provides an integrated development environment (IDE) and a command line tool developed in Go.

    (source: https://en.wikipedia.org/wiki/Arduino )

    In my device I used an Arduino Uno card which attaches to a bread board that has a simple electronic circuit for regulating electric current to a coil, the coil itself is considered a form of Transcranial Magnetic Stimulation, except in this case it is referred to as Transcerebral Magnetic Stimulation. First, I will cover the wiring of the bread board electric circuit then go over the code file which is used to program the Uno card.

    To begin we will need the following parts list:

    Parts list:

    • Arduino Uno Board, plus necessary connectors (purchase a starter kit online)
    • Arduino Breadboard
    • 1K Ohm Resistor
    • TIP120 to 220 Voltage Regulator
    • Diode 1N4007
    • Red LED (620nm Wavelength)

    Make the Circuit:

    see the breadboard completed image below and make sure your breadboard is oriented the proper way before adding electronic components to the board.

    1. connect LED to pin a21 and a22
    2. on the Arduino Uno out pin 9 to pin b21 infront right leg of LED on bread board. Connect gound pin 9 to Breadboard – negative base.
    3. Take 1K Ohm Resistor and connect to pin b21 in front of LED short leg, connect resistor out to pin f20 in front of 120 TIP V regulator.
    4. Voltage regulator, facing properly the plastic should be facing you with the metal end farthest away from you, metal hole mount to rear, place in pin g20,g19,g18
    5. Ground the regulator, grab a jumper cable short or med red one or other color as you wish. one end connect to negative rail the other end connect to right pin of voltage regulator place in j18
    6. Coil In, pin h19 in front of center of V regulator
    7. Diode, (grey strip is out) grey end into + positive pin 20 of breadboard base. In goes to pin j19 behind out to coil. (see video)
    8. Coil out, to + positive on board base rail pin 21, should be next to diode, to the left of the diode one pin location.

    Then your finished product should look like this:

    Program the Uno Card:

    the next step is to add the C++ logic to the card so.

    1. Connect your card via usb to your computer
    2. Open the Arduino IDE, download this at https://www.arduino.cc/en/software
    3. get a copy of the arduino code file from my github repo: https://github.com/autonomous019/ahronov-bohm-cybersecurity/blob/main/accelerate_frequency_driver.ino
    4. then upload it to the board, you can search the internet for plenty of videos on how to upload a sketch file to your card. Be sure to select the proper board and serial port for your board in the IDE.
    5. connect the power pin on the card to your bread board, use a jumper cable to connect pin9 on the card to in front of the led pin that is the in to the led, this is the long leg of the led.
    6. connect the card ground (“GRD”) to your bread board negative rail (-).

    if you were succesfull you should see a flashing red led light on the bread board.

    Some things to note about the sketch file, which is written in C++:

    int ledPin = 9;
    int sensorValue;     // variable to stores data
    const int adc = 0 ;   //naming pin 0 of analog input side as ‘adc’
    
    void setup() {
      
      pinMode(9, OUTPUT);
      Serial.begin(9600);  //init serial comm
      
    }

    this bit of code initializes pin 9 as the power source to the bread board.

    //add as many 'pins' or 'counters' to add random config to em waves for security, prevents others from hijacking your shield
    int random_key_1 = random_delay_time(6,30);
    int random_key_2 = random_delay_time(6,30);
    int random_key_3 = random_delay_time(6,30);
    int random_key_4 = random_delay_time(6,30);

    this code block sets a small security ‘salt’ to create a random time delay in the looping of the circuit power.

    The circuit uses a descending time delay from 24ms down to 6ms.

    int phaser_a = phase_former(24,3);
    int phaser_b = phase_former(22,3);
    int phaser_c = phase_former(20,3);
    int phaser_d = phase_former(18,3);
    int phaser_e = phase_former(16,3);
    int phaser_f = phase_former(14,3);
    int phaser_g = phase_former(12,3);
    int phaser_h = phase_former(10,3);
    int phaser_i = phase_former(8,3);
    int phaser_k = phase_former(6,3);
    
    
    }// ends loop()
    
    
    int phase_former(int delay_time, int point_duration) {
      int v = 0;
      digitalWrite(9,HIGH);
      delay(point_duration);
      digitalWrite(9,LOW);
      delay(delay_time);
      return v; 
    }

    we set up specific phases for the circuit then call those phases in phase_former() func.

    If you want to learn how to make the coil and want more information on the overall project see the github repo at https://github.com/autonomous019/ahronov-bohm-cybersecurity

  • Coding a Dynamic Datadriven Front End Application using only Javascript and HTML5

    In the following I will be going over the CSS, HTML5 and Javascript used to code my cute little app that ranks American Football player Offensive Lineman according to data provided by the NFL in one of their Kaggle Challenges. This app is called “O-Line” which you can see at https://feirmeoirsonrai.me/oline/

    And if you want to geek out you can read my paper on the rankings system at Defending the Edge: Evaluating OT Performance through Euclidean Measurements 

    To get a picture of the different components involved in the User Interface for this web app we can take a quick look at the code of the <head> element in the index page HTML:

        <head>
            <meta charset="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
            <meta name="description" content="american football data analytics" />
            <meta name="author" content="michael mccarron macciarain@protonmail.com" />
    
    
            <meta http-equiv="cache-control" content="max-age=0" />
            <meta http-equiv="cache-control" content="no-cache" />
            <meta http-equiv="expires" content="0" />
            <meta http-equiv="pragma" content="no-cache" />
    
            <title>0-Line: NFL Analytics</title>
            
            <!-- JS JS JS JS JS-->
            <script src="./js/jquery-3.6.3.min.js"></script>
            <script src="./js/bootstrap.min.js"></script>
            <!-- Plotly.js -->
            <script src="./js/plotly-latest.min.js"></script> <!-- plotly vers plotly.js v1.58.5-->
            <script src="./js/d3.v7.min.js"></script>
    
            <!-- Favicon-->
            <link rel="icon" type="image/x-icon" href="assets/img/logos/o.ico" />
            <!-- Font Awesome icons (free version)-->
            <script src="fontawesome/js/all.js" crossorigin="anonymous"></script>
    
    
    
            <!-- CSS CSS CSS -->
            <!-- Google fonts-->
            <!-- Bootstrap version: Bootstrap v5.1.3 (https://getbootstrap.com/) -->
            <link href="css/montserrat.css" rel="stylesheet" type="text/css" />
            <link href="css/roboto.css" rel="stylesheet" type="text/css" />
            <!-- Core theme CSS (includes Bootstrap)-->
            <link rel="stylesheet" href="./css/bootstrap.min.css">
            <link href="css/styles.css" rel="stylesheet" />
            <link href="fontawesome/css/all.css" rel="stylesheet" />
            
        </head>

    Let’s first note the Javascript libraries used in this project, they are Bootstrap, jQuery, Plotly and D3. We shall go over these libraries momentarily. Next we shall notice the CSS libraries used such as Bootstrap again, Fontawesome and the custom css for this project in css/styles.css.

    Bootstrap

    Bootstrap (formerly Twitter Bootstrap) is a free and open-source CSS framework directed at responsive, mobile-first front-end web development. It contains HTMLCSS and (optionally) JavaScript-based design templates for typographyformsbuttonsnavigation, and other interface components.

    As of May 2023, Bootstrap is the 17th most starred project (4th most starred library) on GitHub, with over 164,000 stars. According to W3Techs, Bootstrap is used by 19.2% of all websites.

    https://en.wikipedia.org/wiki/Bootstrap_(front-end_framework)

    The thing I like about Bootstrap is it’s popularity, which means it’s tried and trusted by many front-end developers, as well there are plenty of tutorials on all aspects of Bootstrap. It does have some advanced features such as Sass and CSS variables.

    One can get started with Sass at the W3Schools site, something I have used often in my coding life, they relate:

    • Sass stands for Syntactically Awesome Stylesheet
    • Sass is an extension to CSS
    • Sass is a CSS pre-processor
    • Sass is completely compatible with all versions of CSS
    • Sass reduces repetition of CSS and therefore saves time
    • Sass was designed by Hampton Catlin and developed by Natalie Weizenbaum in 2006
    • Sass is free to download and use

    CSS variables can be seen in the following quick example:

    <html>
    <head>
        <style>
            :root {
                --main-bg-color: lightblue;
                --main-text-color: darkblue;
            }
            body {
                background-color: var(--main-bg-color);
                color: var(--main-text-color);
            }
        </style>
    </head>
    <body>
        <h1>Welcome to My Website</h1>
        <p>This is a sample paragraph demonstrating CSS variables.</p>
    </body>
    </html>
    

    we call the variable through the syntax: var(), then define the values of the function in the css pseudo class :root{}. A css pseudo class is:

    A pseudo-class is used to define a special state of an element.

    For example, it can be used to:

    • Style an element when a user moves the mouse over it
    • Style visited and unvisited links differently
    • Style an element when it gets focus
    • Style valid/invalid/required/optional form elements

    Many of these things used to be accomplished by using the old jQuery library that we older developers are more used to but if your starting out read the previous link. 🙂

    In my css files for example I use the :root pseudo class in the following way:

    :root {
      --bs-blue: #0d6efd;
      --bs-indigo: #6610f2;
      --bs-purple: #6f42c1;
      --bs-pink: #d63384;
      --bs-red: #dc3545;
    ...
      --bs-body-bg: #fff;
    }
    
    <!-- file: css/styles.css -->
    <!-- note the use of '...' just is filler for missing code -->

    then for instance in the styles.css file later on when defining attributes of the <body> element we use the variable call:

    body {
      ...
    
      background-color: var(--bs-body-bg);
      ...
    }

    This makes writing the css alot easier and more succinct, taking less time once you are used to the syntax and overall operations of bootstrap. Back in the day when the WWW was in it’s infancy, when I got started, things like this would keep one busy for hours on end as each element would need to be individually coded up instead of using classes and variables.

    Fontawesome is a convenient library to deal with icons, you can read more about it at https://fontawesome.com/

    The main javascript libraries used in this comprised of Bootstrap, which is used for such common UI elements like the carousel, styling forms and buttons etc programmatically without having to create one’s own functions to achieve those things. The two main snazzy libraries used for some uncommon UI elements such as plots and animations is that of Plotly and d3.

    I use d3 version 7 in this project, a brief overview of d3.js:

    D3.js (also known as D3, short for Data-Driven Documents) is a JavaScript library for producing dynamic, interactive data visualizations in web browsers. It makes use of Scalable Vector Graphics (SVG), HTML5, and Cascading Style Sheets (CSS) standards. It is the successor to the earlier Protovis framework. Its development was noted in 2011, as version 2.0.0 was released in August 2011. With the release of version 4.0.0 in June 2016, D3 was changed from a single library into a collection of smaller, modular libraries that can be used independently.

    The D3.js library uses pre-built functions to select elements, create SVG objects, style them, or add transitions, dynamic effects, or tooltips. These objects can also be styled using CSS. Large datasets can be bound to SVG objects using D3.js functions to generate text/graphic charts and diagrams. The data can be in various formats such as JSONcomma-separated values (CSV) or geoJSON, but, if required, JavaScript functions can be written to read other data formats.

    (source: https://en.wikipedia.org/wiki/D3.js )

    Later, I shall go into the technical details of implementing a data-driven web app relying only on Javascript with no server-side backend, such as Ruby, Python, PHP, ASP or Java, using JS and CVS only.

    Plotly is a javascript library that provides a UI for web based data visualizations, many Python based Data Science developers are familiar with Plotly in Python, in this case it is for HTML. Built on top of d3.js and stack.gl, Plotly.js is a high-level, declarative charting library. plotly.js ships with over 40 chart types, including 3D charts, statistical graphs, and SVG maps. plotly.js is free and open source and you can view the source, report issues or contribute on GitHub.

    Now that we have an overview of the CSS and JS involved in this web app, we can start to take a deeper dive into the code. As previously stated this web application is one that is based solely in HTML5, CSS and Javascript, though not a single-page-application, which is to me is an overstated desire that often is not functional except for simple sites, it is a app that has no back-end. For those new to the field in the good old days most dynamic sites used such back-end programming languages like Ruby, PHP, Java, etc to serve up dynaic content. This project however does not rely on any of those ‘back-end’ languages but relies on the ‘front-end’ Javascript Language, which is a derivative of ECMAScript, the latest version being ECMAScript 2023 or 14, it should not be confused with the programming language Java, a seperate ball of wax. This project was based in version 6. This project also does not deal with typescript, although in another project I shall be going over TypeScript, which is a strongly typed programming language that builds on JavaScript, for programmers the basic additive of types to JS, which is normally the domain of languages like PHP, Java, etc.

    Now with all that nitty gritty out of the way, we can get to the bone of this cut of meat. The first thing I would like to address is how I dealth with replacing standard url calls for dynamic pages, where ?something=value&anothersomething=anothervalue is encountered which is done using ‘back-end’ languages. I used hashes to seperate my url calls so that the for instance the url call:

    https://feirmeoirsonrai.me/oline/play.html#42476_gameId:2021092603

    has the agrument from the right of the hash tag (‘#’) in the url of the players_id and the the gameid. I created a simple Javascript parser to deal with parsing the hashtag.

    <script>
    
                var hash = $(location).prop('hash').substr(1);
                let uri_args = words = hash.split(':');
                console.log(uri_args);
                player_id = uri_args[0];
                player_id = player_id.split('_');
                player_id = player_id[0];
                game_id = uri_args[1];
                play_id = uri_args[2];
                console.log("game id: "+game_id);
                console.log("play_id: "+play_id);
                console.log("player id: "+player_id);
                                     
    </script>
    

    the variable hash retrieves the url.location ‘object’ using jQuery, a bit outdated method but one I am used to, and then using the split method based on the colon symbol ‘:’ divides the string into two parts, the first part uri_args[0] gives us the player_id which is ‘42476’ and the second part gives us the game_id which is 2021092603

    This then serves up the 18 plays for the game of the player:

    For serving up data, I have parsed all my data into csv file format and json format. For instance, the data for https://feirmeoirsonrai.me/oline/team.html#ARI

    is dynamically served up from the JSON file:

    {"2021091207":2.8938053097,"2021091909":2.9089285714,"2021092603":2.9267241379,"2021100309":2.8297546012,"2021101011":2.850877193,"2021101708":2.869140625,"2021102408":2.8168103448,"2021102800":2.8934782609}

    here we have a simple 2 level object notation for each game by game_id and the overall rating of the Offensive line for that game ie. 2.8938053097. Which is then put into the UI for presentation.

    you can learn more about JSON at https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/JSON

    The nuts and bolts of presenting this in the UI is handled by Javascript through the following code:

    team_data = [];
    
        $(document).ready(function(){
           
            p_id = player_id;
           
            $.getJSON("./js/json/teams/"+team+"/summary.json", function(data){
                cnt = 0;
                details = document.getElementById( "games" )
                team_name = document.getElementById( "team_name");
                $(team_name).html(team);
                plays_text = "";
    
                for (const key in data) {
                    if (data.hasOwnProperty(key)) {
                      console.log(`${key}: ${data[key]}`);
                      let p_data = `${key}: ${data[key]}`;
                      t_qbi = data[key];
                      team_data.push(t_qbi);
                      game_id = key;
    
                      t_qbi = (Math.round(t_qbi * 100) / 100).toFixed(2);
                      console.log(game_id + " -- "+ t_qbi);
                  
                       
                       var play_plot = "Team QBI for game: "+t_qbi+"  <a class='research-link highlight' href='game.html#"+team+":"+game_id+"'>;   View Game Details: "+game_id+"   </a><br />\n";
                       plays_text = play_plot;
                       $(details).append(plays_text + "<hr>");
    
                    }
                  }
                  const arr_avg = arr => arr.reduce((a,b) => a + b, 0) / arr.length
                  season_qbi_avg = arr_avg(team_data);
                  season_qbi_avg = (Math.round(season_qbi_avg * 100) / 100).toFixed(2);
                  let season_qbi = "<small>Week 1-8 of 2021 Collective QBI:</small> "+season_qbi_avg;
                  console.log(season_qbi);
               
                spin = document.getElementById( "spin" )
                $( spin ).hide();
                team_qbi = document.getElementById("team_qbi");
                $( team_qbi ).html(season_qbi);
    
            }).fail(function(){
                    console.log("An error has occurred.");
                });
            
        });
    
    

    which is in the file js/handlers/teams.js being a JS based project and since their is no privileged user data or any other secure data all my code is open to the public if you have a browser and want to navigate to it one can and then reverse engineer everything oneself.

    As a coder the funniest and most challenging aspect of this application was the plotting of each play, I wanted to show step by step how the team blocked on each play so one could trace out how the play developed. This was envisioned primarily as an educational tool for offensive line players so they could see the overall picture of what constituted a bad rating and a good rating. This is all done using Javascript, JSON and CVS in a HTML5 UI Layer, below we will walk through the code to see how this is done using Plotly. See the following link for a live example: https://feirmeoirsonrai.me/oline/games_plot.html#2021091207:152

    One is able to view the play from moment to moment as it unfolds, so that at time 0 we see:

    and then at a later time at t=37, which is actually 3.7sec into the play:

    One can also see a path trace of the same play I compiled and put in MP4 format:

    https://feirmeoirsonrai.me/oline/plots/plots/2021091207/2021091207_152_all.mp4

    When we look at games_plot.html we see that it relies on a JS handler file to do the heavy lifting, this is in js/handlers/plot11_on_11.js. One can download the code file as is at https://feirmeoirsonrai.me/oline/js/handlers/plot_11on11.js

    The first bit of lifting is getting the data which I use builtin functions of d3 to do:

    Plotly.d3.csv("./csv/all/"+game_id+"/all_"+play_id+".csv", function (data) {
     function iterateObject(obj) {
            for (const key in obj) {
              console.log(obj);
              if (obj.hasOwnProperty(key)) {
                console.log(`${key}: ${obj[key]}`);
              }
            }
          }
    

    this pulls in a dynamaically referenced csv file, where the data is in comma seperated format which is then accessible in the ‘data’ variable. We then set up some simple X,Y coordinates, which is also common to any datascience plot, then create a object to assign the data to specific parts of the object:

           if (!(trace = bytime_frame[role])) {
              trace = bytime_frame[role] = {
                x: [],
                y: [],
                id: [],
                text: [],
                marker: {size: []}
              };
            }

    so that we end up with different arrays, such as x,y, id, text, marker

    we then iterate the data and push the specific data to the right object part:

        // Go through each row, get the right trace, and append the data:
          for (var i = 0; i < data.length; i++) {
            var datum = data[i];
            var trace = getData(datum.time_frame, datum.role);
            trace.text.push(datum.position);
            trace.id.push(datum.position);
            trace.x.push(datum.x);
            trace.y.push(datum.y);
            trace.marker.size.push(20000000);
        
            
          }
        

    then we slice the data up and put into a traces array

          // Create the main traces, one for each role:
          var traces = [];
          for (i = 0; i < roles.length; i++) {
            var data = firsttime_frame[roles[i]];
            var marker_color = "";
            if(i == 0){
                marker_color = '#000000';
            } else {
                marker_color = '#e64a17';
            }
        
            traces.push({
              name: roles[i],
              x: data.x.slice(),
              y: data.y.slice(),
              id: data.id.slice(),
              text: data.text.slice(),
              mode: 'markers',
              marker: {
                size: data.marker.size.slice(),
                sizemode: 'area',
                color: marker_color,
                sizeref: 175000
              }
              
             //type: 'scatter'
            });

    after this we don’t need to do much more custom handling of the data and the rest is part of the standard operating procedure for serving up any plot. See the link above to the js file to see the whole picture.

    This was a fun project to develop and as it was my own project I had free license to do what I wanted with it and decided to have some fun. I hope you enjoy it’s different way of handling data and lack of a ‘back-end’. As an experienced developer can see such a methodology works well for sites not requiring secure logins, for informational sites such a setup can easily replace andy LAMP stack backends and rely solely on front-end technology to get the job done although we are dealing with MBs of data.