<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Michael Jacob M. Delos Santos</title><description>A personal blog about software development, technology, and more.</description><link>https://michael679089.github.io/</link><language>en</language><item><title>LedgerGrad (My Thesis Project)</title><link>https://michael679089.github.io/Michael679089/posts/ledger-grad-my-thesis-project/</link><guid isPermaLink="true">https://michael679089.github.io/Michael679089/posts/ledger-grad-my-thesis-project/</guid><pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Uses Vercel to deploy the website.&lt;/p&gt;
&lt;p&gt;Demonstration video: https://drive.google.com/file/d/18DAViS1PKJsmoKjJ4kNf0L8ebonKqpR8/view&lt;/p&gt;
</content:encoded></item><item><title>Tutorial on how to create a portfolio website like this</title><link>https://michael679089.github.io/Michael679089/posts/tutorial-on-how-to-create-a-portfolio-website-like-this/</link><guid isPermaLink="true">https://michael679089.github.io/Michael679089/posts/tutorial-on-how-to-create-a-portfolio-website-like-this/</guid><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Tools we’re using&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;li&gt;Obsidian (for static content management (you put more content by git pushing not via server).)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is the tutorial&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;Step 1: Get an Astro Theme&lt;/h1&gt;
&lt;p&gt;Get an Astro theme website here: https://astro.build/themes/1/.&lt;/p&gt;
&lt;h1&gt;Step 2: Follow this tutorial&lt;/h1&gt;
&lt;p&gt;Usually the astro theme has a .github folder which handles the way the portfolio website becomes a static page in github pages, if not please follow this tutorial:&lt;/p&gt;
&lt;p&gt;https://docs.astro.build/en/guides/deploy/github/&lt;/p&gt;
&lt;h1&gt;Step 3: Download Vault CMS&lt;/h1&gt;
&lt;p&gt;After you’re done setting up your astro website, install Vault CMS, here’s their official website: https://vaultcms.org/&lt;/p&gt;
&lt;p&gt;At the root of the project do this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create vault-cms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or if the project uses pnpm do this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm create vault-cms
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
always check if the astro theme you used, uses pnpm or npm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Step 4: Download Obsidian (if you don’t have one)&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;We will need to add content via obsidian text editor, which is free forever, but it’s closed source.&lt;/li&gt;
&lt;li&gt;Download obsidian here: https://obsidian.md/download&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Step 5: Open Obsidian at the src/content folder&lt;/h1&gt;
&lt;p&gt;if the astro theme you chose has a src/content folder in it, you can run obsidian in that folder and you can start adding content. Just follow the wizard guide set up and you can do start adding content.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;And now you’re done, you now have a free static github page that you can easily add more content into without having to pay any fees and it’s yours to keep forever.&lt;/p&gt;
&lt;p&gt;Please share this page if you find it helpful.&lt;/p&gt;
</content:encoded></item><item><title>Machine Learning in Hotel Room Pricing</title><link>https://michael679089.github.io/Michael679089/posts/machine-learning-in-hotel-room-pricing/</link><guid isPermaLink="true">https://michael679089.github.io/Michael679089/posts/machine-learning-in-hotel-room-pricing/</guid><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Link to my PDF (IPFS from Pinata): https://indigo-historic-salmon-454.mypinata.cloud/ipfs/bafybeidz544kyrmux3xgwnp3kbdyrgstjzj6nudaxxrtd5cwee7f5aus7q&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;1 Machine Learning in Hotel Room Pricing&lt;/h1&gt;
&lt;p&gt;Data Mining Research - Michael Jacob M. Delos Santos
March 20, 2026&lt;/p&gt;
&lt;h1&gt;2 Problem&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;With the Philippines still being a popular tourist destination for Westerners, venturing into hotels offers a promising business opportunity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Goal&lt;/strong&gt;: What&apos;s the best price for room types in Urban, Suburban, and Rural Areas?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;3 Dataset Description:&lt;/h1&gt;
&lt;p&gt;We have used various sources of data from data scrapers, google maps and via popular hotel booking sites like Agoda.com and Booking.com&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data Types&lt;/th&gt;
&lt;th&gt;List&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Label&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REAL&lt;/td&gt;
&lt;td&gt;latitude, longtitude, price, TouristAttraction_latitude, TouristAttraction_longtitude, distance_from_nearest_TouristAttraction_in_meters, rating_10_star_system, rating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;area_type_sort, price_for_x_adults, num_of_reviews, length_of_stay_days, stars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DATE&lt;/td&gt;
&lt;td&gt;checkin_date, checkout_date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOMINAL&lt;/td&gt;
&lt;td&gt;room_type, name_of_hotel, booking_platform, latitude_and_longtitude, region, area_type, day_of_week, season, nearest_TouristAttraction_name, City, FirstLevelNationAdministrativeDivision SecondLevelNationAdministrativeDivision, Nation, description, checkin_time, checkout_time, amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BINOMIAL&lt;/td&gt;
&lt;td&gt;has_balcony, is_a_resort, is_there_nearby_TouristAttraction, is_near_an_airport, is_near_a_metro_station, is_near_a_University_Building, is_TouristAttraction_A_Mall&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;em&gt;(the rest are amenities I splitted with &quot;, &quot;, now they&apos;re on their own columns with true / false)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;4 Data Visualization&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Prediction of the price of the hotel room based on numerical, text, and boolean attributes&lt;/li&gt;
&lt;li&gt;Predicted Prices as a scatter / Bubble Chart&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1774004716/Obsidian/cnte9algzidrywbbsrdp.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Predicts the price of the hotel room based on numerical, text, and boolean attributes&lt;/p&gt;
&lt;h2&gt;4.1 Predicted Price&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1774005877/Obsidian/zoqigh3xgnbwbctsn2di.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The prediction(price) compared to distance_from_nearest_TouristAttraction_in_meters.&lt;/p&gt;
&lt;p&gt;Also works with malls too:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1774011298/Obsidian/kkypvwuskf76kpzq0axq.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see from the image, if you&apos;re closer to the mall, it will likely increase the base price of the hotel (around PHP 18,000). But if you live far away from malls or from any tourist attraction, the base price significantly decreases to the base price of PHP 5,000.&lt;/p&gt;
&lt;h1&gt;5 How Did I Prepared the Data?&lt;/h1&gt;
&lt;h2&gt;5.1 Step 1: Plan on how to Web Scrape Data&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; When finding for info of hotels for Philippines, I searched first in Kaggle but most of the hotel data I found were from hotels located around Europe. When I searched on the internet, I couldn&apos;t find a decent dataset that I could use to get hotel data. That&apos;s when I decided to scrape data from Google Maps instead.&lt;/p&gt;
&lt;h2&gt;5.2 Step 2: Get the Tools Required&lt;/h2&gt;
&lt;h3&gt;5.2.1 Tools used:&lt;/h3&gt;
&lt;p&gt;Here are the tools that I have used to scrape real-time data of Hotel Prices in Google Maps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instant Data Scraper&lt;/strong&gt; (FREE)
&lt;ol&gt;
&lt;li&gt;An extension available in the google chrome extension store.&lt;/li&gt;
&lt;li&gt;This was used for the initial data gathering.&lt;/li&gt;
&lt;li&gt;I scraped the Google Maps with this chrome extension.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JuPyTer + Skyscraper API:&lt;/strong&gt; (SUBSCRIPTION)
2. I desperately needed data, and their free tiers are very limited. So I subscribed to one of the API&apos;s found in Rapid API.&lt;br /&gt;
1. The API I used in RapidAPI is Air Scraper:
1. https://rapidapi.com/apiheya/api/sky-scrapper
2. It costs around $9.
3. Used JuPyTer notebooks to automatically scrape every hotel booking website related to the hotel name for their room-type and price.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JuPyTer + Ollama:&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;Automating the area_type (Urban, Suburban, Rural) using AI because I don&apos;t know how to identify if a location is either Urban, Suburban, or Rural. I don&apos;t have a realtime database for population of every barangay in the Philippines.&lt;/li&gt;
&lt;li&gt;To figure out if a location is either Urban, Suburban, or Rural.
&lt;ol&gt;
&lt;li&gt;Reason: The factor that determines if a location is Urban, Suburban, or Rural are rapidly changing for me to take track on and I don&apos;t &quot;think&quot; that I have the hardware, money or time to track those, therefore I will use remaining knowledge from this LLM Model to figure out if the current location is either Urban, Suburban, or Rural.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Model used: llama3.1:latest&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Playwright:&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;I used this because I ran out of tokens using Air Scraper so I scraped Google Map&apos;s data instead, specifically for ratings and number of reviews.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5.3 Step 3: Scrape Google Map for 30 minutes&lt;/h2&gt;
&lt;p&gt;I just searched for nearby hotels in Google Map and it showed me a pagination of hotels that my Instant Data Scraper can scrape to.&lt;/p&gt;
&lt;p&gt;I started web-scraping Google Maps on April 18, 2026 then stopped today at March 20, 2026. So my dataset is very recent, and updated.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1774012441/Obsidian/wxacl7x6rse7wwef7ztl.png&quot; alt=&quot;This is Google Maps with Instant Data Scraper Enabled.&quot; /&gt;
&lt;em&gt;This is Google Maps with Instant Data Scraper Enabled.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;5.4 Step 4: Make the JuPyTer project and then automate scraping of booking hotels&lt;/h2&gt;
&lt;p&gt;Using Air Scraper requires payment.&lt;/p&gt;
&lt;p&gt;So I paid around $9 for Air Scraper&apos;s Pro Plan&lt;/p&gt;
&lt;p&gt;I started gathering data around April 18, 2026. I have achieved around 7,000 rows of data. But I reduced it to 5,000 because most of the rows contain empty attributes.&lt;/p&gt;
&lt;p&gt;After I gathered data from Air Scraper using JuPyTer, I noticed that some of the rows didn&apos;t contain ratings, num_of_reviews or prices, they&apos;re either zero or null. I tried to run the JuPyTer program multiple times hoping the Air Scraper doesn&apos;t give me a timeout. Most of the rows got their prices right but some didn&apos;t get ratings and num_of_reviews. I then heard that playwright can scrape data from the web and pretend it&apos;s like a user, and that&apos;s what I used.&lt;/p&gt;
&lt;h2&gt;5.5 Step 5: Setup a worker function to make Playwright get Data&lt;/h2&gt;
&lt;p&gt;Some rows may contain missing reviews and ratings using the values from the rows you scraped from Google Docs. With Playwright, we will search in Google Maps, go directly to the hotels page in Google Maps and get the visible stars and number of reviews there.&lt;/p&gt;
&lt;p&gt;The code here is used to get rating and number of reviews from a direct page of the google hotel place.
Here&apos;s my code that I used for using playwright:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import asyncio
from typing import Any
import urllib.parse
import sys
from playwright.async_api import async_playwright
import difflib
from typing_extensions import TypedDict
import os

def search_query(name_of_hotel: str):
    encoded_query = urllib.parse.quote(name_of_hotel)
    maps_url = f&quot;https://www.google.com/maps/search/?api=1&amp;amp;query={encoded_query}&quot;
    return maps_url

def get_similarity(search_term: str, actual_name: str) -&amp;gt; float:
    search_clean = search_term.lower().strip()
    actual_clean = actual_name.lower().strip()
    similarity = difflib.SequenceMatcher(None, search_clean, actual_clean).ratio()
    return round(similarity, 2)

async def Login_and_Save_Google_State(browser: Any) -&amp;gt; bool:
    context = await browser.new_context()
    page = await context.new_page()
    await page.goto(&quot;https://accounts.google.com/&quot;)
    
    print(&quot;Please log in to your Google account in the browser.&quot;, file=sys.stderr)
    print(&quot;Once you are fully logged in, come back here and press ENTER.&quot;, file=sys.stderr)
    
    await page.wait_for_timeout(1000 * 60)
    await context.storage_state(path=&quot;google_auth.json&quot;)
    print(&quot;Login state saved to google_auth.json!&quot;, file=sys.stderr)
    
    await page.close()
    await context.close()
    return True

# --- THE MAIN SCRAPING FUNCTION ---
async def search_maps_tab(context: Any, name_of_hotel: str) -&amp;gt; dict[str, float | int]:
    chosen_rating: float = 0.0  
    num_of_reviews: int = 0  
    
    # Open a new tab in the shared browser context
    page = await context.new_page()

    try: 
        print(f&quot;Starting the Scraping Process for {name_of_hotel}&quot;, file=sys.stderr)
        maps_url: str = search_query(name_of_hotel)
        print(f&quot;🏃 Navigating directly to: {maps_url}&quot;, file=sys.stderr)
        
        try:
            await page.goto(maps_url, wait_until=&quot;networkidle&quot;, timeout=1000)
        except Exception:
            pass # Network idle timeout reached
        
        isResultsPageVisible: bool = False
        try: 
            print(&quot;🔎 checking if Results Heading is Visible&quot;, file=sys.stderr)
            resultsHeading = page.get_by_role(&quot;heading&quot;, name=&quot;Results&quot;, exact=True)
            await resultsHeading.first.wait_for(state=&quot;visible&quot;, timeout=3000)
            isResultsPageVisible = await resultsHeading.is_visible()
        except Exception:
            try: 
                print(&quot;🔎 checking if You&apos;re at the end of the list&quot;, file=sys.stderr)
                endOfListSpan = page.get_by_text(&quot;You&apos;ve reached the end of the list.&quot;, exact=True)
                await endOfListSpan.wait_for(state=&quot;visible&quot;, timeout=3000)
                isResultsPageVisible = await endOfListSpan.is_visible()
            except Exception:
                isResultsPageVisible = False

        # Nested direct scrape function
        async def direct_google_map_page_scrape(page: Any, chosen_rating: float = 0.0, num_of_reviews: int = 0) -&amp;gt; dict[str, float | int]:
            try:
                await page.locator(&apos;button[aria-label^=&quot;Photo of &quot;]&apos;).first.wait_for(state=&quot;visible&quot;, timeout=10000)
            except Exception:
                await page.wait_for_timeout(1000)

            max_attempts = 5
            attempt = 0
            while chosen_rating == 0.0 and attempt &amp;lt; max_attempts:
                all_ratings = await page.locator(&apos;div[tabindex=&quot;-1&quot;] div.fontBodyMedium span[aria-hidden=&quot;true&quot;]&apos;).all_inner_texts()
                span_texts = [text.strip() for text in all_ratings if text.isascii() and text.strip()]
                
                for text in span_texts:
                    try:
                        chosen_rating = float(text)
                        break 
                    except ValueError:
                        pass
                
                attempt += 1
                await page.wait_for_timeout(100)
            
            await page.wait_for_timeout(1000)

            if chosen_rating != 0.0:
                print(f&quot;✅ Ratings Found! {chosen_rating} Moving on to reviews...&quot;, file=sys.stderr)
                await page.wait_for_timeout(1000)
                reviews_locator = page.locator(&apos;div[tabindex=&quot;-1&quot;] span[aria-label*=&quot;review&quot;]&apos;).first
                
                try:
                    await reviews_locator.wait_for(state=&quot;attached&quot;, timeout=5000)
                    aria_label = await reviews_locator.get_attribute(&quot;aria-label&quot;)
                    if aria_label:
                        num_of_reviews_str = &apos;&apos;.join(filter(str.isdigit, aria_label))
                        if num_of_reviews_str:
                            num_of_reviews = int(num_of_reviews_str)
                            print(f&quot;✅ Number of reviews found: {num_of_reviews}&quot;, file=sys.stderr)
                except Exception:
                    print(&quot;❌ No reviews element found (Timeout).&quot;, file=sys.stderr)
            
            await page.wait_for_timeout(1000)
            return {
                &quot;rating&quot;: chosen_rating,
                &quot;num_of_reviews&quot;: num_of_reviews,
            }

        if isResultsPageVisible:
            print(&quot;📍 You&apos;re in the results page&quot;, file=sys.stderr)
            links = page.locator(&quot;a[aria-label]&quot;)

            class HotelResult(TypedDict, closed=True):
                hotel_name: str
                similarity_score: float
                rating: float  
                href_link: str

            listThing: list[HotelResult] = []

            js_code = &quot;&quot;&quot;
            elements =&amp;gt; elements.map(e =&amp;gt; {
                let rating = 0.0;
                const ratingSpan = e.querySelector(&apos;span[aria-label*=&quot;stars&quot;], span[aria-label*=&quot;star&quot;]&apos;);
                if (ratingSpan) {
                    const aria = ratingSpan.getAttribute(&apos;aria-label&apos;);
                    const match = aria.match(/[\\d\\.]+/);
                    if (match) {
                        rating = parseFloat(match[0]);
                    }
                }
                return {
                    name: e.getAttribute(&apos;aria-label&apos;),
                    rating: rating,
                    href: e.getAttribute(&apos;href&apos;) || &apos;&apos; 
                };
            })
            &quot;&quot;&quot;
            all_hotel_data = await links.evaluate_all(js_code)
            for data in all_hotel_data:
                hotel_name_res = data[&apos;name&apos;]
                href_link = data[&apos;href&apos;]
                extracted_rating = data[&apos;rating&apos;] 
                similarity = get_similarity(name_of_hotel, hotel_name_res)
                
                listThing.append(HotelResult(
                    hotel_name=hotel_name_res, 
                    similarity_score=similarity, 
                    rating=extracted_rating,  
                    href_link=href_link
                ))
            
            listThing.sort(key=lambda x: (x[&apos;similarity_score&apos;], x[&apos;rating&apos;]), reverse=True)
            
            if listThing:
                print(f&quot;✅ Best Match: {listThing[0][&apos;hotel_name&apos;]}&quot;, file=sys.stderr)
                await page.goto(f&quot;{listThing[0][&apos;href_link&apos;]}&quot;)
            await page.wait_for_timeout(3000)

            responseDirectGoogeleMapScrape = await direct_google_map_page_scrape(page)
            chosen_rating = responseDirectGoogeleMapScrape[&quot;rating&quot;]
            num_of_reviews = int(responseDirectGoogeleMapScrape[&quot;num_of_reviews&quot;])
        else:
            print(&quot;📍 You&apos;re in the direct Google Map Page&quot;, file=sys.stderr)
            responseDirectGoogeleMapScrape = await direct_google_map_page_scrape(page)
            chosen_rating = responseDirectGoogeleMapScrape[&quot;rating&quot;]
            num_of_reviews = int(responseDirectGoogeleMapScrape[&quot;num_of_reviews&quot;])
            
    except Exception as e:
        print(f&quot;❌ Error during scraping {name_of_hotel}: {e}&quot;, file=sys.stderr)
    finally:
        # ALWAYS CLOSE THE TAB
        await page.close()
    
    return {
        &quot;rating&quot;: chosen_rating,
        &quot;num_of_reviews&quot;: num_of_reviews,
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the entire worker function and I used a concurrent.engine to run multiple threads that will gather data in one google browser instance, this makes it faster and reduces resource usage.&lt;/p&gt;
&lt;p&gt;The concurrent engine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading
import random
from dotenv import load_dotenv
from typing import Any, cast

# Limit Playwright to 10 active tabs at a time
MAX_TABS = 20
semaphore = asyncio.Semaphore(MAX_TABS)

cached_list: list[dict[str, Any]] = []
total_hotels = len(filteredDF) # Assuming filtered_df is your loaded dataframe

async def process_hotel(context: Any, index: int, row_series: Any):
    async with semaphore:
        load_dotenv(override=True)
        stop_threads_flag = os.getenv(&quot;STOP_PROGRAM_THREADS_NUMBER_TWO&quot;, &quot;false&quot;).lower() == &quot;true&quot;
        
        if stop_threads_flag:
            return
        name_of_hotel = str(row_series[&apos;name_of_hotel&apos;])
        # Add a tiny async sleep to stagger the tabs opening
        await asyncio.sleep(random.uniform(0.1, 1.5))
        
        try:
            # Call the function we defined in Cell 1!
            # We use asyncio.wait_for as our new &quot;Kill Switch&quot; (45 seconds)
            result = await asyncio.wait_for(
                search_maps_tab(context, name_of_hotel),
                timeout=45.0
            )
            
            cached_list.append({
                &quot;index&quot;: index,
                &quot;rating&quot;: result.get(&apos;rating&apos;),
                &quot;num_of_reviews&quot;: result.get(&apos;num_of_reviews&apos;)
            })
            print(f&quot;🎯 Saved [{len(cached_list)}/{total_hotels}]: {name_of_hotel} -&amp;gt; {result}&quot;)
            
        except asyncio.TimeoutError:
            print(f&quot;💀 KILLED [{len(cached_list)}]: Tab hung for over 45 seconds --- {name_of_hotel}&quot;)
        except Exception as e:
            print(f&quot;❌ Failed [{len(cached_list)}]: {e} --- {name_of_hotel}&quot;)

async def run_scraper():
    print(&quot;Launching single browser instance...&quot;)
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False, # Set to False if you want to watch the tabs!
            channel=&quot;chrome&quot;,
            args=[&quot;--disable-blink-features=AutomationControlled&quot;]
        )
        
        # Load the cookies ONCE for all tabs to share
        if os.path.exists(&quot;google_auth.json&quot;):
            context = await browser.new_context(
                storage_state=&quot;google_auth.json&quot;,
                viewport={&quot;width&quot;: 1920, &quot;height&quot;: 1080},
                user_agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36&quot;
            )
        else:
            context = await browser.new_context()

        # Create a task for every row
        tasks: list[Any] = []
        for index, row in filteredDF.iterrows():
            tasks.append(process_hotel(context, cast(int, index), row))
            
        print(f&quot;Firing {len(tasks)} concurrent tab tasks (Max {MAX_TABS} at a time)...&quot;)
        await asyncio.gather(*tasks)
        
        await context.close()
        await browser.close()

# --- THE JUPYTER THREADING WRAPPER ---
def start_scraping_in_background():
    if sys.platform == &apos;win32&apos;:
        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
    asyncio.run(run_scraper())

# Execute the thread
print(&quot;Spinning up Playwright...&quot;)
scraper_thread = threading.Thread(target=start_scraping_in_background)
scraper_thread.start()
scraper_thread.join()

print(&quot;All tabs finished. Merging data back into the DataFrame...&quot;)
for item in cached_list:
    row_idx: int  = cast(int, item[&quot;index&quot;])
    inputDF.at[row_idx, &apos;rating&apos;] = item[&quot;rating&quot;]
    inputDF.at[row_idx, &apos;num_of_reviews&apos;] = item[&quot;num_of_reviews&quot;]

output_filename = &quot;output-data/hotel_data_fully_scraped.xlsx&quot;
os.makedirs(&quot;output-data&quot;, exist_ok=True)
inputDF.to_excel(output_filename, index=False) # type: ignore
print(f&quot;🎉 Scraping complete! Data saved to {output_filename}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5.6 Step 6: Start Automating and Collecting Data&lt;/h2&gt;
&lt;p&gt;After the functions above have been declared and set up. You can start collecting now.&lt;/p&gt;
&lt;h2&gt;5.7 Step 7: Double Check and Clean your Rows&lt;/h2&gt;
&lt;h3&gt;5.7.1 Step 7.1: Splitting the Data&lt;/h3&gt;
&lt;p&gt;There was just one problem, the amenities column is just one big strings of multiple amenities. If there was a way to split those values separated by commas, we can accurately determine if a hotel&apos;s room option contains a certain amenity via true / false on their own columns. That&apos;s what I did with JuPyTer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pandas as pd

inputFolder = &quot;input-data/for-splitting-data&quot;
outputFolder = &quot;output-data/for-splitting-data&quot;
file_name = &quot;Training_Data_Set.xlsx&quot;
inputDFFour = pd.read_excel(f&quot;{inputFolder}/{file_name}&quot;, sheet_name=0) # type: ignore

# 1. Get dummies
# 2. Add the prefix (e.g., &apos;amenity_&apos;)
# 3. Convert to bool
amenities_expanded = (
    inputDFFour[&quot;amenities&quot;]
    .str.get_dummies(sep=&quot;, &quot;)
    .add_prefix(&quot;amenity_&quot;)
    .astype(bool)
)

# Concatenate to the original dataframe
inputDFFour = pd.concat([inputDFFour, amenities_expanded], axis=1)

# Force memory defragmentation to avoid PerformanceWarnings
inputDFFour = inputDFFour.copy()

inputDFFour.to_excel(f&quot;{outputFolder}/{file_name}&quot;, index=False) # type: ignore
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the code I used to split the amenities into their own separate columns, now the data looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773991792/Obsidian/pay9g7nkip0pfceezqjr.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
If the automation wasn&apos;t able to fill the missing attributes in the rows, I have to manually search for it in Google or try to get information quickly via Gemini AI (Google&apos;s AI which also has access to Google&apos;s Hotel API).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h1&gt;6 Model Selection&lt;/h1&gt;
&lt;p&gt;And now we can finally look into the JuPyTer Program.&lt;/p&gt;
&lt;p&gt;I was deciding on what I should use, I tested with both Decision Tree and Random Forests. Random Forests provides a better answer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773998030/Obsidian/fvzvfr4rovswbog75j3d.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;During 2024, I used Decision Tree with Cross Validation.&lt;/p&gt;
&lt;p&gt;But now I&apos;m using Random Trees because that works better. With Cross Validation.&lt;/p&gt;
&lt;p&gt;There are four types of outputs here.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First is the performance of the cross validation, to make the model train to predict on unseen data.&lt;/li&gt;
&lt;li&gt;After that we apply that model to a Random_Forest operator using 0.30% of the data (which wasn&apos;t trained on) to predict it&apos;s price.&lt;/li&gt;
&lt;li&gt;After that I aggregate for the Average, Median, Mode, Minimum, Maximum, Value_For_Money, and Range of each price, grouped by area_type and then by room_type.
&lt;ol&gt;
&lt;li&gt;The Value_For_Money here is the hedonic pricing, which means the lower the value it is, the better it is worth for, even if the initial price is higher.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Lastly I outputted the correlation matrix for myself to see which columns are driving the prices up and which columns are driving the prices down.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6.1 Cross Validation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Here&apos;s to prove that Random Forest is better than Decision Tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Decision Tree&lt;/strong&gt;
&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773997602/Obsidian/dgz5ojju9cjxeikgi9pn.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Random Forest&lt;/strong&gt;
&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773997612/Obsidian/tpn1a3yd14y1ugk6ankx.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Chosen Model will be the Random Forest due to it&apos;s higher percent score in prediction.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6.2 Applying both Models to test their Performance&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;For this one, I wanted to see what&apos;s the performance score when I try to apply the data to the remaining 30% percent of the training data that wasn&apos;t part of the 70% training data.&lt;/li&gt;
&lt;li&gt;Here are my Percent scores when I applied the model to unseen data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Random Forest Cross Validation Model:&lt;/strong&gt;
&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773997724/Obsidian/d6krvjys80drqowrdrwo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;82% accurate at prediction&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Decision Tree Cross Validation Model:&lt;/strong&gt;
&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1773998190/Obsidian/ypytimnicvpckzfv5lax.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;62% accurate at prediction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Random Forest Wins!&lt;/p&gt;
&lt;h1&gt;7 Data Gathered&lt;/h1&gt;
&lt;p&gt;This aggregated table tells us that this is how hotels should be priced based on their area and their room type. Unfortunately not all premium type rooms are included in all three areas but the most commons ones are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Room (which is standard Room)&lt;/li&gt;
&lt;li&gt;Shared Room&lt;/li&gt;
&lt;li&gt;Single Room&lt;/li&gt;
&lt;li&gt;Triple Room&lt;/li&gt;
&lt;li&gt;Suite&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;room_type&lt;/th&gt;
&lt;th&gt;area_type&lt;/th&gt;
&lt;th&gt;area_type_sort&lt;/th&gt;
&lt;th&gt;average(price)&lt;/th&gt;
&lt;th&gt;mode(price)&lt;/th&gt;
&lt;th&gt;median(price)&lt;/th&gt;
&lt;th&gt;minimum(price)&lt;/th&gt;
&lt;th&gt;maximum(price)&lt;/th&gt;
&lt;th&gt;average(value_for_money)&lt;/th&gt;
&lt;th&gt;range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4596.020408&lt;/td&gt;
&lt;td&gt;1413&lt;/td&gt;
&lt;td&gt;2476&lt;/td&gt;
&lt;td&gt;392&lt;/td&gt;
&lt;td&gt;96922&lt;/td&gt;
&lt;td&gt;1054.467454&lt;/td&gt;
&lt;td&gt;96530&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;867.4705882&lt;/td&gt;
&lt;td&gt;415&lt;/td&gt;
&lt;td&gt;820&lt;/td&gt;
&lt;td&gt;350&lt;/td&gt;
&lt;td&gt;1977&lt;/td&gt;
&lt;td&gt;196.7184565&lt;/td&gt;
&lt;td&gt;1627&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3532.744681&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;1527&lt;/td&gt;
&lt;td&gt;374&lt;/td&gt;
&lt;td&gt;21044&lt;/td&gt;
&lt;td&gt;850.8499671&lt;/td&gt;
&lt;td&gt;20670&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8354.864407&lt;/td&gt;
&lt;td&gt;3255&lt;/td&gt;
&lt;td&gt;4872&lt;/td&gt;
&lt;td&gt;1434&lt;/td&gt;
&lt;td&gt;25788&lt;/td&gt;
&lt;td&gt;2012.140749&lt;/td&gt;
&lt;td&gt;24354&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Triple room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2277.5&lt;/td&gt;
&lt;td&gt;1260&lt;/td&gt;
&lt;td&gt;1566&lt;/td&gt;
&lt;td&gt;1080&lt;/td&gt;
&lt;td&gt;4242&lt;/td&gt;
&lt;td&gt;949.6994411&lt;/td&gt;
&lt;td&gt;3162&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Twin room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rural&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3689.702703&lt;/td&gt;
&lt;td&gt;4095&lt;/td&gt;
&lt;td&gt;2660&lt;/td&gt;
&lt;td&gt;788&lt;/td&gt;
&lt;td&gt;16164&lt;/td&gt;
&lt;td&gt;857.1778926&lt;/td&gt;
&lt;td&gt;15376&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deluxe Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;42650&lt;/td&gt;
&lt;td&gt;42650&lt;/td&gt;
&lt;td&gt;42650&lt;/td&gt;
&lt;td&gt;42650&lt;/td&gt;
&lt;td&gt;42650&lt;/td&gt;
&lt;td&gt;10797.46835&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Double room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2866.966403&lt;/td&gt;
&lt;td&gt;4008&lt;/td&gt;
&lt;td&gt;2127&lt;/td&gt;
&lt;td&gt;380&lt;/td&gt;
&lt;td&gt;37800&lt;/td&gt;
&lt;td&gt;708.6419634&lt;/td&gt;
&lt;td&gt;37420&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Family Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;7235.714286&lt;/td&gt;
&lt;td&gt;2396&lt;/td&gt;
&lt;td&gt;4225&lt;/td&gt;
&lt;td&gt;1873&lt;/td&gt;
&lt;td&gt;57000&lt;/td&gt;
&lt;td&gt;2178.776219&lt;/td&gt;
&lt;td&gt;55127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queen Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;4750&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3846.848883&lt;/td&gt;
&lt;td&gt;3215&lt;/td&gt;
&lt;td&gt;2441&lt;/td&gt;
&lt;td&gt;353&lt;/td&gt;
&lt;td&gt;48751&lt;/td&gt;
&lt;td&gt;941.6135443&lt;/td&gt;
&lt;td&gt;48398&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;955.9090909&lt;/td&gt;
&lt;td&gt;353&lt;/td&gt;
&lt;td&gt;578.5&lt;/td&gt;
&lt;td&gt;353&lt;/td&gt;
&lt;td&gt;8482&lt;/td&gt;
&lt;td&gt;233.7225406&lt;/td&gt;
&lt;td&gt;8129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1831.709302&lt;/td&gt;
&lt;td&gt;1803&lt;/td&gt;
&lt;td&gt;1589&lt;/td&gt;
&lt;td&gt;551&lt;/td&gt;
&lt;td&gt;4164&lt;/td&gt;
&lt;td&gt;477.7475015&lt;/td&gt;
&lt;td&gt;3613&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8453.822581&lt;/td&gt;
&lt;td&gt;2430&lt;/td&gt;
&lt;td&gt;5123&lt;/td&gt;
&lt;td&gt;1043&lt;/td&gt;
&lt;td&gt;36607&lt;/td&gt;
&lt;td&gt;1896.618407&lt;/td&gt;
&lt;td&gt;35564&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Triple room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4400.28&lt;/td&gt;
&lt;td&gt;5250&lt;/td&gt;
&lt;td&gt;2759&lt;/td&gt;
&lt;td&gt;1164&lt;/td&gt;
&lt;td&gt;22712&lt;/td&gt;
&lt;td&gt;1005.167115&lt;/td&gt;
&lt;td&gt;21548&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Twin room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suburban&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5862.163842&lt;/td&gt;
&lt;td&gt;11893&lt;/td&gt;
&lt;td&gt;3446&lt;/td&gt;
&lt;td&gt;440&lt;/td&gt;
&lt;td&gt;48329&lt;/td&gt;
&lt;td&gt;1333.617398&lt;/td&gt;
&lt;td&gt;47889&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Double Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5743.333333&lt;/td&gt;
&lt;td&gt;2394&lt;/td&gt;
&lt;td&gt;5836&lt;/td&gt;
&lt;td&gt;2394&lt;/td&gt;
&lt;td&gt;9000&lt;/td&gt;
&lt;td&gt;1487.486325&lt;/td&gt;
&lt;td&gt;6606&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Double room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3672.149184&lt;/td&gt;
&lt;td&gt;687&lt;/td&gt;
&lt;td&gt;2506&lt;/td&gt;
&lt;td&gt;510&lt;/td&gt;
&lt;td&gt;42605&lt;/td&gt;
&lt;td&gt;855.2919723&lt;/td&gt;
&lt;td&gt;42095&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Family Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2380.5625&lt;/td&gt;
&lt;td&gt;1071&lt;/td&gt;
&lt;td&gt;1787&lt;/td&gt;
&lt;td&gt;1071&lt;/td&gt;
&lt;td&gt;13200&lt;/td&gt;
&lt;td&gt;570.5645281&lt;/td&gt;
&lt;td&gt;12129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;King Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;29180&lt;/td&gt;
&lt;td&gt;29180&lt;/td&gt;
&lt;td&gt;29180&lt;/td&gt;
&lt;td&gt;29180&lt;/td&gt;
&lt;td&gt;29180&lt;/td&gt;
&lt;td&gt;6343.478261&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queen Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;7595&lt;/td&gt;
&lt;td&gt;4233&lt;/td&gt;
&lt;td&gt;7392&lt;/td&gt;
&lt;td&gt;4233&lt;/td&gt;
&lt;td&gt;11160&lt;/td&gt;
&lt;td&gt;1929.718876&lt;/td&gt;
&lt;td&gt;6927&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4504.661451&lt;/td&gt;
&lt;td&gt;6800&lt;/td&gt;
&lt;td&gt;3312&lt;/td&gt;
&lt;td&gt;385&lt;/td&gt;
&lt;td&gt;40235&lt;/td&gt;
&lt;td&gt;1045.433574&lt;/td&gt;
&lt;td&gt;39850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;654.9090909&lt;/td&gt;
&lt;td&gt;357&lt;/td&gt;
&lt;td&gt;562&lt;/td&gt;
&lt;td&gt;357&lt;/td&gt;
&lt;td&gt;1767&lt;/td&gt;
&lt;td&gt;194.2465377&lt;/td&gt;
&lt;td&gt;1410&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1690.441176&lt;/td&gt;
&lt;td&gt;800&lt;/td&gt;
&lt;td&gt;1366&lt;/td&gt;
&lt;td&gt;468&lt;/td&gt;
&lt;td&gt;6527&lt;/td&gt;
&lt;td&gt;430.7194787&lt;/td&gt;
&lt;td&gt;6059&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5319.074074&lt;/td&gt;
&lt;td&gt;1083&lt;/td&gt;
&lt;td&gt;3060&lt;/td&gt;
&lt;td&gt;1083&lt;/td&gt;
&lt;td&gt;16500&lt;/td&gt;
&lt;td&gt;1241.744932&lt;/td&gt;
&lt;td&gt;15417&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Triple room&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Urban&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;12094.83333&lt;/td&gt;
&lt;td&gt;1730&lt;/td&gt;
&lt;td&gt;14439.5&lt;/td&gt;
&lt;td&gt;1730&lt;/td&gt;
&lt;td&gt;19836&lt;/td&gt;
&lt;td&gt;2664.758646&lt;/td&gt;
&lt;td&gt;18106&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;According to this table data, I conclude these are the recommended price limit for hotels:
&lt;ul&gt;
&lt;li&gt;The mode(price) becomes the minimum and the average(price) becomes the maximum.&lt;/li&gt;
&lt;li&gt;If mode(price) is higher than average(price), then:
&lt;ul&gt;
&lt;li&gt;average(price) becomes the minimum and mode(price) becomes the maximum.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;For Rural Areas:&lt;/em&gt;
&lt;ol&gt;
&lt;li&gt;Room (which is standard Room) = PHP 1413 - PHP 4596.02&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 415 - PHP 867.470&lt;/li&gt;
&lt;li&gt;Single Room = PHP 502 - PHP 3532.7&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 1260 - PHP 2277.5&lt;/li&gt;
&lt;li&gt;Suite = PHP 3255 - PHP 8354.86&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;For Suburban Areas:&lt;/em&gt;
&lt;ol&gt;
&lt;li&gt;Room (which is standard Room) = PHP 3215 - PHP 3846.85&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 353 - PHP 955.91&lt;/li&gt;
&lt;li&gt;Single Room = PHP 1803 - PHP 1831.71&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 4400.28 - PHP 5250&lt;/li&gt;
&lt;li&gt;Suite = PHP 2430 - PHP 8453.82&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;For Urban Areas:&lt;/em&gt;
&lt;ol&gt;
&lt;li&gt;Room (which is standard Room) = PHP 4504.66 - PHP 6800&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 357 - PHP 654.91&lt;/li&gt;
&lt;li&gt;Single Room = PHP 800 - PHP 1690.44&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 1730 - PHP 12094.83&lt;/li&gt;
&lt;li&gt;Suite = PHP 1083 - PHP 5319.07&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Summary:
&lt;ul&gt;
&lt;li&gt;For the summary I will be just summing up &lt;strong&gt;using only the similar rooms&lt;/strong&gt;.
&lt;ol&gt;
&lt;li&gt;RURAL AVERAGE SUM: PHP 23,318.30279&lt;/li&gt;
&lt;li&gt;SUBURBAN AVERAGE SUM: PHP 25,350.7337&lt;/li&gt;
&lt;li&gt;URBAN AVERAGE SUM: PHP 24,263.91912&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;%% my sheet: https://docs.google.com/spreadsheets/d/1A4TdJQI-dHGTnPOWh0-cKxIXkXBp_ZR1WP7EJfcNUgA/edit?gid=0#gid=0 %%&lt;/p&gt;
&lt;h2&gt;7.1 Which attributes affect the price a lot?&lt;/h2&gt;
&lt;h3&gt;7.1.1 Increases the price:&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;amenity_fitness_center&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;0.3954972263976445&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;is_a_resort&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.3839405545822225&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_spa&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.36997983662569445&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_beach_umbrellas&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.3678213414201146&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_tanning_beds&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.36652284421977144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_private_beach&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.3445662898910885&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_poolside_bar&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.34224123123286265&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_steam_room&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.317359511801071&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_wedding_facilities&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.31619981742977193&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_baby-sitting&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.31035344523307484&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_children&apos;s_play_area&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.30735097964370345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_airport_shuttle&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2686881397044429&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_diving&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2665853174978052&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_shuttle_service&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2647189836800071&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_tennis_court&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.26420755364863424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_breakfast_included&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2605974702209003&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_laundry&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2525143430460432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_car_rental&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.22706739603977677&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_valet_parking&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2222274558349386&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_garden&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.21915065479528942&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_atm&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.2118972418416284&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_nightclub&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.20507619988472892&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_table_tennis&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.19950782313712515&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_ticket_office&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.19623791330641568&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_wake-up_call&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.19103087392689588&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_tours&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.1893518909956548&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_coffee-maker&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.1701530886379577&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_bathrobe&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.16959568004556638&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_pants_press&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.16471111954203052&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_executive_floor&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.16302298368815557&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_hairdressers&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.15675035340142823&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_golf_course&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.15348094989835107&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_library&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.1511156754416117&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_entertainment_rooms&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.14634382583015476&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_station_shuttle&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.13303521344465918&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_bathtub&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.1300553457870314&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_playground&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.12208781374355587&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_aromatherapy&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_beach_bar&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_beach_cabanas&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_billiards&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_body_wrap&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_bowling&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_canoeing&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_chess&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_express_check-in_and_check-out&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_free_parking&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_grocery_and_convenience_store&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_ironing_service&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_meeting_and_banqueting_facilities&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_racquetball&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_restaurant_and_bar&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_snorkeling&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_water_sports_activities&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11965619864084852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_outdoor_swimming_pool&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11954804626387995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_bicycle_rental&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11252502675962407&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_casino&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11166170871364041&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_chapel&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.11098483988631086&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_hairdryer&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.10216458402474596&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_24-hour_front_desk&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;0.10216086013580386&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📈 Attributes that &lt;strong&gt;increase&lt;/strong&gt; price&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The strongest positive drivers are luxury or exclusivity-related amenities. The &lt;strong&gt;top 5&lt;/strong&gt; are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fitness center&lt;/strong&gt; (+0.395) – Guests associate gyms with premium hotels.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resort classification&lt;/strong&gt; (+0.384) – Being labeled a resort signals higher-end services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spa&lt;/strong&gt; (+0.370) – Wellness facilities strongly boost perceived value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Beach umbrellas&lt;/strong&gt; (+0.368) – Indicates beachfront property, which is highly desirable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tanning beds&lt;/strong&gt; (+0.367) – Another leisure/luxury amenity tied to vacation settings.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Other notable price boosters include &lt;strong&gt;private beach access&lt;/strong&gt;, &lt;strong&gt;poolside bars&lt;/strong&gt;, &lt;strong&gt;steam rooms&lt;/strong&gt;, and &lt;strong&gt;wedding facilities&lt;/strong&gt;. These all suggest exclusivity, convenience, or luxury experiences, which justify higher pricing.&lt;/p&gt;
&lt;h3&gt;7.1.2 Decreases the price&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;amenity_outdoor_pool&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;-0.34823685416882183&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;amenity_pool&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.33254620736012436&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_bar&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.3215620065206103&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_concierge&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.30977521527037905&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_currency_exchange&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.29642723184301606&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_safe_deposit_box&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.29457219088648834&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_massage&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2918156359091895&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_iron&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2906423108267366&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_accessible&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2868776723692815&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_restaurant&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.26721910631170376&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_sauna&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2652084802857098&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_business_center&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.258683467223575&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_banquet_service&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2457752564331239&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_shops&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2410757526615483&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_meeting_facilities&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.23197752624286658&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_luggage_storage&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.22415094482699047&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_children&apos;s_pool&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.2237498850249475&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_express_check-out&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.20335694659113474&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_games_room&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.19713506817707016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_express_check-in&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.1969228308664086&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_wheelchair_accessible&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.19347911041259366&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_fax&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.19009540802600186&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_cafÃ©&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.18299451440694972&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_elevator&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.1820671158246891&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;longitude&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.15572566834898344&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_meals_not_included&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.14911200301295766&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_pool_table&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.14533685023424428&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_multilingual_staff&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.140664288863232&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_room_service&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.1373974817379809&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_photocopier&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.1306228100577222&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenity_room_only&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.12834193655159085&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;is_near_a_University_Building&lt;/td&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;-0.1063300171139152&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📉 Attributes that &lt;strong&gt;decrease&lt;/strong&gt; price&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Interestingly, some common amenities are associated with &lt;strong&gt;lower prices&lt;/strong&gt;, likely because they are standard or expected rather than premium. The &lt;strong&gt;top 5 negative drivers&lt;/strong&gt; are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Outdoor pool&lt;/strong&gt; (−0.348) – Pools are common, so they don’t signal luxury.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;General pool&lt;/strong&gt; (−0.333) – Same reasoning; widespread availability reduces exclusivity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bar&lt;/strong&gt; (−0.322) – Bars are typical in mid-range hotels, not necessarily luxury.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concierge&lt;/strong&gt; (−0.310) – Once a premium service, now fairly standard in many hotels.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Currency exchange&lt;/strong&gt; (−0.296) – Practical but not a luxury feature, often found in budget-friendly hotels.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Other negative drivers include &lt;strong&gt;safe deposit boxes&lt;/strong&gt;, &lt;strong&gt;massage services&lt;/strong&gt;, &lt;strong&gt;iron availability&lt;/strong&gt;, and &lt;strong&gt;business centers&lt;/strong&gt;. These are functional or expected amenities, so they don’t justify higher pricing.&lt;/p&gt;
&lt;h1&gt;8 Conclusion&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;To answer the core question:&lt;/strong&gt; &lt;em&gt;What is the optimal price for different room types across Urban, Suburban, and Rural areas?&lt;/em&gt; The predictive insights generated via Altair RapidMiner serve as a data-driven guide. Hotel owners and operators can use this framework to set competitive, marketable rates that reflect actual market behavior rather than relying on simple averages or guesswork.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recommended Prices based on Table Data:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For Rural Areas:
&lt;ul&gt;
&lt;li&gt;Room (which is standard Room) = PHP 1413 - PHP 4596.02&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 415 - PHP 867.470&lt;/li&gt;
&lt;li&gt;Single Room = PHP 502 - PHP 3532.7&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 1260 - PHP 2277.5&lt;/li&gt;
&lt;li&gt;Suite = PHP 3255 - PHP 8354.86&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;For Suburban Areas:
&lt;ul&gt;
&lt;li&gt;Room (which is standard Room) = PHP 3215 - PHP 3846.85&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 353 - PHP 955.91&lt;/li&gt;
&lt;li&gt;Single Room = PHP 1803 - PHP 1831.71&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 4400.28 - PHP 5250&lt;/li&gt;
&lt;li&gt;Suite = PHP 2430 - PHP 8453.82&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;For Urban Areas:
&lt;ul&gt;
&lt;li&gt;Room (which is standard Room) = PHP 4504.66 - PHP 6800&lt;/li&gt;
&lt;li&gt;Shared Room = PHP 357 - PHP 654.91&lt;/li&gt;
&lt;li&gt;Single Room = PHP 800 - PHP 1690.44&lt;/li&gt;
&lt;li&gt;Triple Room = PHP 1730 - PHP 12094.83&lt;/li&gt;
&lt;li&gt;Suite = PHP 1083 - PHP 5319.07&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Key Trends Show:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exclusivity Drives Premium Pricing:&lt;/strong&gt; Amenities focused primarily on leisure drive prices upward, as the data demonstrates a market willingness to pay a premium for these experiences. Conversely, highly functional amenities correlate with lower prices, as consumers already expect them as standard features rather than luxury additions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Baseline Expectation Penalty:&lt;/strong&gt; Features that might traditionally be considered &quot;perks&quot; (such as a standard pool, concierge, or a bar) actually show a strong negative correlation with price. In the current market, these are no longer luxury differentiators; they are baseline expectations for highly competitive, mid-tier, or budget properties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &quot;Rural Resort&quot; Phenomenon:&lt;/strong&gt; While urban markets are highly standardized and competitive, rural areas contain extreme price outliers and command high rates for single rooms and suites. When paired with the high positive correlation of features like &lt;code&gt;is_a_resort&lt;/code&gt;, &lt;code&gt;beach_umbrellas&lt;/code&gt;, and &lt;code&gt;diving&lt;/code&gt;, it is clear that the rural market in this dataset is heavily influenced by luxury vacation destinations rather than budget roadside accommodations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suburban Markets Command a &quot;Space Premium&quot;:&lt;/strong&gt; Even when comparing core room types, suburban areas maintain the highest average marketable price (PHP 25,350). This is largely driven by the high pricing of high-capacity rooms like Suites and Triple Rooms, suggesting suburban hotels successfully cater to families or groups willing to pay a premium for larger footprints.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1774009939/Obsidian/utg6smekg3cmspld3szs.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;9 Thank You! For Listening!&lt;/h1&gt;
&lt;p&gt;checkout my PDF file related to this post: https://indigo-historic-salmon-454.mypinata.cloud/ipfs/bafybeidz544kyrmux3xgwnp3kbdyrgstjzj6nudaxxrtd5cwee7f5aus7q&lt;/p&gt;
</content:encoded></item><item><title>First Impressions on IBM Cognos - 2026</title><link>https://michael679089.github.io/Michael679089/posts/first-impressions-on-ibm-cognos-2026/</link><guid isPermaLink="true">https://michael679089.github.io/Michael679089/posts/first-impressions-on-ibm-cognos-2026/</guid><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I first discovered this IBM Cognos when I was searching for a job, then I found this job https://ph.jobstreet.com/job/89895617?ref=search-standalone&amp;amp;type=standard&amp;amp;origin=showNewTab#sol=4851426656ce331a8796715565c7b2f273ae916e which needs people who use IBM Cognos.&lt;/p&gt;
&lt;p&gt;When I opened the IBM Cognos web application it reminded me of Power BI which easily enabled me to pick up the web application tool well. The problem however is that it’s paid, I was given free trial 30 days. Unlike Power BI when the snapping of the objects are based on the objects themselves, in IBM Cognos you can be able to attach entire graphs on like a template box, like it can easily snap on it which made doing the job easier.&lt;/p&gt;
&lt;p&gt;Within an hour I was able to create this (this was from the tutorial from IBM as well):&lt;/p&gt;
&lt;h1&gt;My First Dashboard created in IBM Cognos – 2/14/2026&lt;/h1&gt;
&lt;p&gt;tutorial used: https://mediacenter.ibm.com/media/1_eof056a5
&lt;img src=&quot;https://res.cloudinary.com/michaelpersonal/image/upload/v1771058316/Obsidian/f8oyvu9uzlgvbtwjrjia.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My thoughts about this is it’s Power BI but it’s better.&lt;/p&gt;
&lt;p&gt;Also check this out, I can put it in my website too:&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe src=&quot;https://ap2.ca.analytics.ibm.com/bi/?perspective=dashboard&amp;amp;pathRef=.my_folders%2FMy%2BFirst%2BDashboard&amp;amp;closeWindowOnLastView=true&amp;amp;ui_appbar=false&amp;amp;ui_navbar=false&amp;amp;shareMode=embedded&amp;amp;action=view&amp;amp;mode=dashboard&amp;amp;nav_filter=true&quot; width=&quot;320&quot; height=&quot;200&quot; frameborder=&quot;0&quot; gesture=&quot;media&quot; allow=&quot;encrypted-media&quot; allowfullscreen=&quot;&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;the above interactable object is an &lt;strong&gt;iframe&lt;/strong&gt; tag provided from the share button of your dashboard. Makes it very easy to share.&lt;/p&gt;
&lt;p&gt;I think I need to log in to this.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PROS&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provides templates&lt;/td&gt;
&lt;td&gt;It’s paid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In the Templates you can snap your graphs to easily resize them automatically.&lt;/td&gt;
&lt;td&gt;online–only - if you like being able to work on your dashboard, you will need constant internet.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How to create your first dashboard (11.2.0)&lt;/em&gt;. (n.d.). IBM Mediacenter. https://mediacenter.ibm.com/media/1_eof056a5&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>My Flask Project that contains Multiple Apps</title><link>https://michael679089.github.io/Michael679089/posts/my-flask-project-that-contains-multiple-apps/</link><guid isPermaLink="true">https://michael679089.github.io/Michael679089/posts/my-flask-project-that-contains-multiple-apps/</guid><pubDate>Fri, 13 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href=&quot;https://github.com/Michael679089/Multi-System_Flask-App&quot;&gt;Multi-System_Flask-App on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Rebuilding a Multi-System Web App with Flask: A Journey from PHP to Python&lt;/h1&gt;
&lt;p&gt;In 2022, I set myself a clear challenge: &lt;strong&gt;take an existing multi-system PHP project and rebuild it as a modern Flask application in Python&lt;/strong&gt;. The result of that effort is the repository &lt;a href=&quot;https://github.com/Michael679089/Multi-System_Flask-App&quot;&gt;&lt;code&gt;Multi-System_Flask-App&lt;/code&gt;&lt;/a&gt; — a learning-focused project that explores how to migrate real functionality from one tech stack to another while preserving (and improving) the core behavior.&lt;/p&gt;
&lt;p&gt;This blog-style write‑up walks through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;motivation&lt;/strong&gt; behind the project&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;core idea&lt;/strong&gt; of a “multi-system” app&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;architecture&lt;/strong&gt; of the Flask version&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;user experience&lt;/strong&gt; and core flows&lt;/li&gt;
&lt;li&gt;What this migration taught me about web development&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The original app was built in PHP. This Flask project is a &lt;strong&gt;Python reimplementation&lt;/strong&gt; of that same multi-system concept.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Why Rebuild a PHP App in Flask?&lt;/h2&gt;
&lt;p&gt;The original project was a &lt;strong&gt;“Multi-System” web application&lt;/strong&gt; written in PHP. It bundled several related subsystems (think: users, records, management tools) into a single interface. While the PHP version worked, it had some limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organization was more ad hoc, with logic and presentation often mixed.&lt;/li&gt;
&lt;li&gt;Routing, templating, and structure were less explicit.&lt;/li&gt;
&lt;li&gt;Extending or maintaining features could become cumbersome over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rebuilding it in Flask had several goals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Learn Flask by doing something real&lt;/strong&gt;&lt;br /&gt;
Rather than following only small tutorials, I wanted to work on a project with actual interconnected features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Experience a technology migration&lt;/strong&gt;&lt;br /&gt;
Porting an app forces you to truly understand what the original did, not just copy code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Apply better structure and separation of concerns&lt;/strong&gt;&lt;br /&gt;
Flask encourages a cleaner split between routing, logic, and templates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leverage Python’s ecosystem&lt;/strong&gt;&lt;br /&gt;
For data manipulation, scripting, and rapid prototyping, Python felt like a natural fit.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;What Is a “Multi-System” App?&lt;/h2&gt;
&lt;p&gt;The term “Multi-System” in this context describes a &lt;strong&gt;single web application that bundles multiple functional modules&lt;/strong&gt; under one interface. While the exact modules depend on the original PHP project, a typical “multi-system” academic or practical app often includes things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User/Account Management&lt;/strong&gt;&lt;br /&gt;
Registering users, managing logins, assigning roles or permissions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Business or Domain Modules&lt;/strong&gt;&lt;br /&gt;
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Students, courses, and grades&lt;/li&gt;
&lt;li&gt;Products, orders, and inventory&lt;/li&gt;
&lt;li&gt;Projects, tasks, and reports&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Administrative Tools&lt;/strong&gt;&lt;br /&gt;
Basic dashboards, summary pages, or simple reporting views.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the PHP version, each of these systems might have been represented by separate PHP scripts (&lt;code&gt;users.php&lt;/code&gt;, &lt;code&gt;products.php&lt;/code&gt;, etc.). In the Flask version, the idea is to re‑express those same systems as &lt;strong&gt;structured Flask routes and templates&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;High-Level Architecture of the Flask Application&lt;/h2&gt;
&lt;p&gt;At a high level, the Flask app follows a classic pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Application Entry Point&lt;/strong&gt;&lt;br /&gt;
A central file (typically named something like &lt;code&gt;app.py&lt;/code&gt; or &lt;code&gt;main.py&lt;/code&gt;) where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Flask&lt;/code&gt; application instance is created&lt;/li&gt;
&lt;li&gt;Configuration is defined&lt;/li&gt;
&lt;li&gt;Routes or blueprints are registered&lt;/li&gt;
&lt;li&gt;The development server can be started&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Route Handlers (Controllers)&lt;/strong&gt;&lt;br /&gt;
Functions decorated with &lt;code&gt;@app.route(...)&lt;/code&gt; that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Receive HTTP requests&lt;/li&gt;
&lt;li&gt;Validate and process input&lt;/li&gt;
&lt;li&gt;Interact with the underlying data (database or in‑memory structures)&lt;/li&gt;
&lt;li&gt;Render templates or return responses&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Templates (Views)&lt;/strong&gt;&lt;br /&gt;
Jinja2 templates under a &lt;code&gt;templates/&lt;/code&gt; directory that define:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Page layouts (headers, navigation, footers)&lt;/li&gt;
&lt;li&gt;Forms and tables for each system&lt;/li&gt;
&lt;li&gt;Reused components (like base layouts and partials)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Layer&lt;/strong&gt;&lt;br /&gt;
Depending on the assignment’s scope, this might use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A lightweight database (e.g., SQLite) via SQL or an ORM&lt;/li&gt;
&lt;li&gt;Or simple Python data structures for demo purposes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The migration from PHP to Flask wasn’t just a language change — it was an opportunity to introduce &lt;strong&gt;more explicit structure&lt;/strong&gt;, making the app easier to navigate, extend, and understand.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Route Design: From PHP Scripts to Flask Endpoints&lt;/h2&gt;
&lt;p&gt;In traditional PHP apps, it’s common to have separate entry-point files like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;users.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;products.php&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each file handles its own logic and HTML. Flask encourages a more centralized and explicit routing style. The equivalent in Flask becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@app.route(&apos;/&apos;)&lt;/code&gt; – Home or dashboard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.route(&apos;/users&apos;)&lt;/code&gt; – User listing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.route(&apos;/users/create&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])&lt;/code&gt; – Create user&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@app.route(&apos;/products&apos;)&lt;/code&gt; – Products system&lt;/li&gt;
&lt;li&gt;and so on…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This shift has several benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Routing is declared in one place&lt;/strong&gt; (or per module/blueprint).&lt;/li&gt;
&lt;li&gt;URL patterns are easier to reason about.&lt;/li&gt;
&lt;li&gt;Every endpoint is a Python function with clear responsibilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Template Layer: From PHP Echoes to Jinja2&lt;/h2&gt;
&lt;p&gt;Another big change in the migration is the &lt;strong&gt;templating layer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In PHP, templates often intermix logic and presentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;?php echo $variable; ?&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;?php foreach ($items as $item) { ?&amp;gt; ... &amp;lt;?php } ?&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Flask, Jinja2 templates encourage cleaner, more readable views:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{{ variable }}&lt;/code&gt; for output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{% for item in items %} ... {% endfor %}&lt;/code&gt; for loops&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{% if condition %} ... {% endif %}&lt;/code&gt; for branching&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Typical templates in a multi‑system Flask app might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;templates/base.html&lt;/code&gt;&lt;br /&gt;
A layout containing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Site header&lt;/li&gt;
&lt;li&gt;Navigation menu linking each system&lt;/li&gt;
&lt;li&gt;Main content block (&lt;code&gt;{% block content %}{% endblock %}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Footer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;templates/index.html&lt;/code&gt;&lt;br /&gt;
The home page or dashboard, usually extending &lt;code&gt;base.html&lt;/code&gt; and introducing the systems:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{% extends &quot;base.html&quot; %}
{% block content %}
  &amp;lt;h1&amp;gt;Welcome to the Multi-System Flask App&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Select a system from the navigation above.&amp;lt;/p&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;System‑specific templates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;templates/users_list.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;templates/user_form.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;templates/products_list.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By moving from PHP templates to Jinja2, the project gains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistency&lt;/strong&gt; in view logic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;, where Python code lives in route functions, not embedded in markup&lt;/li&gt;
&lt;li&gt;The ability to reuse layout components more cleanly&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Data Flow and Core User Experience&lt;/h2&gt;
&lt;p&gt;Although the specific data models depend on the original PHP app, the &lt;strong&gt;user experience pattern&lt;/strong&gt; in the Flask version is familiar and intuitive:&lt;/p&gt;
&lt;h3&gt;1. Home / Dashboard&lt;/h3&gt;
&lt;p&gt;The user lands on a main page that introduces the application and provides navigation to each subsystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Users”&lt;/li&gt;
&lt;li&gt;“Items” / “Products”&lt;/li&gt;
&lt;li&gt;“Reports”&lt;/li&gt;
&lt;li&gt;Or whatever modules the original Multi-System design included.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Navigating to a System&lt;/h3&gt;
&lt;p&gt;Choosing a system (for example, &lt;strong&gt;Users&lt;/strong&gt;) routes to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/users&lt;/code&gt; – a list of users (often with pagination or filters)&lt;/li&gt;
&lt;li&gt;Options to:
&lt;ul&gt;
&lt;li&gt;Add a new user&lt;/li&gt;
&lt;li&gt;Edit an existing user&lt;/li&gt;
&lt;li&gt;Remove a user&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The underlying route might:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Query the data source&lt;/li&gt;
&lt;li&gt;Pass a list of records into a template&lt;/li&gt;
&lt;li&gt;Render an HTML table with actions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Creating and Editing Data&lt;/h3&gt;
&lt;p&gt;Forms are central to any management system. A typical flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user visits &lt;code&gt;/users/create&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt; request: Flask returns an HTML form via a template.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The user submits the form:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; request: Flask route validates the input and:
&lt;ul&gt;
&lt;li&gt;Saves the data (e.g., in a database).&lt;/li&gt;
&lt;li&gt;Redirects back (&lt;code&gt;/users&lt;/code&gt;) or to a detail page, often with a success message.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This read–create–update–delete (CRUD) pattern repeats across all systems in the app, giving a consistent and predictable user experience.&lt;/p&gt;
&lt;h3&gt;4. Feedback and Validation&lt;/h3&gt;
&lt;p&gt;Flask’s features, such as &lt;code&gt;flash()&lt;/code&gt; messages, make it easy to inform the user when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A record has been successfully created or updated&lt;/li&gt;
&lt;li&gt;Validation fails (e.g., missing fields or invalid formats)&lt;/li&gt;
&lt;li&gt;An operation (like deletion) completed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is an app that &lt;strong&gt;feels coherent and professional&lt;/strong&gt;, even as it was built first and foremost as a learning exercise.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Configuration and Dependencies&lt;/h2&gt;
&lt;p&gt;Although lightweight, the project leverages standard Flask practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;requirements.txt&lt;/code&gt; (or similar) listing dependencies like:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Flask&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Potentially &lt;code&gt;Jinja2&lt;/code&gt;, &lt;code&gt;Flask-Login&lt;/code&gt;, &lt;code&gt;SQLAlchemy&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A configuration block or file that sets:
&lt;ul&gt;
&lt;li&gt;Debug mode&lt;/li&gt;
&lt;li&gt;Secret key (for sessions)&lt;/li&gt;
&lt;li&gt;Database URI, if a DB is used&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This level of structure makes it straightforward to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up the project on another machine&lt;/li&gt;
&lt;li&gt;Understand how the app should be run (&lt;code&gt;flask run&lt;/code&gt; or &lt;code&gt;python app.py&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Extend configuration for different environments, if needed&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Lessons Learned from the Migration&lt;/h2&gt;
&lt;p&gt;Rebuilding the Multi-System PHP app in Flask was more than just rewriting code. It highlighted several important lessons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Language changes, architecture remains&lt;/strong&gt;&lt;br /&gt;
Concepts like routing, templates, and data models exist in both PHP and Python — they just look different syntactically. The underlying ideas are portable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Frameworks impose useful structure&lt;/strong&gt;&lt;br /&gt;
Flask encouraged me to think in terms of explicit routes, view functions, and reusable templates. That structure makes the application easier to navigate and reason about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separation of concerns is powerful&lt;/strong&gt;&lt;br /&gt;
Pulling business logic out of templates and into Python functions clarified what each part of the codebase was responsible for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migration deepens understanding&lt;/strong&gt;&lt;br /&gt;
To port the project, I had to thoroughly understand the original PHP version — every form, every flow, every data interaction. That process alone was valuable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python + Flask are excellent for teaching and experimentation&lt;/strong&gt;&lt;br /&gt;
Flask’s minimalism and Python’s readability make them a great combination for educational and prototype projects like this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;Multi-System_Flask-App&lt;/code&gt; project represents a &lt;strong&gt;concrete step in evolving a multi-feature web application from PHP to Python&lt;/strong&gt;, using Flask as a modern, lightweight framework.&lt;/p&gt;
&lt;p&gt;While the application itself is relatively small and focused on educational value, it demonstrates several important skills:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understanding an existing system and its requirements&lt;/li&gt;
&lt;li&gt;Translating features across languages and frameworks&lt;/li&gt;
&lt;li&gt;Designing clear routes and templates&lt;/li&gt;
&lt;li&gt;Building a modular web application rather than isolated scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For anyone considering a similar migration — whether from PHP, another framework, or even a collection of scripts — this kind of project is an excellent way to &lt;strong&gt;deepen your understanding of web architecture while gaining confidence in a new stack&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you’re interested in the code or want to build on this idea, you can explore the repository here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Michael679089/Multi-System_Flask-App&quot;&gt;Multi-System_Flask-App on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;References&lt;/h1&gt;
</content:encoded></item></channel></rss>