Spb Transport Online для iOS

June 10th, 2012

Бесплатное социальное приложение для Санкт-Петербурга “SPB Transport Online” предназначено для тех, кто иногда или постоянно пользуется общественным транспортом:

  • Отображение на карте транспорта в режиме реального времени
  • Режим автообновления каждые 15 секунд
  • Поиск и отображение маршрутов на карте

 
Read the rest of this entry »

Сортировка слиянием

December 31st, 2011

Стэнфордские онлайн-курсы

December 22nd, 2011

Завершились первые стэнфордские онлайн-курсы и хотелось бы кратко поделиться своими впечатлениями по итогам.
Во-первых, интересно было взглянуть на образовательный процесс за рубежом. Понятно, что онлайн накладывает свою специфику:

  • более широкая аудитория, как следствие, полная автоматизация проверки заданий. В чем-то, конечно, задания становятся более “типовыми” с вариантами ответа, вместо предоставления простора для фантазии.
  • более сжатые сроки – вместо стандартного семестра, курсы были рассчитаны на 10 недель.  Для преподавателей это, по сути, дополнительная работа, на чистом альтруизме, так что не думаю, что полноценные пилотные курсы были бы возможны в принципе.

Read the rest of this entry »

Тестирование Middleware в Django

November 24th, 2011

Для тестирования view в django есть удобный класс django.test.client.Client и хорошая официальная документация, но что делать, если нам надо протестировать лишь middleware, а не всю фазу от запроса до ответа?
Допустим у нас есть middleware, позволяющее задавать маску по урлам для обертывания в login_required и маску с исключениями в конфигурационном файле:

import re
 
from django.conf import settings
from django.contrib.auth.decorators import login_required
 
class RequireLoginMiddleware(object):
    """
    Middleware component that wraps the login_required decorator around 
    matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and 
    define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your 
    settings.py. For example:
    ------
    LOGIN_REQUIRED_URLS = (
        r'/topsecret/(.*)$',
    )
    LOGIN_REQUIRED_URLS_EXCEPTIONS = (
        r'/topsecret/login(.*)$', 
        r'/topsecret/logout(.*)$',
    )
    ------                 
    LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must 
    be a valid regex.     
 
    LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly 
    define any exceptions (like login and logout URLs).
    """
    def __init__(self):
        self.required = tuple([re.compile(url) for url in settings.LOGIN_REQUIRED_URLS])
        self.exceptions = tuple([re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS])
 
    def process_view(self, request, view_func, view_args, view_kwargs):
        # No need to process URLs if user already logged in
        if request.user.is_authenticated(): return None
        # An exception match should immediately return None
        for url in self.exceptions:
            if url.match(request.path): return None
        # Requests matching a restricted URL pattern are returned 
        # wrapped with the login_required decorator
        for url in self.required:
            if url.match(request.path): return login_required(view_func)(request, *view_args, **view_kwargs)
        # Explicitly return None for all non-matching requests
        return None

Класс достаточно простой, но в его тестировании есть ряд ньюансов.
Начнем с того, что на вход подается объект request, который неплохо бы как-то получить в подходящем для тестирования виде. Для этого в django есть специальный класс RequestFactory:

from django.test import TestCase
from django.test.client import RequestFactory
 
class RequireLoginMiddlewareTest(TestCase):
    def test_url_is_not_in_login_required(self):
        rf = RequestFactory()
        request = rf.get('/abc/test')

Возникает вопрос, что делать с масками для URL? Тест не должен полагаться на данные из конфига, которые могут быть в любой момент изменены. В версии 1.4 будет добавлена возможность временно менять значения из настроек в тестах: Overriding settings, но это trunk, а на продакшене стоит последний stable релиз, т.е. 1.3
Решение не столь элегантное, но зато доволное простое: тесты запускаются в single-thread, так что мы можем поменять настройки в setUp и использовать их далее в тестах:

    def setUp(self):
        self.old_settings_required_urls = settings.LOGIN_REQUIRED_URLS
        self.old_settings_exceptions_urls = settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
        settings.LOGIN_REQUIRED_URLS = (
            r'/test/(.*)$',
        )
        settings.LOGIN_REQUIRED_URLS_EXCEPTIONS = (
            r'/test/exc1(.*)$',
            r'/test/exc2(.*)$',
        )
        self.middleware = RequireLoginMiddleware()
 
    def tearDown(self):
        settings.LOGIN_REQUIRED_URLS = self.old_settings_required_urls
        settings.LOGIN_REQUIRED_URLS_EXCEPTIONS = self.old_settings_exceptions_urls

Теперь нам осталось добавить атрибут session к request’у, иначе мы получим
AttributeError: ‘WSGIRequest’ object has no attribute ‘session’
так как сессия к request’у добавляется в отдельном SessionMiddleware.
Вместо view_func передадим аргументом пустую анонимную функцию, нам важен только возвращаемый результат: None (если пользователь залогинен или URL не требует аутентификации) или HttpResponseRedirect на форму входа.

    def test_url_is_not_in_login_required(self):
        rf = RequestFactory()
        request = rf.get('/abc/test')
        request.session = {}
        response = self.middleware.process_view(request, lambda x: x, [], {})
        self.assertIsNone(response)
 
    def test_access_excluded_from_login_required_urls(self):
        rf = RequestFactory()
        request = rf.get('/test/exc1/foo')
        request.session = {}
        response = self.middleware.process_view(request, lambda x: x, [], {})
        self.assertIsNone(response)
 
    def test_redirect_guests(self):
        rf = RequestFactory()
        request = rf.get('/test/something/')
        request.session = {}
        response = self.middleware.process_view(request, lambda x: x, [], {})
        self.assertIsInstance(response, HttpResponseRedirect)
        self.assertEqual(reverse('auth_login') + '?next=/test/something/', response['Location'])
        self.assertEqual(response.status_code, 302)

Остался последний случай: залогиненные пользователи.
Здесь мы воспользуемся библиотекой Mock (опять из-за сессий на которые опирается django.contrib.auth.login) и “подменить” метод is_authenticated() у user’a чтобы он возвращал True:

    def test_logged_user_access(self):
        rf = RequestFactory()
        request = rf.get('/test/something/')
        request.session = {}
        request.user.is_authenticated = Mock(return_value=True)
        response = self.middleware.process_view(request, lambda x: x, [], {})
        self.assertIsNone(response)

Асимптотический анализ

November 18th, 2011

 

Алгоритм сортировки вставками

November 6th, 2011

Передача объектов по ссылкам и значениям в PHP

March 26th, 2011

Дописывая свой проект столкнулся с проблемой передачи объектов в PHP, о чем и хотел бы тут все расписать, мало ли кому пригодится.

Общеизвестно, что с 5ой версии PHP все объекты передаются по ссылке. Но что же стоит за этой фразой?
Пример первый:

< ?php
session_start();
class My {
        public $i = 0;
}
 
if (isset($_SESSION['my_int'])) {
        $my_int = $_SESSION['my_int'];
} else {
        $my_int = 0;
        $_SESSION['my_int'] = $my_int;
}
if (isset($_SESSION['my_obj'])) {
        $my_obj = $_SESSION['my_obj'];
} else {
        $my_obj = new My();
        $_SESSION['my_obj'] = $my_obj;
}
echo('my_int=' . $my_int++);
echo(' my_obj.i=' . $my_obj->i++);
?>

Read the rest of this entry »

Grin – поиск по файлам

February 6th, 2011

Случайно наткнулся на замечательный инструмент для поиска по файлам:
http://pypi.python.org/pypi/grin
Раньше для поиска пользовался чем-то аля:

find . | xargs grep searchstring

но grin это более высокоуровневая обертка над теми же стандартными юниксовыми командами, которая ищет и подсвечивает результаты сразу “из коробки”:
Все что нужно сделать, это запустить:

grin threading

и насладиться результатом:
…

./multiprocessing/queues.py:
13 : import threading
62 :         self._notempty = threading.Condition(threading.Lock())
152 :         self._thread = threading.Thread(
./multiprocessing/reduction.py:
15 : import threading
70 :     _lock = threading.Lock()
85 :                 t = threading.Thread(target=_serve)

Также, по умолчанию игнорируются все “служебные” директории (аля .svn/, CVS/ and build/.), что очень удобно.
Дополнительные настройки можно экспортировать в GRIN_ARGS, если что-то не устраивает и не вводить каждый раз. Например, добавить в вывод по 2 строчки контекста до и после найденого совпадения:

export GRIN_ARGS="-C 2"

Задачи с доминошками на собеседованиях

October 27th, 2010

По личному опыту, могу сказать, что довольно популярными на собеседованиях являются задачи, так или иначе связанные с покрытиями доминошками каких-либо поверхностей. Условия могут варьироваться, но суть у всех остается одна.

Пример 1.

Имеется шахматная доска 8 на 8 клеток, левый нижний и правый верхний углы которой отрезаны.
задача с доминошками
Можно ли полностью покрыть такую доску доминошками размера 2×1 клетку?
Читать дальше >>

Отображение даты в часовом поясе пользователя

May 21st, 2010

Есть список событий с датами, который необходимо отобразить пользователю. Пользователь может находиться в любом часовом поясе и, соответственно, время будет разное для разных часовых поясов.
Время в базе хранится по гринвичу, т.е. GMT.
Для инициализации объекта Date надо передать строку одного из определенных форматов (‘YYYY-MM-DD HH:II:SS’ в их число не входит), в большинстве фреймворков есть инструменты для форматирования вывода прямо в шаблоне, если вы пишите без них – используйте функции аля:
string date ( string $format [, int $timestamp ] )
в php и им подобные. В django это будет так:

<span class="datetime" {{ row.datetimegmt|date:"M d, Y H:m:s" }} GMT</span>

На выходе соответсвенно получаем:

<span class="datetime">May 21, 2010 23:05:00 GMT</span>

А дальше все просто, создаем объект Datetime, так как мы явно указали зону (GMT) в конце – то дата будет корректна иниуцилизирована, а при выводе отобразиться в пользовательской часовой зоне:

    $(".datetime").each(function() {
        var d = new Date($(this).text());
        var local_date = d.format('Y-m-d H:i'); 
        $(this).text(local_date);
    });

Формат может задать свой или использовать функции toString(), toLocaleString(), но мне их вывод показался диковатым для пользователя, поэтому использовалась javascript-реализация аналогичная php варианту format(), доступная здесь