Table of Contents
CSE 30332 - HW5

CSE 30332 - HW5

Programming Paradigms


In this assignment you will be using HTML and CSS to build flask templates for your API. Building on top of HW4 you will be using these tools to add some additional functionality to your site. The Settings object will be modified to a User object, and you will be implementing simple user accounts for your website. Additional functionality will include a navbar that will allow you to search the current page.

Modifying your classes

reddit_classes.py


Your classes will remain largely similar to HW4, with a few small tweaks. Your Settings object will now be a User object. To facilitate this, add new columns to your User object for username and password. Other than this, your class should not really need to change.

Setting up the API

reddit_api.py


Your API will have 5 endpoints:

  • The landing page will display the subreddit links.
  • The second endpoint will allow the user to add a single number to the URL to see the post titles for the subreddit associated with the number they added to the URL.
  • The third endpoint will allow the user to add another number to the URL in addition to the first. This second number will be associated with a post title from the subreddit associated with the first number. This endpoint will display the comments for the post.
  • The fourth endpoint will be one accepting both GET and POST requests that will allow the requester to change attributes of the Settings object during runtime. using the HTML forms we saw during lecture. If the user is not logged in yet, redirect them to the login page.
  • In addition to the settings endpoint you will now need a login endpoint. This endpoint will be similiar to settings but will just have a username and password box, along with a submit button to allow the user to login. You can again use simple forms as you did for the above endpoint. While there are significant security implications to consider when you start adding user accounts to your site, this is not the class to worry about that in.


  • Note that now instead of expecting your user to modify the URL directly, they will have links that they can click to take them forward in the "levels" of our webpage. For now, we can simply rely on the user's web browser's back button to take them back to the previous page. We can keep the endpoint URL scheme the same, but now we will need to pass a URL for each button to the template that we can insert.

    Running a Flask server


    To run a flask server you first need to set the environmental variable FLASK_APP for your shell. After doing so you can run your server using the command:
    python3 -m flask run -p [PORT]
    where [PORT] is a number between 9000 and 9999. You could also add a main function to your API file to run your code as we've seen in class 4-5 times now.

    Code Overview and Scaffold


    To help you get some sense of the approximate length of each part of the code, I have included the number of lines used in each section in my solution. I will of course probably implement my code slightly differently than you may. Therefore you should take these as a relative reference only, it is completely reasonable that your code may be several lines shorter or longer. You should only start to worry if yours is significantly different than what I have.
    reddit_api.py
                            
    from flask import Flask, request, render_template
    from flask_sqlalchemy import SQLAlchemy
    from db_manager import db_session
    from reddit_classes import Settings, Subreddit, Post
    from flask_bootstrap import Bootstrap
    from flaskext.markdown import Markdown
    
    app = Flask(__name__)
    Bootstrap(app)
    Markdown(app)
    
    user = None
    subs = None
    
    def check_globals() -> None: # 8 LOC
        global user
        global subs
    
        # create a default User object if it doesn't exist
    
        # get the subreddit objects from the database and add the User object
        # if the subreddit objects don't already exist
    
    @app.route('/')
    def display_subreddits() -> str: # 6 LOC
        check_globals()
    
        # return a template displaying all of the subreddits and links to their posts
    
    @app.route('/<int:sub_id>/')
    def display_post_titles(sub_id: int) -> str: # 2 LOC
        check_globals()
    
        # return a template displaying all of the posts and links to their comments
    
    
    @app.route('/<int:sub_id>/<int:post_id>/')
    def display_post_comments(sub_id: int, post_id: int) -> str: # 6 LOC
        check_globals()
    
        # return a template displaying all of the comments
    
    @app.route('/settings/', methods=['GET', 'POST'])
    def settings() -> None: # 12 LOC
        check_globals()
    
        # if there is a POST request update the User object with the new items
        # if there is a GET request, and the user is not logged in, redirect them
        # to the login page, otherwise display the settings page for the user
    
        # return a status code for a successful request
    
    @app.route('/login/', methods=['GET', 'POST'])
    def login() -> None: #10 LoC
    
        # if there is a POST request, log the user in and load their User object from the DB
        # and redirect to the index page
        # if there is a GET request, return the login template
    
    @app.teardown_appcontext
    def shutdown_session(exception=None): #4 LoC
        # save the users settings to the database
        db_session.remove()
    
    if __name__ == '__main__':
        # run your app
                            
                        
    reddit_classes.py
                            
    import re
    import os
    import requests
    from db_manager import Base
    from sqlalchemy import Column, Integer, String, Boolean
    
    class User(Base):
        __tablename__ = 'users'
        user_id = Column(Integer, primary_key=True)
    
        # add all of the Columns for the settings the program will have
        # 14 LOC
    
        def __init__(self, sub_regex='.*', title_regex='.*', comment_regex='.*',
                            sub_num=25, title_num=25, comment_num=25,
                            sub_reverse=False, title_reverse=False, comment_reverse=False,
                            title_attr='score', comment_attr='score'):
    
           # set all of the attributes for the Settings object 
           # 11 LOC
    
        def __repr__(self) -> str:
            return super().__repr__()
    
    class Subreddit(Base):
        __tablename__ = 'subreddits'
        id = Column(Integer, primary_key=True)
    
        # add a column for the subreddit URL
        # 1 LOC
    
        def __init__(self, url: str, user: User) -> None: # 2 LOC
            # set the two Subreddit attributes
    
        def scrape(self) -> None: # 4 LOC
            # scrape the Subreddit URL and instantiate a list of Post objects
    
        def display(self, loc: int, titles: bool = False) -> tuple: # 8 LOC
            # return a tuple with the subreddits URL and a list of Posts you want to
            # display (it's possible this may be an empty list)
            # if titles is True, then scrape the subreddit
            
        def filter(self) -> bool: # 3 LOC
            # Check if the URL of the subreddit matches the regex for subreddits
    
        def __repr__(self) -> str:
            return super().__repr__()
    
    class Post(Base):
        __tablename__ = 'posts'
        id = Column(Integer, primary_key=True)
        # add a column for the post URL
        # 1 LOC
    
        def __init__(self, data, user) -> None: # 6 LOC
            # set the Posts attributes from the passed in data
            # such as title, selftext, and name
    
        def scrape(self) -> None: # 3 LOC
            # scrape the Post's URL
    
        def display(self, loc: int, comments: bool = False) -> tuple: # 10 LOC
            # return a tuple containing the posts title and a list of all Comments to display
            # it's possible the comment list may be empty
            # if comments is true, then scrape the post
    
        def display_comment_tree(self, reply_dict: dict, depth: int) -> str: 12 LOC
            # return a single comment object, whose children attribute contains the comments children
    
        def filter(self, item, comments: bool = False) -> bool: # 6 LOC
            # return true or false depending on whether or not the item matches its regex
    
        def __repr__(self) -> str:
            return super().__repr__()
    
    class Comment():
        def __init__(self, user, time, body, attr):
            # initialize the attributes of the comment class
            # additionally add a list of child comments as well
                            
                        
    navbar.html
                            
    {% extends 'bootstrap/base.html' %}
    
    {% block content %}
    <nav class="navbar navbar-default">
        &lt;!-- Add a navbar to your site, include the same elements as a seen in the screenshots --&gt;
        &lt;!-- 27 LoC --&gt;
    </nav>
    {% endblock %}
                            
                        
    subreddits.html
                            
    {% include 'navbar.html' %}
    
    {% block content %}
    <div class="container">
        {% for sub in subs %}
        <!-- Add each subreddit to a panel container and add a link to view the subreddits posts -->
        <!-- 6 LoC -->
        {% endfor %}
    </div>
    {% endblock %}
                            
                        
    posts.html
                            
    {% include 'navbar.html' %}
    
    {% block content %}
    <div class="container">
        {% for post in posts %}
        <!-- Add each post to a panel container and add a link to view the posts comments -->
        <!-- Use a flask filter to correctly render the markdown -->
        <!-- 9 LoC -->
        {% endfor %}
    </div>
    {% endblock %}
                            
                        
    comments.html
                            
    {% include 'navbar.html' %}
    
    {% block content %}
    <style>
        /*
        Add styling to your comments using CSS
        101 LoC
        */
    </style>
    
    <h4 class="text-center mb-4 pb-2">{{ title }}</h4>
    
    <div class="container">
    {% for comment in comments recursive %}
        <div class="comment">
    
    <!-- Add each comment to a container recursively including the user, attr, post-time, and body -->
    <!-- 40 LoC -->
    
        </div>
    {%- endfor %}
    </div>
    
    {% endblock %}
                            
                        
    settings.html
                            
    {% include 'navbar.html' %}
    
    {% block content %}
    
    <style>
        /* add some style to your settings page using CSS */
        /* 30 LoC */
    </style>
    
    <div class="container">
        <!-- Add a form entry area for each attribute in your settings object -->
        <!-- When the page is accessed show the current settings in the entry areas -->
        <!-- Have a single submission button to send the new settings to the backend -->
        <!-- You can assume that the entries for the settings will be nicely entered for this assignment -->
        <!-- 55 LoC -->
    </div>
    {% endblock %}
                            
                        
    login.html
                            
    {% include 'navbar.html' %}
    
    {% block content %}
    
    <div class="container">
        <!-- Add a form entry area for your username and password -->
        <!-- When the page is accessed and you aren't logged in-->
        <!-- Have a single submission button to send the user info to the login endpoint-->
        <!-- You can assume that the entries for the login will be nicely entered for this assignment -->
        <!-- 37 LoC -->
    </div>
    
    {% endblock %}
                            
                        

    Example Output


    Here are some screenshots of browsing your reddit API:








    We'll do it live

    Note, since we are pulling data from an active website, the articles may change between runs.

    Reddit API Oddities


    There have been several oddities in the reddit API that have been brought to my attention. Firstly, image posts are causing lots of problems for students and secondly, every once in a while you'll get a replies dictionary that is not empty but also not formatted in the standard manner. To handle these I recommend you make use of try/except blocks. The cats subreddit especially has proven problematic and as such we will be lenient when grading in relation to this specific subreddit.

    Submission Instructions


    This assignment is due by 11:59 PM on Wednesday, May 03rd (05/03).

    To submit, please create a folder named HW5 in your dropbox. Then put your python files, named reddit_api.py, reddit_classes.py, db_manager.py, and reddit_setter.py into this folder. Create a sub-directory named templates as seen in class in which to place your four html files: subreddits.html, posts.html, comments.html, settings.html, login.html, and navbar.html

    Assignments are programmatically collected at the due date.

    Extension Policy


    I've made this assignment due as late as I am possibly allowed to by the university. If this is going to cause an issue please talk to me before the deadline and we can try and come up with a strategy for you to finish your assignment.

    Grading Rubric


    Component Points
    Login endpoint follows guidelines:
        - Allows the user to enter a username and password
        - checks the database for entries matching these fields
        - if there is a match, load the settings, otherwise create an account
    10
    2
    3
    5
    Settings endpoint follows guidelines:
        - If the user is not logged in, redirects them to the login page first
    5
    5
    User class follows guidelines:
        - Modified the Settings class to become User class
        - Correctly add username and password fields to the database
    10
    3
    7
    Navbar follows guidelines:
        - Homepage link set up correctly
        - Filter works correctly relative to page
        - settings and login links work
    10
    2
    5
    3
    Templates correctly follow guidelines:
        - Use jinja2 to add data to the template
        - All pages show navbar at top
    10
    5
    5
    Templates work correctly (Looks nice, buttons work, settings reflected, etc...): 25
    Code style 5
    Total 75