# Replit View Counter

> Published  Mar 03 2021, last updated Mar 03 2021  
> By Ryan Fleck <hello@this-site> and written without LLMs!  
> Original post at <https://ryanfleck.ca/2021/replit-view-counter/>  
> An article of astonishing quality and insight. Happy Hacking!


As a casual browser of this site, you may have noticed the section at
the bottom of each page showing a word count, estimated reading time, and view
counter.

![view counter](/pics/view-counter.png)

The word count and reading time are generated by _Hugo_, but Hugo can't track
view counts by nature; it's a static site generator, only able to smash bits of
HTML together at lightspeed.

I had to implement a backend to handle this dynamic page data.

At first, I had great ambitions for a set of services to connect all my sites,
and started a Django project called _Auxilliary Website Services_. The original
intention was to bring user accounts, bookmarking, comments, and other gizmos to
`ryanfleck.ca`, `manuals.ryanfleck.ca`, `smallminds.dev`, etc. Unfortunately,
there just hasn't been a real need or time to do this, and users have had to
wait _upwards of thirty seconds_ for the Django view-counter to boot. In
addition, I have added `utteranc.es` recently, which was crazy easy to set up
and provides the perfect authentication method (Github login) for a
corporate-facing tech site.

To solve this problem, I decided to take half an hour to rewrite the
view-counter in **Repl.it**, an integrated code editor and PaaS. Using the
included key-value database, it'd be super easy to associate numbers with
website paths.

...and that's exactly what I did. Here's the source code. Scroll to the end of
the page to see the view counter in action.

```py

#Hack the REPL at https://replit.com/@RyanFleck/analytics#main.py
#Bottle Template created by @rediar
#bottle is good for smale scale web applications. It is easy to quickly make API's or small personal webpage using Bottle.

from bottle import request, Bottle
from truckpad.bottle.cors import CorsPlugin, enable_cors
from json import dumps
from replit import db

app = Bottle()


#this is the main page
@app.route('/')
def index():
    return '<pre>RCF Analytics Mk 0.0.0.9</pre>'


#this can only be accesed with a post request (visiting in browser will return 405 Method Not Allowed error)
@enable_cors
@app.post('/api/view-counts/page-tracker/')
def post_request_only():
    print("Analytics POST, collecting data...")
    path: str = str(request.json.get('page_url'))
    site: str = str(request.headers['origin'])
    if not path or not site:
        return dumps({})

    # ReplIT Database HATES '/', so remove 'em
    key: str = f"{site}-{path}".replace('/', ' ').strip().replace(' ', '-')
    print(f"key: {key}")
    try:
        views = db[key]
        print(views)
        db[key] = views + 1
        return dumps({'page_views': views + 1})
    except KeyError:
        print("KeyError, adding...")
        db[key] = 1
        return dumps({'page_views': 1})


#A 404 page if the url doesn't exist
@app.error(404)
def error404(error):
    return (
        "oops! the page you were looked for isn't here. <a href='/'>Return Home?</a>"
    )


app.install(
    CorsPlugin(origins=['https://ryanfleck.ca']))
app.run(
    host='0.0.0.0',
    port=1234)  #this starts the webpage. any code after this line won't be run

```

Page views are cached in _Session Storage_ to prevent multiple views during a
single session from ratcheting up the view count and inflating the popularity of
the page.

All that said, this is certainly the most privacy-respecting way to implement
something like a page view counter, and a good first step towards rolling your
own page analytics.



> Thank you for reading!  
> Find more content at <https://ryanfleck.ca/>  
> Source page: <https://ryanfleck.ca/2021/replit-view-counter/>  
> Site index: [llms.txt](https://ryanfleck.ca/llms.txt)