среда, 18 апреля 2012 г.

По мотивам собеседований

Итак, перед тем как пойти на собеседование ответь про себя на следующие вопросы:

1. Python

Какие бывают типы данных в Python?
Чем отличается list от tuple?
Что такое immutable объекты? Какие Вы знаете? В чем особенность?
Что такое dict? Что может являться ключом? Что не может являться ключом? Каким образом нужно изменить класс что бы он смог быть ключом?

Что такое декораторы?
В какой момент вызывается декоратор?
Может ли декоратор НЕ возвращать декорируемую им функцию?
Можно ли декорировать класс?
Может ли класс быть декоратором?

Что такое новые классы? чем отличаются от старых классов?
Чем отличается @staticmethod от @classmethod? 
Что происходит при обращении к property b экземпляра класса А? Как ищутся properties/methods класса?
Что происходит если property не найдено?
Что такое MRO? алгоритм решения проблемы?
Что такое metaclass?
Как создать metaclass?
Как сделать класс использующий metaclass?

Что такое процессы и потоки в Python? Какие возникают проблемы?
Какие есть решения для синхронизации данных при работе с потоками?
Что такое GIL?

Что такое итераторы?
Что такое генераторы?
Что такое list comprehensions?
Что быстрее встроенные функции map reduce filter или аналогичный код просто через for? Почему?

Что значит ключевое слово global?
Какие особенности областей видимости в Python?
Зачем нужен код исключения?
Что такое lambda?

2. Django

Какой основной шаблон проектирования лежит в основе фреймверка?
Что пошагово происходит когда пользователь нажал на ссылку? от браузера до приложения и обратно.
Что такое middleware?
Как сделать сообщение с трейсбеком при ошибке(500.html недостаточно)?
Какие типы отношений между таблицами бывают в Django-ORM?
Как реализовать отношение ManyToMany без ORM?
Какие встроенные сигналы бывают в Django?
Основные способы развертывания приложений?
Что такое mod_wsgi?
Что такое fixtures?

3. SQL

Что такое внешний ключ и зачем он нужен?
Что такое GROUP BY?
Что такое ORDER BY?
Что обозначает ключевое слово HAVING?
Что в чем отличие между LEFT JOIN, RIGHT JOIN и INNER JOIN?

4. Unix

Можно ли сделать ssh подключение к другому компьютеру с компа, к которому ты итак уже подключен через ssh? как это сделать?

5. SCM

Можно ли удалить коммиты из репозитория в git? Как?
Можно ли восстановить коммиты?
В чем отличие веток git от веток в svn?
Что такое git reset?
Что такое git rebase?
Чем git rebase отличается от git merge?

6. Алгоритмы

Что такое сложность алгоритмов? Как вычисляется?
Какова сложность алгоритма 
for i in x:
    for j in y:
        i*j
Какие алгоритмы сортировки знаете?
Какие есть способы обхода дерева?

7. Прочее

Что такое doctype в html?
Что такое meta в html?
Что значит переменная g в Flask?

понедельник, 20 февраля 2012 г.

Anonymizer для Encounter


Что это?
Простенький инструмент для того, чтобы обойти ограничение количества активных участников в играх Encounter.

Как это работает?
Вы общаетесь с Анонимайзером, а он общается с движком Encounter. При этом вас может быть много, а Анонимайзер один. Поэтому движок Encounter считает, что читает задания и вводит коды только один игрок. А вы в это время видите в браузере привычный интерфейс и не замечаете разницы (кроме разницы в скорости отклика движка).

Как это совмещается с принципом FairPlay?
Моя позиция такова, что ограничение количества активных участников в играх Encounter является глупым, вредным и неэффективным. Глупым - потому что никак не ограничивает число фактических участников игры, а всего лишь затрудняет им доступ к движку. Вредным - потому что провоцирует команды искать окольные пути. Неэффективным - потому что большинство регулярно играющих команд уже проложили и утоптали эти окольные дорожки.
Пожалуй, единственная цель, которая успешно достигается этим ограничением, - это насильственное дробление команд с целью повышения доходов авторов игр.
Таким образом, настоящий FairPlay в играх Encounter возможен только при отмене ограничения количества активных участников. Именно эту цель (отмену ограничения) я и преследую.


Установка(upd: 29.10.2013).
1) Качаем инсталлятор отсюда
2) Устанавливаем
3) В папке, куда оно поставилось, редактируем файл settings.ini
там заполняем:
username - существующий логин под которым будет играть команда.
password - пароль
domain - домен, на котором мы будем играть.

Например, есть игра с ограничением состава на домене msk.en.cx
Наш файл должен иметь примерно такой вид:
username=air
password=airpassword
domain=msk.en.cx
По-умолчанию, сервер будет работать на порту 8001. Если он у вас чем-то занят - то необходимо поменять его в последней строке файла anonymizer.py на любой открытый порт на вашем компьютере.

ВНИМАНИЕ. Домен вводим именно в формате "xxx.en.cx", а не "www.xxx.en.cx" или "http://xxx.en.cx/" Будьте внимательны.

Запуск.(upd: 29.10.2013)
Кликаем на ярлычке.
Если у вас при запуске была следующая ошибка:
socket.error: [Errno 98] Address already in use
значит 8001 порт у вас занят. нужно либо освободить его либо указать другой в скрипте.
Теперь все. Сервер готов к использованию.

Открываем в браузере http://<ваш внешний айпишник>:8001/
Cвой внешний айпишник можно определить зайдя сюда
То есть если у вас айпи 79.125.20.11 и порт указан по-умолчанию, открывать будем
http://79.125.20.11:8001/
В открывшемся окне смотрим залогинены ли мы. Если нет - снова редактируем файл и вводим правильные логин и пароль еще раз. сохраняем файл, перезапускаем сервер, обновляем страницу в браузере.
Если залогинены - кидаем ссылку в чат остальной команде. Приятной игры.

ВНИМАНИЕ. Если вы выходите в сеть через роутер то в браузере вы увидите что страница не найдена. В этом случае вам нужно единоразово "пробросить порты": 
смотрим свой локальный айпишник, идем в настройки роутера, находим там port forwarding и указываем forwarding для определенного порта на свой айпишник, сохраняем настройки, обновляем страничку в браузере. все.


Нюансы.
Анонимайзер "запоминает" все введенные ответы всеми игроками которые его используют в данный момент и не дает повторно вводить одно и тоже в систему - для того что бы было меньше палева в мониторинге.
Сбросить память ему можно введя в качестве ответа слово "ап". Само слово на сервер отправляться не будет но теперь он начнет запоминать ответы игроков с нуля. Это полезно при переходе на следующий уровень в играх линейного типа.

Советы.
1) Рекомендую не играть с помощью анонимайзера в игры типа брутфорс или гх ибо быстродействия вашего компьютера и скорости интернет-соединения может не хватить для того что-бы быстро обрабатывать кучу запросов в секунду. В этом случае анонимайзер может подлагивать.
2) Рекомендую обновлять задания при помощи кнопки "Обновить" в меню игры.
3) Рекомендую использовать анонимайзер исключительно по прямому назначению - то есть смотреть задания и бить ответы. Не нужно там ф5тить статистику или писать посты на форум или делать денежные переводы и тд и тп. Это все лишние запросы к серверу, что может привести к лагам и тормозам.

Исходник.
anonymizer.py
# -*- coding: utf8 -*-
import urllib
import random

from flask import Flask, request
app = Flask(__name__)

from MozillaEmulator import *
dl = MozillaEmulator()

username = ""
password = ""
domain = ""

answers = ['ап']
answer = ""

def do_login():
    login_url = "http://%s/Login.aspx?return=/" % domain
    page = dl.download(login_url, postdata=urllib.urlencode({"Login": username,
                                                             "Password": password}))
    print "******** READY ********"

@app.route('/')
def home():
    url = "http://%s/" % domain
    page = dl.download(url)
    return page

@app.route('/', methods=['GET', 'POST'])
def index(link):
    url = "http://%s/%s?%s&lol=%s" % (domain, link, request.query_string, random.random())
    if request.method == 'GET':
        page = dl.download(url)
    if request.method == 'POST':
        post_data = {}
        for k, v in request.form.to_dict().items():
            post_data[k] = v.encode('utf-8')
            if k == 'Answer':
                answer = post_data[k]
        if answer in answers:
            page = dl.download(url)
            if answer == 'ап':
                global answers
                answers = ['ап']
        else:
            answers.append(answer)
            page = dl.download(url, postdata=urllib.urlencode(post_data))
    return page

if __name__ == "__main__":
    do_login()
    app.run(port=8001, host='0.0.0.0')

Если есть вопросы по установке-настройке-использованию - пишите прям тут в комментах.

понедельник, 6 февраля 2012 г.

VirtualBox как окружение для проекта

Если нужны точные версии серверного софта, то разработка может осложняться. В таких случаях, вместо того, чтобы тратить время на поиски пакетов для вашей десктопной ОС, можно просто взять VirtualBox, установить там тот же (или похожий) дистрибутив, что используется в реальном окружении проекта, и установить нужные версии софта уже там.

Код при этом редактируется (и runserver запускается) на ОС-хозяине, а проблематичные серверы отлично работают на ОС-госте. Для доступа к сервисам гостя с хозяина можно настроить port forwarding. На Mac OS X 10.6.6 это делается, например, так:


/Applications/VirtualBox.app/Contents/MacOS/VBoxManage modifyvm "postgres8.4.8" --natpf1 "guestmemcached,tcp,,11212,,11211"


Выполняется это при выключенной VirtualBox-машине, её имя - postgres8.4.8. В примере guestmemcached - имя "связи", 11212 - номер порта хозяина, 11211 - номер порта гостя.

Следует не забывать, что в настройках серверов на госте надо разрешить доступ с нелокальных адресов.

четверг, 19 января 2012 г.

django-mptt и South

В доке на django-mptt рекомендуют сделать manage.py syncdb при подключении MPTT. При использовании South мы сделаем две миграции: schemamigration после того, как унаследуем модель от MPTTModel и datamigration после неё, чтобы перестроить дерево. К сожалению, с последним пунктом маленькая проблема.



Для перестройки дерева вызывается метод менеджера TreeManager rebuild(). Однако внутри миграции модель "заморожена", и менеджера такого у неё нет. Решение прямое - добавить к модели менеджера и вызвать rebuild()




from mptt.managers import TreeManager
from mptt.models import MPTTOptions
...
def forwards(self, orm):
"Write your forwards methods here."
Place = orm['places.Place']
Place.add_to_class('tree', TreeManager())
Place.add_to_class('_mptt_meta', MPTTOptions())
Place.tree.init_from_model(Place)
Place.tree.rebuild()


Так сработает. Place тут - модель, дерево для которой надо перестроить.