NSDataのエンコーディングを変更する

XPathQuery.h[m]に渡すNSDataはUTF-8でないといけないようで、Shift-JISのページをダウンロードしたNSDataを渡したら怒られてしまった。そこでNSDataのエンコーディングを変更する方法。
以下のサンプルはShift-JIS -> UTF-8の変換。NS***StringEncodingを適宜変えることで他のエンコーディングにも対応できる。

NSString *str = [[NSString alloc] initWithData:responseData encoding:NSShiftJISStringEncoding];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];

Qt(Mac)でOpenCVをインクルードする

少しはまったのでメモ。OpenCVMacPortsからインストール済みです。

方法

.proファイルに以下を追加。

LIBS += -L/opt/local/lib/ \
    -lopencv_core \
    -lopencv_imgproc \
    -lopencv_highgui

INCLUDEPATH += /opt/local/include
DEPENDPATH += /opt/local/include

必要に応じてライブラリを追加。Terminalで

cd /opt/local/lib/
ls | grep opencv

とかやればOpenCVのライブラリの一覧が取得できます。

djangoのログインフォームで「ログイン情報を保持」の有無を選択できるようにする

django標準のログインフォーム(確かAuthenticationForm)ではブラウザをとじたあともログイン状態を保持するかを選択できません。実際のサイト構築ではこの機能が必須になってくるかと思います。

スコトプリゴニエフスク通信さんを参考にしました。
pinaxというdjangoをベースにしたフレームワーク?のコードが引用されています。

私の場合、ログインにusernameを使わず、emailで行っているので強引に工夫します(usernameはプレースホルダが書きこまれています)

forms.py

class LoginForm(forms.Form):
    email = forms.EmailField(label=_("Email"), max_length=30)
    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput(render_value=False))
    remember = forms.BooleanField(label=_("Remember Me"), help_text=_("If checked you will stay logged in for 3 weeks"), required=False)

    user = None

    def clean(self):
        if self._errors:
            return
        
        try:
            u = User.objects.get(email=self.cleaned_data["email"])
        except User.DoesNotExist:
            raise forms.ValidationError(_("The username and/or password you specified are not correct."))
        
        user = authenticate(username=u.username, password=self.cleaned_data["password"])
        if user:
            if user.is_active:
                self.user = user
            else:
                raise forms.ValidationError(_("This account is currently inactive."))
        else:
            raise forms.ValidationError(_("The username and/or password you specified are not correct."))
        return self.cleaned_data

    def login(self, request):
        if self.is_valid():
            login(request, self.user)
#            request.user.message_set.create(message=_(u"Successfully logged in as %(username)s.") % {'username': self.user.username})
            if self.cleaned_data['remember']:
                request.session.set_expiry(60 * 60 * 24 * 7 * 3)
            else:
                request.session.set_expiry(0)
            return True
        return False

views.py

def login(request):
    if request.user.is_authenticated():
        # すでにログイン済み
    
    if request.method == 'POST':
        form = LoginForm(data=request.POST)
        if form.is_valid():
            if form.login(request):
                # ログイン成功
            else:
                # ログイン失敗
    else:
        form = LoginForm()

    return render_to_response('accounts/index.html', {
        'form': form,
    })

簡単ですね。formのclean()はview側でis_valid()を呼ばないと実行されないので注意。

djangoappengineでBlobstoreを用いてファイルアップロード・ダウンロードを実装する

djangoappengineでファイルアップロード、ダウンロードを実装するにはfiletransferを使うのが基本だと思いますが、このサンプル通りに実装すると、アップロード時の動きがGoogle App Engineに隠蔽されまくってて、謎の挙動がいっぱいあります。(単純に私のスキルに起因するものかと思いますが)

そこで、ちょっと工夫してみたいと思います。

モデルにフィールドを追加する

ファイルを保持させたいモデルにフィールド追加します。例えば、

class UserProfile(models.Model)
    profile_photo = models.FileField(upload_to='p', blank=True)

といったように。upload_toは適当で良いと思いますが、指定しておかないとどっかでエラーしたような気がします。

アップロードを実装

viewのファイルを編集します。アップロードされたファイルはrequest.FILES['profile_photo']といった形で取得できます。
このファイルを以下の関数でブロブストアに保存します。
(※2011/06/20現在、この方法はExperimental(実験的)とされています。使うかどうかは各個人の責任としてください)

from __future__ import with_statement #ファイルの先頭に記述する必要あり
from google.appengine.api import files

def _upload_to_blobstore(file):
    file_name = files.blobstore.create(mime_type='image/jpeg')
    with files.open(file_name, 'a') as f:
        for chunk in file.chunks():
            f.write(chunk)
    files.finalize(file_name)
    return files.blobstore.get_blob_key(file_name)

戻り値でBlob Keyが返ってきます。これはダウンロードの際に必要になるので、データベースに保存します。

bk = _upload_to_blobstore(request.FILES['profile_photo'])
# upがUserProfileのインスタンスだとすると
up.profile_photo = str(bk)

up.profile_photoはFileFieldですが、内部的にはBlobKeyが文字列で保存されているだけなので、問題なく動作します。

ダウンロードを実装

ここもAppEngineのAPIを直接たたこうかと思いましたが、あまり上手くいかなかったので、filetransferを使います。
ダウンロードに関してはサンプル通りに実装すれば問題ありません。

def profile_photo_download_handler(request, user_pk):
    profile = get_object_or_404(UserProfile, user__pk=user_pk)
    if profile.profile_photo:
        return serve_file(request, profile.profile_photo)
    else:
        return HttpResponseRedirect('/static/images/default_profile_photo.png')

こんな感じで。
適当に解説してきましたが、不明な点、質問がありましたら、コメントしてください。答えられる範囲で答えます。

VirtualBox + UbuntuでSSH

ポートフォワーディングの設定

VirtualBox 4.0登場、NATポートフォワーディングとエクステンション導入 | エンタープライズ | マイコミジャーナル
VirtualBox4からGUIでポートフォワーディングの設定が可能。親2222->子22のように。

UbuntuでCUI起動をデフォルトにする

[http://labs.zsrv.net/2009/11/ubuntucui%E3%81%A7%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B/:title=[Ubuntu]CUIで起動するように設定を変更する | Labs Zsrv Net]
この記事を参考にしました。ただ、現在のUbuntuには/etc/inittabはなく、/etc/event.d/rc-defaultを変更する必要があります。
【Ubuntu】ランレベルの変更 - チラシの裏

今日の開発で参考になったサイト

自分の備忘録として、書いておきます。

あるモデルから他のモデルを扱いたいときはClassRegistry::init()を使う

以下のように。

<?php
$User = ClassRegistry::init('User');
$allUsers = $User->find('all');

CakePHPのグローバル関数

array_merge(), htmlspecialchars()などよく使う関数はCakePHPのグローバル関数として用意されています。
CakePHP グローバル関数 | Sun Limited Mt.