Category Archives: Python

Reason for my Absence: Freshman Fall

The good news is that I’m done with school until February; I’ve been on break since the middle of December; everything has gone very well since I left. Still, I feel as though I should update my blog–even during the school year–from now on. I learned tons and tons of interesting things this fall, and I’d love to share them!

Oh yeah, and one of my friends (my neighbor, in fact) has started blogging, and I would never want to miss out on the fun. The difference is that he’s writing his own blogging platform, and I’m lazy and running WordPress.

Maybe I’ll do something cool with Django someday.

In the meantime, it’s good to be authoring some content again. I’ll start posting a lot more regularly (no, really, I will this time), especially since I’ll be authoring lots of cool software in 6.01 (Intro to Electrical Engineering and Computer Science I) next semester. Here’s what I’ve done since I left:

Wrote a Book

A long time ago, I started authoring (and nearly published) a blog post about how one could beat the SAT essay. I expanded that rough post into a much longer handbook that I lovingly call The SAT Essay Ace Template. You can get your own copy at http://satessayace.com. Kindle and print (!) versions are forthcoming on Amazon!

Almost Released Hawgrade GradeSolve

As I reread my blog, I realized that I hadn’t given any of my last summer’s work nearly as much love as it deserved. I released a beta version (that I redesigned and am almost ready to release) of a web application remake of Hawgrade that I call GradeSolve. Learn more at GradeSolve‘s website.

Joined a Fraternity

Or, as my girlfriend’s father calls it, a franerdity. I’m a pledge-member of Phi Kappa Sigma at MIT. Hopefully, I’ll post that I am a brother within a few weeks.

Lived Through Freshman Year

I did well, even with Pass/No Record taken out of the picture. I’m proud of how I did.

Started Writing More Software

I’m working with a friend to expand GradeSolve into something even more useful. I’ll probably be posting a lot of code snippets from my work.

Speaking of code snippets, I’ve been working with updating Django model data with user input from ModelForms. This would normally be trivial, except I’m getting my data via AJAX: I cannot simply render a ModelForm with data in a GET request, and I don’t think it’s necessary for all relevant fields to be present in the POST data for the update.

In other words, I want to update what a user tells me to update, and leave everything else alone. Simply specifying a model instance for a ModelForm does not work. Luckily, you can create a new dictionary to which to bind the ModelForm for validation. The dictionary contains the data supplied by the user, and form fields left blank are automatically populated from the model instance. Here’s the method:

def edited_form_data_merge(post_data, model_instance, form_fields):
	data = { }
	for field in form_fields:
		if field in post_data:
			data[field] = post_data[field]
		else:
			data[field] = getattr(model_instance, field)
 
	return data

The method should be invoked like so (Subject is a model, SubjectForm is a ModelForm for Subject):

from x.y.z import Subject, SubjectForm, edited_form_data_merge
# ...
subject = Subject.objects.get(id = subject_id)
frm_data = edited_form_data_merge(request.POST, subject, SubjectForm._meta.fields)
form = SubjectForm(frm_data, instance = subject)
# ...

The form will always validate (assuming correct user input), and will reflect only changes: if data is not specified by a user, then data from the model is used instead.

One very interesting part of this is SubjectForm._meta.fields. This collection contains a string list of fields that makes it very easy to create the new data dictionary.

It’s good to be back!

Easy SQL Query Counting in Django

I’ve become somewhat of a Django ninja during my time as GradeSolve’s developer (okay, a Django hacker…). One of the things that I have noticed as I have written several thousand lines of Django code is that SQL queries tend to be made pretty regularly–often, a little too regularly. Some of GradeSolve’s pages were loading a little slowly for my tastes, so I decided to do some basic SQL profiling to see where the trouble was.

The issue there was that django-debug-toolbar didn’t want to play nice, and I lacked the motivation to try to get it working. Instead, I re-invented the wheel a little bit and write a simple piece of middleware to print out the SQL execution time and number of SQL queries made for a particular request. Obviously, this is going to go away in the production version of GradeSolve (which is being released in eight days!), but for the time being, I like the extra information on my debug console.

Anyway, here’s the middleware class. Stick this in one of your project’s files:

from django.db import connection
class SqlPrintMiddleware(object):
    def process_response(self, request, response):
        sqltime = 0 # Variable to store execution time
        for query in connection.queries:
            sqltime += float(query["time"])  # Add the time that the query took to the total
 
        # len(connection.queries) = total number of queries
        print "Page render: " + unicode(sqltime) + "sec for " + unicode(len(connection.queries)) + " queries"
 
        return response

Now, in your settings.py file, add the path to the middleware class. For example, if your project’s name is gradesolve and your file’s name is middleware.py, your middleware classes setting would end up looking like:

MIDDLEWARE_CLASSES = (
    # ...
    'gradesolve.middleware.SqlPrintMiddleware',
    # ...
)

Happy (very simple) profiling!

Creating Profiles with Django-registration

As part of developing Hawgrade, I’m learning the Django framework. That seemed like a noble idea about a week ago, and I’m practically competent already, but I have run into several snags so far. Major snags. Hair-pulling snags. One of the big ones was how to create a profile while simultaneously registering a user with django-registration 1.0 (FYI: I am using Python 2.7.4 with Django 1.5.1 on Ubuntu 13.04 “Raring Ringtail” x86). I’ve decided to do this post because I saw about a million questions about this topic on StackOverflow. This tutorial assumes that you have already installed django-registration and have included it in your urls.py file such that you access the django-registration app in your accounts/ url:

urlpatterns = patterns('',
    # ...
 
    url(r'^accounts/', include('registration.backends.default.urls')),
 
    # ...
)

We’re going to do the following:

  1. Define a model for our profile.
  2. Define a registration form that inherits from registration.forms.RegistrationForm.
  3. Attach to the django-registration user_registered signal so we know when the user registers. We save the user’s profile in the callback.
  4. Modify urls.py to use our custom registration view.

And now, we get to the Python! We’ll start in models.py. Our example profile stores whether or not a user is human.

class ExUserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    is_human = models.BooleanField()
 
    def __unicode__(self):
        return self.user

Next, we need the custom registration form. If you don’t already have a file named forms.py in your app, make one. You’re going to make a custom form.

from registration.forms import RegistrationForm
from django import forms
 
class ExRegistrationForm(RegistrationForm):
    is_human = forms.ChoiceField(label = "Are you human?:")

After the form, we need to attach to the user_registered signal. This is easily accomplished. We’re going to put the method in our models.py file, because, well…I’m new to this…the Django documentation says so!

from registration.signals import user_registered
 
def user_registered_callback(sender, user, request, **kwargs):
    profile = ExUserProfile(user = user)
    profile.is_human = bool(request.POST["is_human"])
    profile.save()
 
user_registered.connect(user_registered_callback)

The last step is the simplest one: editing the urls in urls.py so that users can actually access your cool new registration form. It is REALLY IMPORTANT to put your new entry BEFORE the django-registration include entry.

from YOUR_APP.forms import ExRegistrationForm
from registration.backends.default.views import RegistrationView
 
urlpatterns = patterns('',
 
    # ...
 
    url(r'accounts/register/$', 
        RegistrationView.as_view(form_class = ExRegistrationForm), 
        name = 'registration_register'),
 
    url(r'^accounts/', include('registration.backends.default.urls')),
 
    # ...
)

This code should be fairly extensible–even if you’re not very familiar with Django. One thing I have noticed is that the documentation is very thorough. Good luck!

Hawgrade: Coming soon to a school near you?

I’m sitting in my new office at RMC, writing this post with Ubuntu 13.04 on monitor #2 (I had to bring in my own NVidia GeForce GTS 250 to get two monitors to work; some gamers may note that the GTS 250 is a beefy card). I think it’s only right that I update my blog a log more frequently this summer, since it was RMC that gave rise to this blog in the first place.

OOOVVVEEERRRRKKIILLLLLL!!!

OOOVVVEEERRRRKKIILLLLLL!!!

So what am I working on? For one thing, probably fewer C# tutorials. I’m working on learning Python and the Django framework. To do so, I’m porting Hawgrade to Python/Django. The best part is that RMC is paying me to do so; I’m going to try to get a marketable product by the end of the summer. Right now, the process is going surprisingly well:

Looks a lot like the current version of Hawgrade, huh? I need a graphic designer...

Looks a lot like the current version of Hawgrade, huh? I need a graphic designer…

[Much more frequent] updates will continue. For now, I have a backend to finish!

Computational Chemistry is the Spice of Life

Actually, no, it isn’t. It’s pretty darn interesting, though.

Since AP Chemistry ran out of things to do (since we took the AP test), my teacher decided to assign our class a project: come up with a way to relate chemistry to your future career field. Easy, I thought. MIT has Course 6-7 (computational biology), so some sort of chemistry equivalent (computational chemistry) must exist somewhere on the Internet. As it turned out, I was spot on. Computational chemistry is big.

I started looking at computational chemistry software. All of it carried a steep learning curve and a steeper price tag, and I couldn’t really think of much high-level research, since I just finished AP Chemistry. Instead, I decided to take matters into my own hands. I thought it might be interesting to do some “exploration” around the Ideal Gas Law. At first, the goal of my project was simply to show that PV did indeed equal nRT. After I finished the first step, I started looking into why (hint: gas molecules aren’t very big). After that, it dawned on me that I could calculate an approximate value of R, the Universal Gas Constant.

What’s more, I wrote my program in Python:

#!/usr/bin/env python3
# Computational chemistry: does PV = nRT?
# Copyright (c) 2013 John Parsons. All rights reserved.
import sys
import random
import math
import copy
 
class Particle:
    """Gas molecule."""
    def __init__(self, vel, pos):
        """Creates a new object instance. Vel: (xVelocity, yVelocity, zVelocity). Pos = (x, y, z) coordinates."""
        # Set up variables
        self.velocity = vel
        self.position = pos
 
    def getListOfDistances(self, allParticlesInTank):
        """Gets a sorted list of distances between the current particle and all other particles. allParticlesInTank: list of all other particles."""
 
        particles = [x for x in allParticlesInTank if x.velocity != self.velocity and x.position != self.position] # Get a shallow copy of the particle list and remove the current particle
        particleDistances = [] # List of tuples that stores distances
 
        for particle in particles: # Loop through particles
            distance = 0
 
            # Quick way of implementing 3D distance formula
            for x in range(3):
                distance += (self.position[x] - particle.position[x])** 2
 
            distance = distance ** 0.5 # Take the square root
 
            particleDistances.append((particle, distance)) # Add the particle - distance tuple to the list
 
        return sorted(particleDistances, key = lambda x:x[1]) # Return particles sorted by their distance from the current particle
 
 
# Shameless self-promo        
print("compchem.py -- Copyright (c) 2013 John Parsons")
print("Compares simulated gas pressure with the Ideal Gas Law.")
# Print usage if user doesn't know what he's doing
if len(sys.argv) < 7:
    print("USAGE: python compchem.py TEMPERATURE_IN_KELVIN MOLAR_MASS_IN_GRAMS NUMBER_OF_PARTICLES ITERATIONS ATOMIC_RADIUS_PICOMETERS INTERMOLECULAR_COLLISIONS_TRUE-FALSE")
    print("EX: python compchem.py 300 16.00 1000 1000 200 True")
    exit()
 
# Constants / calculations to make the later code more verbose
AVOGADRO_NUMBER = 6.022 * (10 ** 23)
CUBE_SIDE_WIDTH = 1000000 # Bounds of simulation, in nanometers
SURFACE_AREA_M = (6 * (CUBE_SIDE_WIDTH ** 2)) * 10**-18 
NUMBER_OF_PARTICLES = int(sys.argv[3]) # User-defined number of particles to play with
GAS_CONSTANT = 8.3144621 # R
GAS_TEMPERATURE = float(sys.argv[1]) # T
GAS_MOLAR_MASS = float(sys.argv[2]) # M (grams -- note that we must convert to KG for momentum!)
GAS_PARTICLE_MASS = GAS_MOLAR_MASS / AVOGADRO_NUMBER # Convenient mass of single particle
GAS_SPEED = (3 * GAS_CONSTANT * GAS_TEMPERATURE / (GAS_MOLAR_MASS / 1000)) ** 0.5 # (3rt/m)^.5, root-mean-square velocity of gas
TANK_VOLUME = CUBE_SIDE_WIDTH ** 3 # Volume of container in nm^3, used later.
SIMULATOR_ITERATIONS = int(sys.argv[4])
ATOMIC_RADIUS = float(sys.argv[5]) # In picometers = 10**-12m
INTERMOLECULAR_COLLISIONS = False
if sys.argv[6].lower() == "true": # Whether or not to check for collisions between molecules
    INTERMOLECULAR_COLLISIONS = True
 
 
# Print all the simulation parameters (calculated and hard-coded) in a nicely formatted manner
print("Simulation paramaters:\r\n\tGAS_SPEED = " + str(GAS_SPEED) + " m/s\r\n\tGAS_MOLES = " + str(NUMBER_OF_PARTICLES / AVOGADRO_NUMBER) + " moles\r\n\tTANK_VOLUME = " + str(TANK_VOLUME) + " nm^3\r\n\tATOMIC_RADIUS = " + str(ATOMIC_RADIUS) + " pm\r\n\tCHECK_INTERMOLECULAR_COLLISIONS = " + str(INTERMOLECULAR_COLLISIONS) + "\r\n\tSIMULATION_TIME = " + str(SIMULATOR_ITERATIONS * 10**-8) + " s")
 
# Apply ideal gas law: num particles/Avogadro = n; GAS_CONSTANT = R, GAS_TEMPERATURE = T, tank_volume * 10**-24 = tank volume in m^3
IDEAL_PRESSURE = ((NUMBER_OF_PARTICLES / AVOGADRO_NUMBER) * GAS_CONSTANT * GAS_TEMPERATURE) / (TANK_VOLUME * (10 ** -27))
print("\r\nIdeal Gas Law calculated pressure: " + str(IDEAL_PRESSURE) + " Pa\r\n")
 
def velocityVectorWithSpeed(speed):
    """Creates a velocity vector with random direction and specified speed."""
    x = random.randint(-500, 500) # Create random vector components
    y = random.randint(-500, 500)
    z = random.randint(-500, 500)
    magnitude = (x**2 + y**2 + z**2)**0.5 # Magnitude of vector
    unitVector = (x/magnitude, y/magnitude, z/magnitude)
 
    return ([i * speed for i in unitVector]) # Return vector such that its magnitude is the specified speed
def randomPosition():
    """Generates a random tuple (x, y, z) that stores a position."""
    return ([random.randint(0, CUBE_SIDE_WIDTH) for x in range(3)])
 
particles = [] # list of particles
 
# Create all our cute little particles
for i in range(0, NUMBER_OF_PARTICLES):
    particles.append(Particle(velocityVectorWithSpeed(GAS_SPEED), randomPosition()))
 
sumOfMomenta = 0 # Used for storing total impulse imparted to walls of container
print("Simulating: 0/" + str(SIMULATOR_ITERATIONS) + "\r", end = "")
sys.stdout.flush() # Force printing
 
# Now run the simulation -- each iteration will represent 10^-8 seconds
for ii in range(0, SIMULATOR_ITERATIONS): # Do some number of 10**-8 second iterations (as defined by user)
    if ii % 100 == 0: # Make it pretty as hell
        print("Simulating: " + str(ii) + "/" + str(SIMULATOR_ITERATIONS) + "\r", end="")
        sys.stdout.flush() # Make it actually print
 
    for particle in particles: # Loop through all the particles
        repositionVector = ([x * 10 for x in particle.velocity]) # x m/s * 10**9 nm/m * 10**-8 s/iteration = nm/iteration
 
        # Loop through and change positions
        for i in range(3):
            particle.position[i] = particle.position[i] + repositionVector[i] # Adjust particle position
            if particle.position[i] >= CUBE_SIDE_WIDTH or particle.position[i] <= 0: # Check to see if we need to change direction
                particle.velocity[i] = particle.velocity[i] * -1 # We do! Now we need to figure out how much impulse is imparted
                if particle.position[i] >= CUBE_SIDE_WIDTH: # Prevent the particle from being turned around again by putting the particle where it should really be.
                    particle.position[i] = CUBE_SIDE_WIDTH - (particle.position[i] - CUBE_SIDE_WIDTH) 
                else:
                    particle.position[i] = -particle.position[i]
                # p = mv (mass in kilograms). J = delta p = dp
                dp = (GAS_PARTICLE_MASS * 2 * math.fabs(particle.velocity[i])) / 1000
                sumOfMomenta += dp
 
        # Now we do collision detection
        if INTERMOLECULAR_COLLISIONS:
            distances = particle.getListOfDistances(particles)
            for distance in distances:
                if distance[1] <= ATOMIC_RADIUS * 10**-3: # If they're too close together, we have a collision
                    # We had a collision!
                    print("Collision between molecules.")
                    sys.stdout.flush()
                else:
                    break # There will be no distances greater than this one
 
print("Simulating: " + str(ii + 1) + "/" + str(SIMULATOR_ITERATIONS), end="")
print("\n\r\nSum of momentum changes: " + str(sumOfMomenta) +" m*N")
 
# We ran for (10**-8 * SIMULATOR ITERATIONS) seconds; P=F/A; mv = Ft so P = mv/tA
ACTUAL_PRESSURE = sumOfMomenta / ((10**-8 * SIMULATOR_ITERATIONS) * SURFACE_AREA_M)
PERCENT_ERROR = (math.fabs(IDEAL_PRESSURE - ACTUAL_PRESSURE) / IDEAL_PRESSURE) * 100 # |actual-experimental|/actual
 
# Let the user know how we did
print("Average pressure: " + str(ACTUAL_PRESSURE) + " Pa")
print("Pressure error: " + str(PERCENT_ERROR) + "%")
 
if PERCENT_ERROR > 5:
    print("Percent error was too great; Ideal Gas Law did not hold.")
else:
    print("Percent error was reasonable; Ideal Gas Law held as expected.")
 
print() # Formatting
 
# Now calculate the simulated value of the gas constant
# PV/nT = R
ACTUAL_GAS_CONSTANT = (ACTUAL_PRESSURE * (TANK_VOLUME * 10**-27)) / ((NUMBER_OF_PARTICLES / AVOGADRO_NUMBER) * GAS_TEMPERATURE)
GAS_CONSTANT_ERROR = (math.fabs(GAS_CONSTANT - ACTUAL_GAS_CONSTANT) / GAS_CONSTANT) * 100 # Same error formula as above
 
print("Calculated gas constant: " + str(ACTUAL_GAS_CONSTANT) + " m^3*Pa*K^-1*mol^-1")
print("Gas constant error: " + str(GAS_CONSTANT_ERROR) + "%")
 
input("Press enter to exit.")

I won’t go into any sort of massive description of my code. I will go through a few points, though.

  • You will notice from my (likely very improvable) code that adding molecular collisions will add significantly to the running time of the program. 
  • You will get a significant amount of error when molecules are travelling very quickly. This is expected. The error, however, is on the wrong side of the Ideal Gas Law. It happens because particles literally escape the “test chamber” and thus are unable to impart pressure on the inside of its walls.
  • Low temperatures also create significant error.
  • Newtonian Mechanics are exclusively used. You won’t see Schrodinger’s Equation anywhere in this program. There are a few reasons for this. First, it’s written in Python, so it’s slow to begin with. Second, I have no idea what a partial differential equation is. I might come back to this issue next year!
  • Everything in the program is based on the idea that impulse, or change in momentum, is equal to force times change in time, which is equal to mass times the change in velocity. Thus, mass times change in velocity divided by total change in time is equal to total average force. Divide that by area and you get a pressure! Woo-hoo! Physics!
  • My code probably isn’t very “pythonic.” That’s because I don’t know Python very well. Yet. I’ll get better!
  • Estimation quality and computing time, as they should be, are inversely related (as opposed to the direct relation to time spent doing computational chemistry and fun). The more molecules you simulate for more time, the better your results will be. Fewer molecules give crappier results. Numbers in the thousands for both iterations and particles seem to do okay.

Here’s the program in action. Note the relatively accurate estimation! Fun-fun-fun!

Mmmmmm. Semiconductor chemistry simulating physics. I wonder if the transistors in my CPU feel like actors.

Mmmmmm. Semiconductor chemistry simulating more chemistry. I wonder if the transistors in my CPU feel like actors.

Oh. By the way. This trial eventually finished. FYI, the accepted value of the Universal Gas Constant is 8.3144621 m^3 Pa mol^-1 K^-1. We were off by about 0.0001–we had four sig figs of perfection!

Not bad for a rough approximation that uses "10^-20 moles of gas" and runs for "0.0001 seconds!"

Not bad for a rough approximation that uses “10^-20 moles of gas” and runs for “0.0001 seconds!”

Quotebook, a Grotesquely Over-complicated Version of a Program I Wrote for a Quiz

Last week in my video game development class (which is an introductory-level course on Python 3 with some focus on games), we had a quiz. We had to talk about some things we’d learned, describe the intricate differences between Python’s if, elif and else statements, then write a program. The program intrigued me: the goal was to “simulate a fortune cookie” and display one of five different, pre-determined strings at random.

My first reaction was relief that I was going to be able to write the program with little issue. My second reaction was how incredibly far I could take that idea. An entire social network materialized in my head; a place where people could go to share and discuss quotes or fortunes. Then I realized that I was crazy, but I did decide to make some changes to the program we’d written in my spare time:

  1. Create an online component that would allow anyone to submit quotes to the “fortune cookie”. The “cookie” has become a system that I call Quotebook, for lack of a better name.
  2. Create a Python client for some kind of bare-bones Quotebook API that would accomplish the same task as the quiz program, except after retrieving the quote from Quotebook.

I knew the Python part would be the easy part, so I fired up Visual Studio and got cracking. I used most of the default template but changed the top text and removed the menu. I kept the HeadLoginView and all the other assorted login-related items so I could use the Membership class to attribute submitted quotes to a user. I also added a database to the project and created a table called quotes. Quotes has four columns:

  1. id (int)
  2. byUser (nvarchar)
  3. timestamp (timestamp)
  4. quoteText (nvarchar)

On the homepage, I added a big TextBox and a submit button. Then, I wired it up to write to the database:

protected void btnSubmitQuote_Click(object sender, EventArgs e)
{
    if (HttpContext.Current.User.Identity.IsAuthenticated) // Make sure user is authed before writing bad things to DB
    {
        // Connect to the DB
        SqlConnection sconn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["messageDB"].ConnectionString);
        sconn.Open();
 
        // Create the command to insert the values into the database
        // PARAMETERS are used to STERILIZE all inputs.
        SqlCommand scomm = new SqlCommand("INSERT INTO quotes (byUser, quoteText) VALUES (@byuser, @quotetext)", sconn);
        scomm.Parameters.Add(new SqlParameter("@byuser", System.Data.SqlDbType.NVarChar));
        scomm.Parameters.Add(new SqlParameter("@quotetext", System.Data.SqlDbType.NVarChar));
        scomm.Parameters["@byuser"].Value = Membership.GetUser().UserName;
        scomm.Parameters["@quotetext"].Value = tbQuote.Text;
 
        // Commit to DB
        scomm.ExecuteNonQuery();
 
        // Close and refresh page.
        sconn.Close();
        Response.Redirect("Default.aspx");
    }
    else
    {
        lblError.Text = "
 
You must login or register before adding a quote!"; // Alert user
    }
}

There are a lot of points to cover here. First, the user state gets checked so quotes don’t get attributed to some kind of phantom (and so no exceptions get thrown!). I added a red, textless label to the area next to the submit button so that if the user isn’t logged in, he will get an error message. If he is logged in, the quote gets written to the database. I had to provide my own connection string in the web.config file. The connection string gets used to connect to the database, and a command gets created on that connection.

The command has two parameters: the text of the quote and the user’s name. Those are set through SQL parameters. Although I have no plans to SQL injection attack my own database, it is good practice to use parameters whenever user input is to be committed to a database. After the parameters get set, the data gets written and the connection gets closed.

With the input side of the Quotebook I/O taken care of, I set out to write the output. I wanted two different kinds of output: a mess of all the quotes in reverse chronological order on the homepage and the “bare-bones API” I mentioned earlier. Before I wrote either, I created an easy way to get data out of the database that I call the QuoteEnumerator class. I created a struct called Quote, which contains the quote text and the name of the quote submitter. QuoteEnumerator has a static method that returns a list of all the Quotes stored in the database. I implemented the two like this:

/// <summary>
/// Reads the DB and creates lists of quotes.
/// </summary>
public class QuoteEnumerator
{
    /// <summary>
    /// Obtains a list of quotes from the ASP.NET database.
    /// </summary>
    /// <returns></returns>
    public static List<Quote> GetQuotes()
    {
        List<Quote> retval = new List<Quote>();
 
        // Connect to DB and set up the query
        SqlConnection sconn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["messageDB"].ConnectionString);
        sconn.Open();
        SqlCommand scomm = new SqlCommand("SELECT * FROM quotes", sconn);
 
        // Read the database
        SqlDataReader sdr = scomm.ExecuteReader();
        if (sdr.HasRows)
        {
            while (sdr.Read())
            {
                // Iterate through the results
                Quote q = new Quote();
                q.QuoteSubmitter = sdr["byUser"].ToString();
                q.QuoteText = sdr["quoteText"].ToString();
 
                retval.Add(q);
            }
        }
 
        return retval;
    }
}
 
/// <summary>
/// Contains a quote once it is extracted from the DB.
/// </summary>
public struct Quote
{
    public string QuoteText { get; set; }
    public string QuoteSubmitter { get; set; }
}

The QuoteEnumerator.GetQuotes() method is a relatively simplistic database reading method. It utilizes a SqlDataReader to iterate through all the rows. For each row, it retrieves the quote text and quote submitter, then adds them to a list containing all the other quotes. It returns that list. Using GetQuotes made implementing the homepage side of the output easy.

// Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
    foreach (Quote q in QuoteEnumerator.GetQuotes())
    {
        // Append the quote to the page in a pretty-looking (?) way
        lblError.Text += string.Format("<div style='display:block; padding:2px; margin:0 auto; border:1px solid black; width:80%; text-align:center;'>{0}<br/><br/><span style='font-size:smaller'>Submitted by {1}</span></div><br>", q.QuoteText.Replace("\n", "<br/>"), q.QuoteSubmitter);
    }
}

The result is, uh, kinda stunning…

Quotebook Image

That comment was sure telling the truth!


I only had two obstacles left: implementing the API and implementing the Python script that would utilize it. The API came first. I created a page called getmessage.aspx and deleted all of the code from the ASPX page except the top line indicating that it was, indeed, ASPX. In the code behind, I came up with this little gem to write a random quote as a string to the page (and to give credit to the submitter):

protected void Page_Load(object sender, EventArgs e)
{
    // Grab a random quote
    List<Quote> allQuotes = QuoteEnumerator.GetQuotes();
    Quote chosen = allQuotes[new Random().Next(0, allQuotes.Count)];
 
    // Write the quote as the response text
    Response.Write(chosen.QuoteText + "\n\nSubmitted by " + chosen.QuoteSubmitter);
}

Lastly, I created a Python script to read from the API. It uses urllib, and given my limited Python knowledge, it is more than good enough! (Keep in mind that this is Python 3; my last Python post was written in Python 2)

# randomMessage.py
# Random message downloader python script
print("Fortune Cookie")
DLURL = "http://localhost:6642/getmessage.aspx" # Replace with yours
 
import urllib.request
while input("Enter Q to quit or press ENTER to continue: ").lower() != "q":
    # Open the API page
    response = urllib.request.urlopen(DLURL)
    data = response.read().decode("utf-8") # Download and decode to a string
 
    print("\n" + data, end="\n\n")

With that, I entered some quotes and fired my creation up!

Quotebook Python Script Running

The awesome thing about Python is how incredibly easy it is to build relatively useful programs like this one.


Sure, it’s seven or eight times more code than the school version, but it’s extensible.

Do you have any improvements for this? Additional features? Let me know!

Porting my Calculator to Python

Although the first language I actually learned was VB.NET, Python is what actually inspired me to begin programming. My dad talked me out of learning it (one of my friends suggested that I did); who knows what kind of programmer I would be today if I had learned it. My guess is that I would be better with server-sided activities and worse on the desktop. Perhaps I’d be more of a Linux guy.

Anyway, I got inspired to learn Python yesterday for whatever reason, and today, I decided to port my calculator to Python. For the sake of comparison, I stayed fairly true to my original C# program. Here’s the very annoying program I came up with (it has no way to exit and loops forever so you have to close the console if you want to close it):

# Python calculator program
# This is the first Python program I've written for a LONG time.
def main():
	while 1==1:
		try:
			number1 = float(raw_input("Enter a number, then hit enter: "))
			number2 = float(raw_input("Enter another number, then hit enter: "))
			operator = raw_input("Which operation do you want? (Choose +, -, * or /, then hit enter): ")
			if operator == "+":
				print "The sum is:", Add(number1, number2)
			elif operator == "-":
				print "The difference is:", Subtract(number1, number2)
			elif operator == "*":
				print "The product is:", Multiply(number1, number2)
			elif operator == "/":
				print "The quotient is:", Divide(number1, number2)
			else:
				print "Please choose another operator."
 
		except:
			print "Error, please try again."
 
		print "\r\n"
 
def Add(num1, num2):
	return num1+num2
 
def Subtract(num1, num2):
	return num1-num2
 
def Multiply(num1, num2):
	return num1*num2
 
def Divide(num1, num2):
	return num1/num2
 
main()

Here it is in action, running in PowerShell:

Python Calculator Image

You can do the four most basic operations over and over! Woo!

In doing this port, I noticed that my beginner-targeted C# code was way longer than it needed to be (with all the helper methods) but very easy to understand. I also noticed a lot of things about Python, some of which I liked and some of which I did not.

The Good

  • The console print command makes it very easy to concatenate variables in.
  • In the small program I had, relying on indentation instead of { } to organize code saved a lot of screen space.
  • The raw_input() function serves as an equivalent to Console.WriteLine(), Console.ReadLine() in C#, saving more keystrokes.
  • Overall simplicity and intuitiveness. I’m a C# developer, not a Python developer, but I was able to complete this port with a little help from Google. Python operators were similar to the C# ones and the syntax isn’t difficult to understand at all. I can read Python code and understand much of what it does, unlike Ruby programs which tend to make my eyes glaze over.

The Not-So-Good

  • I’m not sure how well the whole indentation thing would work in a huge program. Things are bound to get mixed up eventually. Still, I’ve spent plenty of time counting curly brackets trying to figure out why my code won’t compile, so it may just come down to preference.
  • Lack of strong typing. I’m used to seeing float number1, not just some random variable declaration. Again, things could get confusing, especially if you forget what a variable is for.
  • The variable declaration syntax. It makes copy and pasting code much easier not having to get rid of keywords, but it is nice to know that you are declaring a variable for the first time, even if it is with the var keyword in JavaScript.
  • This page says, “Java is a pair of scissors, Ruby is a chainsaw.” That’s fair enough, but where does Python figure into things? Does it have enough horsepower to get things done or should I stick with my C#?

Even with its flaws, I do rather like Python. XKCD sums it up pretty well. I think I will try writing some more programs in it. Heck, I might do some server-sided things with it. Learning another programming language can’t possibly be a bad thing.