We're growing into something new. Read More
ejetzer

Émile Jetzer


from __future__ import print_function

class PropagateErrors(object):
    
    def __init__(self):
        self.errors = []
    
    def add_explanation(self, errors, wrong, explain):
        errors = errors[:]
        wrongs = [k[0] for k in errors]
        if wrong in wrongs:
            index = wrongs.index(wrong)
            errors[index] += explain
        else:
            errors.append([wrong] + explain)
        return errors
    
    def propagate_error(self, other, func):
        err = []
        for i in self.errors:
            if isinstance(other, PropagateErrors):
                for j in other.errors:
                    wrong = func(i[0], j[0])
                    explain = []
                    for m in i[1:]:
                        for n in j[1:]:
                            explain.append(m + n)
                    err = self.add_explanation(err, wrong, explain)
            wrong, explain = func(i[0], other), i[1:]
            err = self.add_explanation(err, wrong, explain)
        return err

def errorobj(num, errors, kind):
    res = kind(num)
    res.errors = errors
    return res

class ErrorInt(PropagateErrors, int):
    
    def __init__(self, num):
        self.errors = [[-num, ['Put the wrong sign for {}...'.format(num)]]]
        int.__init__(self, num)
    
    def __add__(self, other):
        res = super(self.__class__, self).__add__(other)
        err = self.propagate_error(other, lambda x, y: x + y)
        res = errorobj(res, err, self.__class__)
        return res
    
    def __sub__(self, other):
        res = super(self.__class__, self).__sub__(other)
        err = self.propagate_error(other, lambda x, y: x - y)
        res = errorobj(res, err, self.__class__)
        return res
    
    def __mul__(self, other):
        res = super(self.__class__, self).__mul__(other)
        err = self.propagate_error(other, lambda x, y: x * y)
        res = errorobj(res, err, self.__class__)
        return res
    
    def __div__(self, other):
        res = super(self.__class__, self).__div__(other)
        err = self.propagate_error(other, lambda x, y: x / y)
        res = errorobj(res, err, self.__class__)
        return res

if __name__ == '__main__':
    a = ErrorInt(1)
    b = ErrorInt(2)
    c = a * b
    print(a, b, c)
    print(c)

Error Prediction in Math Problems

I am working on a problem generator, and one thing that I would like to be able to do is provide feedback based on the user's answer, to help them find their mistakes. In order to do that, I have to be able to find where the mistakes can be. That's a fairly hard task, and I thought about parsing symbolic expressions to do it, but that is fairly hard. I am also using sympy for the symbolic computations, and it provides classes for symbols, that I could use to make my own classes, that keep track of what mistakes can be made at each operation. This is a small demonstration of the idea:

def bike(span=12, dir=0, err=50):
    puddles, past_rain, past_snow = past_weather()
    conditions, post_rain = current_weather()
    if post_rain > MAX_RAIN:
        return False, post_rain, 'rain'
    elif puddles > MAX_SNOW and past_snow:
        return False, puddles, 'snow'
    conditions = conditions[:span-1] if len(conditions) >= span else conditions
    for temp, wind, rain in conditions:
        if not MIN_TEMP < temp < MAX_TEMP:
            return False, temp, 'temp'
        elif wind[0] > MAX_WSPD and (dir-err < wind[1] % 180 < dir+err):
            return False, wind, 'wind'
        elif rain > MAX_RAINFALL:
            return False, rain, 'rain'
    return True

Should I bike or take the bus?

I am trying to reduce the number of decisions I have to make daily, and in that spirit I am making a small program that can decide for me whether I should bike to school, or take the bus. # Important factors Right now, it makes its decision based on the weather forecast. It looks at what the weather should be like, and checks to see whether the conditions are fine for biking. I have to set boundaries for wind speed (in the direction I'm going), temperature, and precipitation, so that it can comb through meteorological data. It also considers possible accumulations of snow or rain from the day before, so that I don't end up biking in small lakes or stuck in snow in the middle of the street.

def square_flake(depth=5, unit=10):
    degrees(4)
    if depth:
        for i in [1, -1, -1, 1, 0]:
            square_flake(depth-1, unit)
            left(i)
    else:
        forward(unit)

def kosch_flake(depth=1, unit=10):
    degrees(6)
    if depth:
        for i in [1, -2, 1, 0]:
            kosch_flake(depth-1, unit)
            left(i)
    else:
        forward(unit)

Turtle Fractals

This is a small program I made that draws some fractals using recursive functions. I'm planning on using them in a website design to build photo galleries, where each photo of the gallery is a line of the fractal. I wanted to build small procedures to build the fractals; the final product will be in HTML and the lines will be the actual pictures, rotated to the correct angles.

Make up math (& oher subjects) problems

This is a program I made up to build lots of exercises for a tutoring student of mine. It generates problems of increasing intensity, and directs you to a harder problem if you're right, and to an easier one if you're wrong, kind of like Khan Academy, but on paper as well as on a computer.

CV Layout

Here is a shot of my CV layout, made in HTML. I had fun with making it easy to read and to browse, and using colors to denote the important elements within sentences. Making the small calendars at the bottom was nice as well, although not very semantic. All the information about the background color of the cells is in the stylesheet, instead of in the markup in the form of classes, so it isn't obvious to someone reading the code what the availabilities are; but the document is made to be printed anyway, so I didn't worry to much about it.

#!/usr/bin/python3.2
# -*- coding: utf-8 -*-

# Global Imports

from subprocess import call
from os.path import abspath, expanduser, exists, join, basename, isdir
from os import listdir, symlink, unlink

# Local Imports

# Definitions

realify = lambda x: abspath(expanduser(x))

## Where to find the repositories
REPFILE = 'Notes & Assignments Repository'
ASSFILE = 'Assignments/'
NOTFILE = 'Notes/'
CLASSES = realify('~/Documentos/McGill')

MAXERRORS = 4 # Maximum num. of File not found errors (code '8')

def fetch(url, cwd):
    code = call(['wget', url], cwd=cwd)
    return code

def repository(folder):
    base, assmod, notmod = 'localhost:80', 'ass{}.pdf', 'notes{}.pdf'
    with open(join(folder, REPFILE)) as repfile:
        base, assmod, notmod = [line.strip() for line in repfile]
    return base, assmod, notmod

def download(url, folder):
    res = []
    while res.count(8) <= MAXERRORS:
        address = url.format(len(res))
        if not exists(join(folder, basename(address))):
            code = fetch(address, folder)
            res.append(code)
            continue
        res.append(0)

def retrieve(folder):
    folder = realify(folder)
    base, assmod, notmod = repository(folder)
    assfold = join(folder, ASSFILE)
    url = join(base, assmod)
    download(url, assfold)
    url = join(base, notmod)
    notfold = join(folder, NOTFILE)
    download(url, notfold)

# Execution

def main():
    courses = []
    for course in listdir(CLASSES):
        if isdir(join(CLASSES, course)):
            retrieve(join(CLASSES, course))

if __name__ == '__main__':
    main()

Syncing Notes & Assignments

This is a short & simple script that allows me to update automatically my class notes & assignments, so that I always have the latest documents on my computer. It iterates over all the folders in a special *CLASSES* directory, and looks up the base url for the notes & assignments, and downloads them to the correct subfolder. I don't really have any error management in this; if a url doesn't exist, it will stop trying after *MAXERRORS*, and because there are not many fiels to look up here, the surplus (useless) requests are not a problem.

#!/usr/bin/python3.2
# -*- coding: utf-8 -*-

import os
import os.path
import sys
import random
import time

realify = lambda x: os.path.abspath(os.path.expanduser(x))
SOURCE = realify('~/Documentos/') # Get projects from documents
# Obvious naming ftw.
EXCLUDE = realify('~/Documentos/Files excluded from drafting')
TIMESTAMP = realify('~/Escritorio/.drafter') # I don't want to see this file
WORKDIR = realify('~/Escritorio/') # The work file has to be easily visible
PRIOR = realify('~/Documentos/Drafting Priorities') # Priorities!
LOG = realify('~/Escritorio/Drafting Log')
TIME = 24*60*60 # 24 hours in seconds

def get_priorities():
    distr = {}
    with open(PRIOR, encoding='utf-8') as priorities:
        for line in priorities:
            name, prob = line.split(':')
            name = os.path.basename(realify(name.strip()))
            prob = int(prob.strip())
            distr[name] = prob
    return distr

def choose_project(logger):
    if not os.path.exists(EXCLUDE):
        print('There is no exclusion file. Creating one.', file=logger)
        with open(EXCLUDE, 'w', encoding='utf-8') as excluded:
            print(EXCLUDE, file=excluded)
            print(os.path.join(SOURCE, 'drafter.py'), file=excluded)
            print(EXCLUDE, file=excluded)
            print(PRIOR, file=excluded)
        print('Exclusion file created.', file=logger)
    # Obtain the list of all files in the Documents folder
    print('Obtaining list of usable files...', file=logger)
    docs = os.listdir(SOURCE)
    print('Raw file list is:', file=logger)
    for i in docs:
        print(' > {}'.format(i), file=logger)
    # Eliminate all zip files from the list
    # Eliminate all excluded files from the list ie completed projects
    print('Eliminating all *.zip and excluded files from the list...',
          file=logger)
    with open(EXCLUDE, encoding='utf-8') as excluded:
        excluded = [os.path.basename(realify(name)) for name in excluded]
        print('Excluded files are:', file=logger)
        for i in excluded:
            print(' > {}'.format(i), file=logger)
    docs = list(filter(lambda x: not (x.endswith('.zip') or x in excluded),
                       docs))
    print('The adjsuted list is:', file=logger)
    for i in docs:
        print(' > {}'.format(i), file=logger)
    print('Adjusting for project priorities...', file=logger)
    iterated_docs = set(docs)
    distr = get_priorities()
    for name in iterated_docs:
        if name in distr:
            docs += [name] * (distr[name] - 1)
    print('The prioritized list is:', file=logger)
    for i in iterated_docs:
        print(' > {}: {}/{}'.format(i, docs.count(i), len(docs)), file=logger)
    # Choose a file at random
    print('Choosing a random project...', file=logger)
    name = random.choice(docs)
    print('\'{}\' has been chosen.'.format(name), file=logger)
    file = os.path.join(SOURCE, name)
    link = os.path.join(WORKDIR, name)
    # Copy it to the work folder
    os.symlink(file, link)
    print('The project has been linked on the desktop.', file=logger)
    # Save the time of the copy
    print('Time stamping the project.', file=logger)
    with open(TIMESTAMP, 'w', encoding='utf-8') as stamp:
        stamp.write(name + '\n')
        stamp.write(str(time.time()))
    print('Project time stamped.', file=logger)

def check_project(logger):
    # Check for time stamp file
    if os.path.exists(TIMESTAMP):
        print('There is a time stamp file.', file=logger)
        print('Checking out last time stamp...', file=logger)
        with open(TIMESTAMP, encoding='utf-8') as stamp:
        # Get the time at which the last project was copied
            name = stamp.readline().strip()
            print('The last project was \'{}\'.'.format(name), file=logger)
            span = float(stamp.readline())
            print('It was chosen at {}.'.format(span), file=logger)
        # Compare with current time
        if span + TIME < time.time():
            # If too much time has elapsed, copy folder back to its location
            # & then delete time stamp file
            print('It has been more than 24h...', file=logger)
            os.unlink(os.path.join(WORKDIR, name))
            print('Desktop link has been removed.', file=logger)
            os.remove(TIMESTAMP)
            print('Its time stamp has been erased as well.', file=logger)
            print('Choosing a new project...', file=logger)
            choose_project(logger)
    else:
        print('No project has ever been chosen.', file=logger)
        print('Choose a first project...', file=logger)
        choose_project(logger)

def main():
    with open(LOG, 'w', encoding='utf-8') as logger:
        print('# Checking on project...', file=logger)
        check_project(logger)
        print('# Done.', file=logger)

if __name__ == '__main__':
    main()

Update on «project randomizer»

This is an update of [this](http://forr.st/~HBY) post, about a small script that selects projects from my document folder, at random. Basically, I implemented priorities into the program, so that it values important projects more than unimportant ones.

#!/usr/bin/python3.2
# -*- coding: utf-8 -*-

# Global Imports
from random import choice, randint

# Local Imports

# Definitions
# REMARK: the units are: meters, and minutes.

def distro2pop(distro, total):
    pop = []
    for distance, prob in distro.items():
        pop += [distance] * int(prob * total)
    return pop

def length(distro, total=2500):
    pop = distro2pop(distro, total)
    return choice(pop)

def time(distro, total=90):
    pop = distro2pop(distro, total)
    return choice(pop)

def stroke(distro, total=100):
    pop = distro2pop(distro, total)
    return choice(pop)

def trainset(maxtime=90, speed=25, p=0.8,
             distro={time:{10:1}, length:{75:1}, stroke:{'crawl':0.5,
                                                         'dos':0.3,
                                                         'brasse':0.1,
                                                         'papillon':0.1}}):
    pop = distro2pop({length: p, time: 1-p}, 10)
    tot_dist, sets = 0, []
    while maxtime > 0:
        fct = choice(pop)
        total = maxtime if fct is time else maxtime * speed
        newset = fct(distro[fct], total)
        maxtime -= newset if fct is time else newset / speed
        tot_dist += newset if fct is length else newset * speed
        sets.append(str(newset) + 'min' if fct is time else str(newset) + 'm')
    sets = [(dist, stroke(distro[stroke])) for dist in sets]
    return sets, tot_dist

# Execution

def main(maxtime=900, speed=25, len_to_time=0.8,
         distro={time:{10:1}, length:{75:1}, stroke:{'crawl':0.5,
                                                     'dos':0.3,
                                                     'brasse':0.1,
                                                     'papillon':0.1}}):
    practice, dist = trainset(maxtime, speed, len_to_time, distro)
    for part in practice:
        print(part)
    print('Total :', dist)

maxtime = 70
speed = 17
len_to_time = 0.9
distro = {time: {5: 0.6, 10: 0.4},
          length: {50: 0.3, 100: 0.5, 200: 0.2},
          stroke:{'crawl':0.5, 'dos':0.3, 'brasse':0, 'papillon':0.2}}
main(maxtime, speed, len_to_time, distro)

Swimming Practice Generator

This is a small program that generates a base for swimming practices. It gives distances & strokes, based on time & speed constraints. It is possible to tweak the ratios of the different strokes, and of the distances, to fit into a training schedule.