22 декабря 2013 г.

Настройка Debian на Cubietruck (cubieboard 3)

В прошлом посте мы установили debian на nand память cubietruck. Итак, сеть не работает.

Проводная сеть.

modprobe sunxi_gmac

Это команда подгрузки модуля сетевого интерфейса в cubietruck. Она вызовет у нас ошибку вроде "could not open moddep file modules.dep.bin". А все потому что такого файла у нас нет и вообще каталога /lib/modules у нас нет. Решается просто - копируйте с SD карты с debian каталог /lib/modules, только проследите за тем, что у ядра у вас совпадают.

root@debtruck:~# mount /dev/mmcblk0p2  /mnt/mmc
root@debtruck:~# modprobe sunxi_gmac
root@debtruck:~# uname -a
Linux debtruck 3.4.61+ #1 SMP PREEMPT Sat Oct 26 13:08:57 CST 2013 armv7l GNU/Linux

После этого сетевой модуль подгрузится и будет работать.

Теперь есть интернет можно поставить еще один пакетик:
apt-get install bash-completion

Включение wifi

modprobe bcmdhd


А проще скопировать файл /mnt/mmc/etc/modules.conf c SD карты в NAND и перезаписать его. Тогда все необходимые модули будут грузиться автоматически. Еще можно скопировать каталог /lib/firmware.

Расширение дискового пространства

Так как изначально образ lubuntu сервера был где-то около 2 Гигабайт, а в NAND вроде нам обещали 8 Гигабайт, то имеет смысл расширить основной раздел. Перезагружаемся на SD карту (В принципе можно и работать прямо с nand, но мне не хотелось захламлять систему пока всякими git и make). Я испытываю проблемы с загрузкой с SD карты, похоже какой-то баг нерешенный. Но вроде приноровился вставлять карту при перезагрузке (см. предыдущий пост). Далее:
# apt-get install git
# git clone git://github.com/linux-sunxi/sunxi-tools
# cd sunxi-tools
# apt-get install libusb-1.0-0-dev make pkg-config
# make
# make install

Далее, запускаем скомпилированную программу nand-part и читаем, что пишут:
"Как видим мы хотим объединить 2 и 3 раздел в один, поэтому новая часть 2 радела is 4194304 + 10584064 = 14778368. "

# ./nand-part -f a20 /dev/nand 32768 'boot 131072' 'rootfs 14778368'

Перезагружаемся без SD карты.

# resize2fs /dev/nandb
# df -h
Filesystem      Size  Used Avail Use% Mounted on
rootfs          7.0G  866M  5.9G  13% /
Отлично, у нас 7 гигабайт!

Память

С памятью вроде проблем нет (dmesg):

Memory: 448MB 1536MB = 1984MB total

Светодиодная индикация

1 голубой включено
2 оранжевый cpu0 (загрузка)
3 белый cpu1 (загрузка)
4 зеленый mmc0 (обращение)


init.d Скрипт отключения/включения светодиодов в cubietruck:

#!/bin/bash
case "$1" in
    start)
        echo 0 > /sys/class/leds/orange:ph20:led2/brightness
        echo "Switching off Orange LED"
        echo 0 > /sys/class/leds/blue:ph21:led1/brightness
        echo "Switching off Blue LED" 
        echo 0 > /sys/class/leds/white:ph11:led3/brightness
        echo "Switching off White LED" 
        echo 0 > /sys/class/leds/green:ph07:led4/brightness
        echo "Switching off Green LED" 
    ;;
    stop)
        echo 1 > /sys/class/leds/orange:ph20:led2/brightness
        echo "Switching on Orange LED"
        echo 1 > /sys/class/leds/blue:ph21:led1/brightness
        echo "Switching on Blue LED" 
        echo 1 > /sys/class/leds/white:ph11:led3/brightness
        echo "Switching on White LED" 
        echo 1 > /sys/class/leds/green:ph07:led4/brightness
        echo "Switching on Green LED" 
     ;;
    *)
        echo "Usage: /etc/init.d/leds {start|stop}"
        exit 1
    ;;
esac


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

19 декабря 2013 г.

Установка Debian на Cubietruck (cubieboard 3) на внутреннюю память (NAND)

Пришел мне cubietruck. На нем для тестов предустановлен android 4. Мне графика ни к чему, да и сервер на андроиде бестолковая затея. Выяснив, что на данный момент  Cubian толком не поддерживает Cubietruck пришлось выбирать из рекомендованного программного обеспечения. В итоге выбор пал на  Lubuntu Server (официальный сайт).

В процессе поиска, я наткнулся на порт debian для cubietruck для SD Card. Но он нам пригодится не для установки, а для работы с NAND. Можно взять Cubiuntu (пароль по умолчанию linaro\linaro).

1. Скачиваем образ по ссылкам.
2. Скачиваем Win32 Disk Imager  (делал под виной)
3. Вставляем sd карту или флешку, и пишем на нее образ. После старта, ищет загрузчик сначала на MicroSD карте, если не находит его, тогда на внутреннем накопителе, и в конце на USB флешке.

Пару команд на заметку:

locale-gen ru_RU.UTF8
dpkg-reconfigure locales


Устанавливаем lubuntu в nand  с помощью LiveSuitPack (или PhoenixSuit1.0.6.rar for Windows). Нажимаем кнопку FEL вставляем miniUSB в компьютер. Отпускаем FEL после включения. Для чего это надо? С этим образом загрузочные разделы, в которых я честно говоря пока не особо шарю, поэтому проще будет сделать так.

Далее, узнаем ip адрес устройства, заходим по ssh или подключаем клавиатуру.

Дальше у меня возникла проблема с этим портом для sd-карты.  Многие кубитраки идут с багой - невозможно загрузить  систему с SD карты. Испробовав разные комбинации обнаружил что если нажать ресет, а потом сразу вставить карту - система грузится! Потом нашел и другое решение - загрузить андроид с nand, и перезагрузится из него.

Попробуем скопировать другой загрузчик (собранный для lubuntu) в карту (не в раздел,а  прямо в устройство).

dd if=u-boot-sunxi-with-spl-ct-20131102.bin of=$card bs=1024 seek=8

В итоге в данном виде загрузка с SD карты у меня происходит без проблем.
Черт, вчера работало, сегодня нет, необъяснимо. :)

Форматируем после перезагрузки основной раздел, где лежит ОС (опять же загружаемся с sd карты пока).

mkfs.ext4 /dev/nandb


Раздел /dev/nanda трогать, как тут смысла я думаю нет. Плюс в /dev/nand уже тоже есть загрузочный сектор, который уже подготовлен.

Установка.

rootfs, устанавливаем стабильный debian.

apt-get update
apt-get install debootstrap
mount /dev/nandb /mnt/rootfs
debootstrap --verbose --arch armhf --variant=minbase --foreign stable /mnt/rootfs http://ftp.debian.org/debian
chroot /mnt/rootfs
/debootstrap/debootstrap --second-stage


cat <<END > /etc/apt/sources.list
deb http://ftp.debian.org/debian/ stable main contrib non-free
deb-src http://ftp.debian.org/debian/ stable main contrib non-free
deb http://ftp.debian.org/debian/ stable-updates main contrib non-free
deb-src http://ftp.debian.org/debian/ stable-updates main contrib non-free
deb http://security.debian.org/ stable/updates main contrib non-free
deb-src http://security.debian.org/ stable/updates main contrib non-free
END

apt-get update
export LANG=C
apt-get install apt-utils dialog locales
dpkg-reconfigure locales
# Выбираем en_US.UTF-8 или что сами хотите.
export LANG=en_US.UTF-8
apt-get install dhcp3-client udev netbase ifupdown iproute openssh-server iputils-ping wget net-tools ntpdate ntp vim nano less tzdata console-tools module-init-tools mc

cat <<END > /etc/network/interfaces
auto lo eth0
allow-hotplug eth0
iface lo inet loopback
iface eth0 inet dhcp
END

echo cubietruck > /etc/hostname
 
cat <<END > /etc/fstab
# /etc/fstab: static file system information.
#
#                
proc /proc proc defaults 0 0
/dev/nandb / ext4 noatime,errors=remount-ro 0 1
END

passwd
/etc/init.d/ntp stop

exit


Выключаем. Вытаскиваем SD карту. Включаем.  Должно работать, но сети у меня пока нет.  :) Исправлю - напишу.
Продолжение "настройка debian на cubietruck".
Еще пару экспериментов для памяти (которые мне не помогли):

под дескопной Виндой или Линуксом (од нашу АРМ систему не пойдет):
wget http://dl.cubieboard.org/software/a20-cubieboard/lubuntu/cb-a20-lubuntu-server-13.06-v1.00.img.gz
wget "http://forum.xda-developers.com/attachment.php?attachmentid=1740401&d=1361198061" -O "imgRePacker_204.zip"
gunzip cb-a20-lubuntu-server-13.06-v1.00.img.gz
unzip imgRePacker_204.zip
chmod 777 imgrepacker
./imgrepacker cb-a20-lubuntu-server-13.06-v1.00.img.gz

Заливаем через scp bootloader.fex

dd if=/usr/src/bootloader.fex of=/dev/nanda
это извлечение из nand образа загрузчика (какого-то уровня). imgrepacker отсюда.

18 декабря 2011 г.

Аналог who lock me в windows 7

Была такая утилита, who lock me, которая замечательно работала в windows xp. Утилита позволяет узнать какой процесс блокирует файл или папку в windows. К сожалению, в windows seven она уже не работает.

Поэтому есть альтернатива: Unlocker.

Также на сайте указана табличка с альтернативами, может вам хватит Process Explorer от Sysinternals или LockHunter от Crystal Rich.

6 ноября 2010 г.

musicmans.ru | Как сделать сайт на Django | GWT

Посмотрел я на дерево жанров и оно мне не понравилось. Страшное, неудобное. И решил сразу заняться клиентской стороной. Тем более у нас есть отличнейший повод!

Итак, настроим gwt. Скачиваем eclipse 3.6 для java.
Далле переходим на страницы с загрузками GWT. Ставим gwt плагин для eclipse.

Создаем проект File > New > Web Application Project.

Название: genre
package: ru.musicmans

Запуск - Debug As > Web Application.

Переходим по адресу, устанавливаем плагин. Все работает.

Устанавливаем GWT Designer. Читаем quick start.

Далее можно конвертировать, созданный проект из gwt plugin (gwt plugin мы поставили потому, что в нем все равно находиться сам gwt) в gwt java project, который идет с дизайнером (правой кнопкой на проекте - convert to) или создать новый:



Сразу создадим модуль ru.musicmans.genre.GenreTree:



Итак, проект создан. Открываем genre.java и кликаем на вкладку Design.



Что нам надо для организации передачи данных? Мы не можем использовать rpc call gwt, так как у нас на стороне сервера django. Что делать в данном случае? Я рассматривал данный вопрос около года назад. Итог такой: возможен вывод данных в темплейте и преобразование их в javascript object, но это не очень оптимальный путь, тем более, что приложению обычно нужны данные в процессе работы (в том числе, обновленные). Поэтому лучшим решением мне предоставляется REST (с помощью этого подхода не надо проходить новый путь создания интерфейсов сервисов). Я решил не использовать SmartGWT, слишком он навороченный. В чистом GWT нет поддержки REST, поэтому воспользуемся Restlet Framework, ну а со стороны django - django-piston.

Скачаем Restlet Framework, Edition for Google Web Toolkit. Установим (укажем java build path в свойствах проекта).

Django-Piston

Теперь перейдем к серверной стороне. django-piston я уже как-то упоминал. Так вот, устанавливаем:

>c:\Python26\Scripts\pip.exe install hg+http://bitbucket.org/jespern/django-piston@c4b2d21db51a#egg=piston

Читаем документацию. Создадим приложение api, пропишем (r'^api/', include('api.urls')), в главном urls.py. Создадим в приложении файлы urls.py и handlers.py. Остальные файлы, кроме __init__.py можно удалить.
handlers.py:

from django.core.urlresolvers import reverse

from piston.handler import BaseHandler#@UnresolvedImport

from genre.models import GenreDirStyle#@UnresolvedImport

class GenreHandler(BaseHandler):
allowed_methods = ('GET', )
fields = ('name', 'type', 'url' )
model = GenreDirStyle

@classmethod
def url(self, genre):
return reverse('genre_genre', args=[genre.id])

def read(self, request, genre_id):
genre = GenreDirStyle.objects.get(id=int(genre_id))
return genre

urls.py:

from django.conf.urls.defaults import *

from piston.resource import Resource#@UnresolvedImport

from api.handlers import GenreHandler#@UnresolvedImport

genre_resource = Resource(handler=GenreHandler)

urlpatterns = patterns('',
url(r'^genre/(?P[^/]+)/$', genre_resource, name='api_genre_id'),
)


Переходим по адресу, например http://localhost:8000/api/genre/3/ :

{
"url": "/genre/id/4/",
"type": 3,
"name": "Prog-Rock"
}

Чтобы открывать application/json в firefox, установите дополнение, а еще лучше используйте RESTClient для Firefox.

Вернемся к клиентской части.

Добавим widget дерево в gwt приложение tree:






Запускаем отладку, кстати, сразу рекомендую изменить параметр logLevel в конфигурации отладки.

С помощью firebug видим, что ответа на запрос по адресу http://localhost:8000/api/genre/3 не увенчались успехом, поэтому вспоминаем про проблему SOP.

В процессе поиска решений наткнулся на django-crossdomainxhr-middleware.py, позволяющее использовать кроссдоменные запросы (требуется firefox > 3.5).



Но мы пока его использовать не будем.

Итак, проблему SOP при разработке решим следующим образом. Отключим Jetty сервер, запускаемый в отладке, а также укажем порт 8000.


Сделаем символическую ссылку с директории war проекта на www\media\static\gwt\genre, под windows, например, так:

www\media\static\gwt>mklink /d genre d:\path\to\gwt\war\

Далее, можно отлаживать приложение по адресу примерно такому (предварительно запустив веб-сервер отладки django) http://localhost:8000/media/static/gwt/genre/GenreTree.html?gwt.codesvr=127.0.0.1:9997 адресу. Параметр gwt.codesvr обязателен при отладке gwt приложения.

А еще лучше создать темплейт django, подключив к нему приложение gwt примерно следующим образом. Создаем div с id='genreTreeEntryPointId' в темплейте, а в gwt root panel определяем следующим образом:
RootPanel rootPanel = RootPanel.get("genreTreeEntryPointId");

Теперь мы можем отлаживать gwt приложение прямо в "окружении" django-проекта, например, по адресу такому http://localhost:8000/genre/tree/?gwt.codesvr=127.0.0.1:9997&genre_id=2 .

Серверный и клиентский код выкладывать слишком много, остановимся на нюансах:

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

treeqs = GenreDirStyle.objects.raw("""SELECT t2.*
FROM genre_genredirstyle AS t1
LEFT JOIN genre_genredirstyle AS t2
ON t2.lft BETWEEN t1.lft AND t1.rgt
WHERE t1.lft < %s AND t1.rgt > %s AND t1.tree_id = 1 AND t2.depth-1 = t1.depth AND t2.tree_id = %s
ORDER BY t2.lft;""", (genre.lft, genre.rgt, genre.tree_id))

Построение дерева решил возложить на плечи клиентов.
- При загрузке приложения выводим div с изображением, который заменяется самим приложением, после его загрузки, а также выполнения всех ajax запросов.
- Настройки приложения выводим как JSON объект в javascript, получая значения которого в gwt приложении:

private final Dictionary paramsDict = Dictionary.getDictionary("gwtGenreParameters");
String paramsDict.get("API_GENRES_MAIN_URL");

- Автоматический разбор JSON ответа. Используем данное приложение.
Оно зависит от: google-gin и totoe (погуглите и подключайте в проект).
- Для обозначения состояния элемента дерева используем своей объект класса (сокращенный вид):

private class GenreTreeItemData
{
private int id;
private Boolean alreadyLoaded = false;
private String description;
}

используя функцию setUserObject(Object).
- При создании проекта, в настройках gwt приложения наследуется стиль по умолчанию gwt standart. Так вот, проблема в том, что в нем есть правила css, переопределяющие наши (в том числе body). Решить это можно двумя способами, вот первый, а можно просто удалить нежелательные строки из standard.css файла в директории gwt\standard\ (их там немного и они вначале).
- Для генерации документации по API используем вид from piston.doc import documentation_view.

После работы все как обычно и результат:



ps. На стороне сервера попробовал использовать Aptana 3.0, там действительно отменная поддержка Django темплейтов в PyDev (но наткнулся на баг, ctrl+space вешает IDE, может только у меня так?).

15 сентября 2010 г.

musicmans.ru | Как сделать сайт на Django | Жанры, направления, стили

Прошу прощения за долгое отсутствие.
Пишем следующее приложение. Я решил, что жанры, направления и стили; инструменты; композиции; исполнители будут у нас отдельными приложениями, потому что планирую, что они обрастут серьезной функциональностью.

Начнем с жанров, направлений и стилей. Создадим приложение genre, сразу создаем модель жанра.
Чтобы создать модель, нам надо определиться, что такое жанр собственно? К сожалению, в русском интернете, большая путница и мешанина из жанров, стилей и направлений. Нет ни исследований, ни достаточно устоявшихся критериев. Прочитав эту заметку начал задумываться структуре систематизации жанров и стилей. И главное, неплохо было бы найти уже существующую отлаженную структуру (Amazon). После долгих поисков (amg, amazon, mp3.com, discogs) остановился на варианте от amg.

Итак, напишем модель жанра, направления и стиля и заполним их структурой.

Так как я один, и у меня нет редакторов, то пришлось воспользоваться результатом чужих трудов и спарсить структуру жанров, направлений и стилей с amg. Надеюсь они на меня за это не в обиде.

Код приводить не буду, расскажу что использовал lxml, а также окружение проекта для записи жанров/стилей.

После того как данные в базе, сделаем initial data для приложения:

>python manage.py dumpdata genre > apps\genre\fixtures\initial_data.json

Теперь попробуем вывести дерево жанров/стилей.

На данном этапе понимаем, что вывод "детей" стилей сулит нам лавинообразные запросы к базе данных (select_related нам тоже не поможет, он не срабатывает для моделей с полями отношения ForeignKey с null=True ( father = models.ForeignKey('self', verbose_name=_(u'Родитель'), related_name='child_dirs_styles', null=True) ) ), поэтому воспользуемся приложением для хранения деревьев в базе данных django-treebeard (документация).

C:\>c:\Python26\Scripts\pip.exe install git+git://github.com/tabo/django-treebeard@8eb52a4f4274615e86a7572a8bab39b79d718b88

Добавляем 'treebeard' в INSTALLED_APPS. Если вы используете админку, то настройка немного посложней.

Воспользуемся моделью хранения деревьев Nested Sets. Не стоит пугаться последней ссылки, тем то и хорошо приложение treebeard, что за нас уже решен вопрос хранения деревьев в SQL базе. Нам лишь стоит воспользоваться набором функций.

Смотрим нашу модель:

# -*- coding:utf-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from treebeard.ns_tree import NS_Node #@UnresolvedImport

GENRE_DIR_STYLE = (
(0, _('Музыка')),
(1, _('Жанр')),
(2, _('Направление')),
(3, _('Стиль')),
)

class GenreDirStyle(NS_Node):
name = models.CharField(max_length=1000, verbose_name=_(u'Title'))
name_ru = models.CharField(max_length=1000, verbose_name=_(u'Название'), blank=True, null=True)
type = models.IntegerField(choices=GENRE_DIR_STYLE)
description = models.CharField(max_length=10000, verbose_name=_(u'Описание'), blank=True)

class Meta:
ordering = ["name"]
verbose_name = _(u'Жанр, направление, стиль')


Пробуем, работает ли миграции South с treebeard:

./manage.py schemamigration genre --auto

получаем сообщение следующего вида

? The field 'GenreDirStyle.lft' does not have a default specified, yet is NOT NULL.
? Since you are adding or removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice:

South нас просит указать обязательное значение по умолчанию. Нажимаем 2, и значение 0.

Как создать дерево? Просто:
1. Создаем корень дерева.
pop_music = GenreDirStyle.add_root(name = "Популярная музыка", type = 0)#оно сразу сохраняется save()
2. Создаем жанр:
pop_music.add_child(name = "Rock", type = 1)

И здесь сталкиваемся с проблемой, в случае парсинга (см. выше) html и создания базы "на лету". Поэтому читаем basic-usage внимательней и переписываем примерно так:

>>> get = lambda node_id: Category.objects.get(pk=node_id)
>>> root = Category.add_root(name='Computer Hardware')
>>> node = get(root.id).add_child(name='Memory')
>>> get(node.id).add_sibling(name='Hard Drives')

>>> get(node.id).add_sibling(name='SSD')

>>> get(node.id).add_child(name='Desktop Memory')

>>> get(node.id).add_child(name='Laptop Memory')

>>> get(node.id).add_child(name='Server Memory')


Не забудем обновить json данные, после изменения и миграций моделей.
Для вывода дерева жанров используем функцию get_tree.


# -*- coding: utf-8 -*-
from annoying.decorators import render_to#@UnresolvedImport
from django.shortcuts import get_object_or_404

from models import GenreDirStyle

@render_to('genres/genre_tree.html')
def genre_tree(request):

pop_genre = GenreDirStyle.objects.get(name="Популярная музыка", type=0)
classic_genre = GenreDirStyle.objects.get(name="Классическая музыка", type=0)

pop_tree = GenreDirStyle.get_tree(pop_genre)
classic_tree = GenreDirStyle.get_tree(classic_genre)

return {
'pop_tree': pop_tree,
'classic_tree': classic_tree
}

@render_to('genres/genre_genre.html')
def genre_genre(request, genre_id):

genre = get_object_or_404(GenreDirStyle, id = int(genre_id))

return {
'genre': genre
}

На данном этапе django-toolbar показывал замечательные 5 запросов за 13 мс. А вот общая генерация страницы занимала 6573.00 ms. Это очень долго, хотя при выключенном debug режиме ощутимо быстрее. Все упирается в рендеринг. Проэтому применим кеш в темплейте (на шесть часов, например):

{% load cache %}
{% cache 21600 pop_tree_chache %}
{% for node in pop_tree %}
{% include "genres/genre_node.html" %}
{% endfor %}
{% endcache %}

А также включим на время (позже настроим memcached на сервере) кеширование в память, в settings/common.py:

CACHE_BACKEND = 'locmem:///'

Темплейты интуитивно понятны, покажу лишь темплейт жанра, включаемый в цикл вывода дерева.

{% load dj_tags %}


(Ужасно, blogger все сломал, смотрите здесь)
Обратите внимание на фильтр multiply и substract. Это не стандартные фильтры django, а написаные в нашем приложении dj_tags.

# -*- coding: utf-8 -*-
from django import template
register=template.Library()

@register.filter(name='multiply')
def multiply(value, arg):
return int(value) * int(arg)

@register.filter(name='subtract')
def subtract(value, arg):
return int(value) - int(arg)

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

Ну, окончание, как обычно, тесты, мерж, развертывание.




ps. Не забудем обновить django (>c:\Python26\Scripts\pip.exe install --upgrade Django и прописать в requirements.txt)!

7 августа 2010 г.

musicmans.ru | Как сделать сайт на Django | Пользователи. Личные сообщения. Уведомления

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

#pip install git+git://github.com/robhudson/django-debug-toolbar
Смело ставим последнюю версию, ибо если что-то сломается, то на машине разработчика это не критично.
development.py:

INSTALLED_APPS += (
'django.contrib.admin',#tests
'debug_toolbar',
)

MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)

INTERNAL_IPS = ('127.0.0.1',)


Обновляем localhost:


Поинтересуйтесь содержимым пунктов меню, там много интересного.

Итак, личные сообщения. Опять все написано за нас. Устанавливаем из транка (кстати, вот консольный svn клиент для windows):

>pip.exe install svn+http://django-messages.googlecode.com/svn/trunk/@141#egg=django_messages

141-я ревизия транка на данный момент, как написано на странице проекта это будущая версия 0.5, совместимая с Django 1.2 (как раз то, что нам нужно). Не забываем requirements.txt (кстати, я подумал, что неплохо было бы сначала добавлять строчку в requirements.txt, а ставить основываясь на файле. Надо только использовать одну директорию с кешом pip, чтобы он постоянно не скачивал дистрибутивы. Ставить установленные приложения по новой он не будет, но мы будем уверены, что не забудем прописать все приложения в requirements.txt и не испытаем проблем на сервере с сайтом).

Документация по настройке. Тут кстати возникает путаница, здесь приложение указано как django_messages, а в документации используется messages, так что используем django_messages.

Изменим темплейты.

Скопируем из приложения и изменим под свои нужды.



В документации к приложению читаем, что оно использует django-mailer и django-notification, если они установлены. Первое у нас есть, ставим второе (хорошее приложение, пригодится в будущем).
>pip.exe install git+git://github.com/jtauber/django-notification@3f023adf0ce2eafcee744904e2c358792f253721@egg=notification

Пропишем 'django_messages', 'notification' в приложения, настроим url.py. Синхронизируем базу.

Просмотрим таблицу notification_noticetype, в ней должны находиться оповещения о работе с личными сообщениями. Приложение notification рассмотрим ниже.

Как только мы видим список чего бы то ни было (список входящих сообщений), сразу рождаются мысли о пагинации. И как не удивительно :-) , django поддерживает ее из коробки, а чтобы было совсем просто установим приложение django-pagination.

>pip.exe install git+git://github.com/ericflo/django-pagination@47e7ec874cd7dddda5ed13ffb6993a64dced2537

Настраиваем. Добавляем css разметку.

А также переведем пару строк, так как в приложении нет русской локализации, djutils\locale\ru_RU\LC_MESSAGES\django.po:
msgid "previous"
msgstr "назад"

msgid "next"
msgstr "вперед"

Не забудем скомпилировать.

в темплейтах с сообщениями добавляем (с месторасположением самостоятельно, 2-ка для теста):

{% load pagination_tags %}
{% autopaginate message_list 2 %}
{% paginate %}


В итоге получается:


Чтобы пользователи не были у нас безликими, давайте организуем им вывод профайлов.
В user/url.conf

url(r'^profile/(?P\d+)/$', users_views.userprofile, name='users_profile'),

(блогспот пытается закрыть тег /userprofile_id самостоятельно, конечно это в коде не требуется)
Вид:

@login_required
@render_to('users/user_profile.html')
def userprofile(request, userprofile_id):

request_user = get_object_or_404(User, id=int(userprofile_id), is_active=True)

return {
'request_user' : request_user,
}

Отмечу, что получать надо user, а не наш UserProfile, который еще может быть не создан.

Соответственно, напишем темплейт.



django_messages\templates\notification\ переведите вручную, перевод, который идет с приложением не работает (устарел наверное). Скопируем темплейты в users/templates, создадим папку locale в приложении и запустим создание файла перевода:
>python C:\Python26\Lib\site-packages\django\bin\django-admin.py makemessages -e .html,.txt --locale=ru_RU
в директории users.
После редактирования скомпилируем.

Темлейт для уведомлений notices.html для самостоятельного написания (можно подсмотреть в pinax).

Что такое notification? Это приложение для уведомлений. Когда происходит событие в системе, мы имеем возможность создать уведомление для пользователя, с возможностью настройки дополнительных рассылок уведомлений:

* при логине на сайт.
* по почте (настраивается пользователем).
* по rss

Подключим контекстный процессор "notification.context_processors.notification".

Мне не понравилась часть приложения по работе с url. Поэтому скопировал приложение себе в проект и поправил под свои нужды, получилось примерно следующее:



Пишите сообщения. :)

3 августа 2010 г.

musicmans.ru | Как сделать сайт на Django | Пользователи. Дополнительная аутентификация

В прошлый раз мы остановились на том, что начали создавать приложение users. Давайте создадим openid аутентификацию на сайте.
Я обещал писать бекенды для django-registration, но оказалось существует замечательное приложение для всевозможных видов аутентификации с последующим созданием пользователя и авторизации.

* OpenID - yandex.ru, rambler.ru, yahoo.ru, google.com
* OAuth - twitter.com
* OpenAPI - Вконтакте.ру
* FacebookConnect - facebook.com

Устанавливаем:
c:\Python26\Scripts\pip.exe install hg+http://bitbucket.org/offline/django-p
ublicauth@7371e8f71be1#egg=django-publicauth

Прописываем в requirements.txt.
Добавляем 'publicauth' в INSTALLED_APPS. Добавляем 'annoying.middlewares.RedirectMiddleware' в MIDDLEWARE_CLASSES.

Добавим 'publicauth.PublicBackend' в AUTHENTICATION_BACKENDS.

Запускаем syncdb.

OpenID

Необходимо установить python-openid (2.2.5).

Добавим
(r'', include('publicauth.urls')),
в файл /users/urls.py , тем самым переопределив url login и logout приложения нашими url'ами, и оставив остальные, необходимые для работы приложения. Ознакомимся с содержимым темплейта login.html, и добавим следующую форму в наш login.html:


Openid URL




Создадим директорию publicauth в users/templates/ , скопируем туда содержимое и поправим под наш сайт.
Также в файле users/forms.py создадим форму ExtraForm (форма для заполнения дополнительных полей после сторонней аутентификации, например имя пользователя).
Подсмотреть можно здесь.

PUBLICAUTH_EXTRA_FORM = "users.forms.ExtraForm"

Пробуем логиниться.

Приложение требует установленного и настроенного messages framework, как указано в документации (для самостоятельного рассмотрения) для вывода сообщений. У меня уже оно почти настроено (сообщение при сохранении профиля реализованно как раз через него).

Сообщения к сожалению не переведены, придется сделать это самим.

Создадим файл src/djutils/locale/ru_RU/LC_MESSAGES/django.po и переведем файл.

msgid "To complete registration, check your email and activate your account"
msgstr "Для завершения регистрации проверьте e-mail и активируйте учетную запись"

msgid "We are sorry, but registration is disabled. Come back later"
msgstr "Извините, но регистрация закрыта"

msgid "Please fill openid url field"
msgstr "Пожалуйста, заполните поле openid"

msgid "Your authentication provider returned bad response, please try again"
msgstr "Ваш провайдер аутентификации вернул влохой ответ, попробуйте еще раз"

msgid "You have cancelled OpenID authentication"
msgstr "Вы отменили аутентификацию по OpenID"

msgid "OpenID authentication failed. Reason: %s"
msgstr "Аутентификация OpenID провалилась. Причина: %s"

msgid "You have successfully logged out"
msgstr "Вы успешно вышли"

msgid "Your existing account was merged with new authentication account"
msgstr "Существующая учетная запись была объединена с новой учетной записью"

msgid "Your account is not activated. Please activate it first."
msgstr "Ваша учетная запись не активирована. Пожалуйста, активируйте ее сначала."

msgid "You have successfully authenticated"
msgstr "Вы успешно аутентифицированны"

msgid "Invalid response received from facebook server, please start the authentication process again"
msgstr "Неверный ответ от сервера facebook, пожалуйста запустите процесс аутентификации еще раз"

msgid "Invalid response received from OpenID server, please start the authentication process again"
msgstr "Неверный ответ от сервера OpenID, пожалуйста запустите процесс аутентификации еще раз"

msgid "Invalid response received from vkontakte server, please start the authentication process again"
msgstr "Неверный ответ от сервера vkontakte, пожалуйста запустите процесс аутентификации еще раз"

Скомпилируем (запуск в директории src\apps\djutils\)
>python C:\Python26\Lib\site-packages\django\bin\django-admin.py compilemessages.

Добавим 'django.middleware.locale.LocaleMiddleware', в MIDDLEWARE_CLASSES.

ВКонтакте

Делаем все как по ссылке на хабр. Единственно, я не понял для чего нужно VKONTAKTE_API_KEY, работает и без него, да и в бекенде vkontakte.py он не используется.

Facebook

Делаем по мануалу, ссылка на документацию на facebook.

В настройки надо добавить
FACEBOOK_PROFILE_MAPPING={ 'name': 'username', }

OAuth (Twitter)

Install python-oauth:
>pip.exe install oauth

Настройки:

TWITTER_CONSUMER_KEY = "key"
TWITTER_CONSUMER_SECRET = "secret"
TWITTER_REQUEST_TOKEN_URL = "https://twitter.com/oauth/request_token"
TWITTER_ACCESS_TOKEN_URL = "https://twitter.com/oauth/access_token"
TWITTER_AUTHORIZE_URL = "https://twitter.com/oauth/authorize"
TWITTER_API_URL = "http://twitter.com/users/show.json?user_id=%s"
TWITTER_PROFILE_MAPPING = { 'screen_name': 'username', }

Темплейт:




В итоге, в url auth_login мы должны иметь авторизацию на сайте или аутентификацию в сторонних сайтах в случае неавторизированного пользователя, а также аутентификацию в сторонних сайтах и сопоставления этих профилей с текущим профилем. И, естественно, логин любым из методов для существующего пользователя.

Как обычно, запускаем тесты, мержим транк, делаем развертывание, и сравниваем musicmans.ru.



ps. По поводу тестов. Да все уже написано:
http://djangotesting.com/
http://habrahabr.ru/blogs/django/91471/
http://pyobject.ru/blog/2009/09/13/django-external-test-tools/
http://night-fairy-tales.com/2009/10/django-eclipse.html

Разбираемся, пишем (На данный момент нами написано лишь редактирование профиля в приложении users, вот для него и можно написать тесты).

pps. Насткнулся на баг в тестах django-registration.
Вот решение (developmet.py)

TEST = False
manage_command = filter(lambda x: x.find('manage.py') != -1, sys.argv)
if len(manage_command) != 0:
command = sys.argv.index(manage_command[0]) + 1
if command < len(sys.argv):
TEST = sys.argv[command] == "test"

if TEST:
LANGUAGE_CODE = 'en-us'