Tuesday, January 20, 2015

TomatoUSB minidlna problem when rebooting the router

There is a problem with TomatoUSB (or other routers running minidlna Media DLNA Server) which causes the files.db overwritten each time the rooter is rebooted.

This looks like a problem with mounting usb disk, which happens after the minidlna daemon is run.

I have managed to get my minidlna running quite "stable".

1. Remove dlna startup from services and make it run after the usb drivers are mounted.
The may appare a problem with USB disk with multiple partitions.

You can run "minidlna -f /etc/minidlna.conf" on disk mount, but what if your minidlna database is located on the disk that is not mounted as the first one?

Here's the script that examines your minidlna.conf script and checks if media_dir and db_dir are available after each patitions is mounted.

minidlna_conf=/etc/minidlna.conf
minidlna_ready=false
minidlna_required_dirs=`awk -F "/" '/^db_dir|^media_dir/ {out="/"$2; for(i=3;i<=NF;i++){out=out"/"$i};print out}' $minidlna_conf`

for dir in $minidlna_required_dirs
  do
    if [ -d $dir ]; then
      minidlna_ready=true
    else
      minidlna_ready=false
    fi
done

if [ "$minidlna_ready" = true ]; then
  minidlna -f $minidlna_conf
fi

The script will run minidlna if media_dir(s) and db_dir exist. (are mounted).

Put this script in "USB and NAS -> USB Support -> Run after mounting form field.

In the window "Run after remounting" simply put "service dlna stop" or "killall minidlna".

2. Once you generate your config file, by temporarily enabling minidlna in USB and NAS -> Media Server make sure it is saved on the the disk of your router. It will be lost after reboot - remember you won't run minidlna with other services which means the config file won't be generated.

In order to save your config there are 2 ways:
a) saving the config on your /jffs/ partition. For me this was not possible, as my WNR3500Lv1 router cound not mount this partition (too low space.. didn't check this actually).
b) saving the config file in nvram by running command from ssh command line:

nvram setfile2nvram /etc/minidlna.conf

Remember to resave this file if you change it.

Also remember, that there is no much space in nvram.


Tuesday, August 5, 2014

Removing libwdi drivers

Just a quick tech note.

I needed to root my android phone and change of usb drivers was needed.
Like many of other folks, I've used the libwdi package with a "Zadig" tool for manipulating the drivers. Yeah.. I've accidentaly change the driver for my Logitech Mouse and from that moment it didn't work anymore.

The zadig author points that restoring the original windows signed driver is easy as uninstalling the device using the "Device Manager" with "Remove driver files.." option checked.
It didn't work for me as I manipulated the drivers using Zadig couple times and in my opinion the option mentioned above restores drivers to the previous one.. libusb-wind32 - libusbK - libusb-win32 and so on in my case.

So, if  you are trying to remove libwdi installed USB drivers (libusbK, libusb-win32, winusb) with zadig, and it returns error you can try the pnputil.exe tool. It's included with windows.

It's easy to achieve what you want with:

1. List all 3rd party inf packages by:
# pnputil.exe -e > somefile.txt

2. Then look for "libwdi" string in somefile.txt
Example:

Published name :            oem134.inf
Driver package provider :   libusbx.org
Class :                     Urządzenia uniwersalnej magistrali szeregowej
Driver date and version :   06/02/2012 6.1.7600.16385
Signer name :               USB\VID_046D&PID_C019 (libwdi autogenerated)

Now all you need is the command below for reach entry found:

# pnptuil.exe -d oem134.inf

PS. Make sure the devices using the drivers above are not connected and that you running commands with admin priveleges assigned. (command prompt in admin mode).


Friday, May 16, 2014

Disney Cars 2 game problem windows 8

Maybe this solution will work for you ?

My son loves the Disney Cars 1 & 2 games. Well.. he loves Disney Cars movies as well :) This game is not supported on windows 8 (especially running on Intel HD4000 graphic card) but it works.. It hangs randomly but we can play. Suddenly I noticed that it no longer runs.
The process starts and crashes after 10 seconds. Using a grat AppCrashView app I found that the process loads a Bitdefener antivirus module which I have installed 2 days before we tried to play Cars again.

Here's the line:

LoadedModule[4]=C:\Program Files\Bitdefender\Antivirus Free Edition\avc3\avc3_sig_241\avcuf32.dll

After uninstalling the antivirus the game works fine. Yeah!

Though I love Bitdefender I have to admit that I can't use it with Cars game. Back to Avast.
Sorry. Maybe this is not your fault Bitdefender. Maybe the problem related to other games. Who knows.

Hope you find this post and it will save your time. Have a nice gaming!

Friday, May 24, 2013

Notification and confirm service using angular-ui bootstrap, phonegap and AngularJS

I've been playing with AngularJS lately when developing a new application using a Phonegap framework.

Phonegap is a framework which lets you create mobile apps for many platforms by just using HTML5 CSS and Javascript. These won't be as fast as native apps, but it makes developing faster.. I don't want to talk about Phonegap advantages and disadvantages here.

In my mobile applicatin I use alerts and confirmations windows many times.
Ripple - chrome extension that lets you emulate Phonegap someway doesn't provide confirmation windows (as far as I know) so I had to write my own fallback code for using native phonegap notifications on phone and html based notifications. For the second, I've used a great project angular-ui bootstrap which has a dialogs built in.

The code is a AngularJS service. I won't write here how AngularJS is great and awesome. Just look at the website and find yourself using this great framework. I think that Angular service is the right place to put this into work.

I should also mention  that I am using a great angular-phonegap-notification.

My intention was to make my service work similar to Phonegap notification API.


Wednesday, February 6, 2013

Restricting django to one user concurrent session only

Here's the tiny code that helps you avoid multiple users logged using the same account.
# -*- coding: utf-8 -*

from django.conf import settings
from django.core.cache import cache, get_cache
from django.utils.importlib import import_module


class UserRestrictMiddleware(object):
    def process_request(self, request):
        """
        Checks if different session exists for user and deletes it.
        """
        if request.user.is_authenticated():
            cache = get_cache('default')
            cache_timeout = 86400
            cache_key = "user_pk_%s_restrict" % request.user.pk
            cache_value = cache.get(cache_key)

            if cache_value is not None:
                if request.session.session_key != cache_value:
                    engine = import_module(settings.SESSION_ENGINE)
                    session = engine.SessionStore(session_key=cache_value)
                    session.delete()
                    cache.set(cache_key, request.session.session_key, 
                              cache_timeout)
            else:
                cache.set(cache_key, request.session.session_key, cache_timeout)

# vim: ai ts=4 sts=4 et sw=4
Hope you like it.
Remember, to put UserRestrictMiddleware somewhere after Session Middleware in MIDDLEWARE_CLASSES (settings.py)

Friday, September 21, 2012

Django cache templatetag with dynamic backend parameter

I've found a nice project django-adv-cache-tag.

From the project website:

With django-adv-cache-tag you can :
  • add a version number (int, string, date or whatever, it will be stringified) to you templatetag : the version will be compared to the cached one, and the exact same cache key will be used for the new cached template, avoiding keeping old unused keys in your cache, allowing you to cache forever.
  • avoid to be afraid of an incompatible update in our algorithm, because we also use an internal version number, updated only when the internal algorithm changes
  • define your own cache keys (or more simple, just add the primary key (or what you want, it's a templatetag parameter) to this cache key
  • compress the data to be cached, to reduce memory consumption in your cache backend, and network latency (but it will use more time and cpu to compress/decompress)
  • choose which cache backend will be used
  • define {% nocache %}...{% endnocache %} blocks, inside your cached template, that will only be rendered when asked (for these parts, the content of the template is cached, not the rendered result)
  • easily define your own algorithm, as we provide a single class you can inherit from, and simply change options or whatever behaviour you want, and define your own tags for them
I am using template fragments caching a lot, but some parts of website need invalidating depending on products type etc. 
I am invalidating cache on save & delete signals for model.

Here's the example:
Say there are 5 types of products and your website includes listing for them (which needs lots of data processing etc) and you want to cache it.


from django.db import models

PRODUCT_TYPE_CHOICES = (
('type1','type2'),
('type2','type2'),
('typen','typen'),
)

class Product(models.Model):
    attr1 = ...
    attrn = ...
    type =  models.CharField("Product Type", max_length=6, choices=PRODUCT_TYPE_CHOICES)
Using one backend, modifying product of type1 will purge the entire cache backend. Set up more backends:
# settings.py

CACHES= {
    'templates_products_type1': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
        },
    'templates_products_type2': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11212',
        },
}
You can also use Redis for caching, which requires you to run one redis instance with multiple databases. Let's create our own {% my_cache %} templatetag with cache backend name as a first vary_on parameter. (this is the quickest way as you can also use get_pk() or even add your own parameters to {% my_cache %} templatetag. You could also use fragment name but it's not resolvable. Let's get to the code:
#my_cache_tags.py
from adv_cache_tag.tag import CacheTag
from django.core.cache import get_cache
from django import template

register = template.Library()

class MyCacheTag(CacheTag):
    def get_cache_object(self):
        backend = self.vary_on[0]
        return get_cache(backend)
MyCacheTag.register(register, 'my_cache')
Here's the template code:
# assuming products_type variables is assigned
{% load my_cache_tags %}

{% with backend="templates_products_"|add:products_type %}
{% my_cache 0 products backend other_vary_on_variables %}
{% for product in product_list %}
{{ product }}
{% endfor %}
{% endmy_cache %}
When saving or deleting products of some type, just purge template cache for produdcts only of this specified type.
from django.core.cache import get_cache
#function run on model save / delete

def clear_products_template_cache(type):
    cache_name = "templates_products_" + type
    cache = get_cache(cache_name)
    cache.clear()
NOTE: This is a SIMPLIFIED solution. I just wanted to point out that you can easily use different backends for one template cache tag. It's your job to make some additional processing code (especialy get_cache_object() for selecting cache backend etc.

Monday, September 17, 2012

Kantor Alior Banku i tajemniczy licznik

W taryfie prowizji i opłat bank wielokrotnie opiera się na wartości "licznika", do wartości którego można
przelewać walutę za darmo. Jeśli nie chcesz czytać całego wpisu, to proszę bardzo.

Możesz wymienić (w tym kantorze) nawet 10 tyś. franków szwajcarskich i nie zapłacisz prowizji za przelew.

Dalej moje boje z kantorami...
W związku z koniecznością wymiany waluty, przy złodziejskim spreadzie walutowym (a więc różnicy w cenie kupna i sprzedaży waluty) postanowiłem poszukać lepszego rozwiązania.

Jako pierwszy przetestowałem serwis inkantor.pl. Jest to jednak kantor, który nie podlega nadzorowi KNF.
Następnym kantorem internetowym był gocash.pl. Jestem z niego zadowolony, a kurs wymiany franka
był jeszcze korzystniejszy.

Obecnie kurs sprzedaży CHF:

3,3717 - gocash
3,3819 - inkantor
3,3666 - kantor alior


W obu przypadkach przelew walutowy zlecony do mbanku trafiał tego samego dnia, o ile zlecony został przed godziną 11ą. Było to nieco problematyczne, gdyż wymiany waluty chciałem dokonywać o godz: 14:50, z której to mbank pobiera kurs do wymiany złotówek na franki w dniu płatności raty za kredyt,
a ja nie chcę bawić się w spekulanta.

Dodam jeszcze, że w przypadku inkantor.pl pobierano ode mnie opłatę ~5 zł za przelew, zaś gocash.pl takiej opłaty nie pobierał.
Może później zrobię porównanie tych serwisów.

Obecnie czas przyszedł na kantor aliorbanku http://kantor.aliorbank.pl
Kursy walut są jeszcze korzystniejsze w moim przypadku. Nie jest to znaczna różnica, gdyż w przypadku gocash.pl te kursy również były bardzo korzystne.

Przelew waluty (nawet w trybie ekspresowym) jest darmowy, ale "do wartości licznika". Co owa wartość oznacza?
Jest to wartość, która powiększa się przy każdej wymianie waluty.
Pierwsza myśl, jaka przychodzi do głowy, to "ile wynosi górna wartość licznika", powyżej której naliczana jest opłata ?
W internecie krążą różne wartości, np. 600 CHF (dla franka szwajcarskiego). Autor tej tezy informuje więc,
że nie opłaca się wymieniać więcej niż 600 franków, gdyż przelanie ich będzie już kosztowne.

Ciekawy tego zagadnienia zacząłem szukać wartości granicznej licznika i niestety nie udało mi się jej znaleźć.
Pani na infolini potwierdziła, że nie ma takiej granicy.

Chodzi o to, by nie wpłacać franków w kasie i korzystać z darmowych przelewów za pośrednictwem tegoż kantoru. Można więc śmiało przelewać każdą wartość waluty, którą za pośrednictwem kantoru wymieniamy.
Pani na infolinii wspomniała też o innych typach zleceń, ale nie to jest tematem tego wpisu.
Najczęściej z kantoru korzystać będzie zwykły zjadacz chleba, który wymienia walutę do swojego kredytu hipotecznego oszczędzając dobre kilkadziesiąt złotych na racie.

Saturday, September 15, 2012

KNF razem z rządem i bankami w jednym tańcu

Nie jest to referat na temat działalności KNF. Są to tylko moje przemyślenia (być może czasami pozbawione sensu), które zmuszą kogoś mądrzejszego do napisania "mądrego referatu", który skłoni co niektórych do zastanowienia się nad polityką tej instytucji i jej roli w życiu Polaków.

 
Komisja Nadzoru Finansowego, powstała w 2006r. instytucja mająca chronić klientów instytucji podlegających pod ten nadzór (a więc np. banki, czy też instytucje ubezpieczeniowe, domy maklerskie itd.) Czy jest jednak tak, że instytucja ta chroni tych najsłabszych ? W moim przekonaniu tak nie jest. Większość wydawanych przez tą instytucję rekomendacji w rzeczywistości negatywnie odbija się na tych najbardziej skubanych przez owe banki - klientów z zaciągniętymi kredytami hipotecznymi. Nie tylko poprzez swoją politykę ogranicza ona dostępność kredytów, ale też zmierza ku wbiciu kolejnej szpili tym najsłabszym. Jak czytać można za niezalezna.pl
Komisja Nadzoru Finansowego zobowiązała banki do prześwietlenia nieruchomości zabezpieczających udzielone przez nie kredyty. Powstanie swoisty spis inwentaryzacyjny nieruchomości kredytowanych w Polsce. Powodem tej gigantycznej akcji są spadające wciąż ceny nieruchomości oraz coraz większa groźba niewypłacalności kredytobiorców.
Artykuł zacznę takim powyższym sformułowaniem i przejdę do grzechów KNFu, które wg. mnie popełnił (świadomie lub nie).

Zanim przejdę dalej pozwolę sobie opisać rynek mieszkaniowy w ostatnich latach. Mam kolegów, którzy w okolicach roku 2003r. zaciągnęli kredyt hipoteczny we franku szwajcarskim. Kilka lat później (a mieli czas do lipca 2008r.) spłacając lub też sprzedając swoją nieruchomość mogli nieźle zarobić.

Stało się tak ponieważ kurs franka w 2003r. oscylował w granicach 3 zł, zaś w lipcu 2008r. wartość jego spadła do 1,99 zł.
Dodatkowo zadziałał tutaj sprytnie wykorzystany przez deweloperów program "Rodzina na swoim", który sztucznie napompował - wspomaganą chęcią zakupu przez osoby urodzone w latach wyżu demograficznego, czyli ~8x - bańkę na rynku nieruchomości. Marże deweloperów kształtowały się nawet w okolicach 50%!

Dzisiaj mamy franka po około 3,5 zł, gdzie jeszcze niedawno wartość jego wynosiła 4 zł.
Mamy również dużą część "umoczonych", których wartość nieruchomości jest znacznie poniżej wartości kredytu ze względu na:
1. Wzrost wartości franka szwajcarskiego do polskiego złotego.
2. Spadek cen nieruchomości. Bańka musiała pęknąć i tak się też dzieje.

Efektem takiej sytuacji jest kolejny atak na bezbronnych klientów maszyny bankowej, ale o tym dalej.

Mamy rok 2008, gdzie kurs franka szwajcarskiego spada do wartości 1,99 zł.
Ciężko w telewizji usłyszeć o niebezpieczeństwie zawierania kredytów hipotecznych w tej walucie.
Nawet nasz "sprawny" KNF nie widzi przeszkód w udzielaniu takich kredytów. Przypomnę, że jest to instytucja działająca od 2006r.


Banki nie dość, że udzielały kredyty we franku na potęgę, to dodatkowo jeszcze namawiały obecnych zadłużonych w złotówkach do przewalutowania swoich kredytów na franka szwajcarskiego.
Demon kusił niższymi ratami, bo przecież frank jest nisko, a oprocentowanie też niskie.
Osobiście dostawałem sygnały o telefonach, gdzie namawiano do tak idiotycznego kroku.

Przypomnę jeszcze raz. KNF milczał.
Korzystając z okazji, udałem się do mojego banku celem przewalutowania kredytu z franka szwajcarskiego na złotówki. Moje doświadczenie techniczne w zakresie analizy technicznej pozwalało na wyciągnięcie takich wniosków. W banku stanęło nade mną 4 panów w białych koszulach, którzy mocno namawiali mnie do odstąpienia od tego kroku. Nie dałem się jednak przekonać i wymusiłem na nich umożliwienie mi
złożenia takiej dyspozycji.
W swojej bezsilności, otrzymałem kawałek czystej kartki A4, gdzie napisać miałem dyspozycję.

Odpowiedź na moją dyspozycję otrzymałem po 2,5 miesiącu, gdy kurs franka poszybował w górę.
Dodatkowo warunki nowej umowy były nieciekawe. Proszę uwierzyć, że do dzisiaj banki mają
totalną swobodę w ilości czasu, jaki potrzebują na rozpatrzenie wniosku o przewalutowanie.
Nie jest to określone w regulaminie (bynajmniej mojego banku), a sam UOKiK nie widzi w tym nic dziwnego.

Wiemy zatem już, że KNF nie widział w kredytach walutowych nic złego w 2008r. Wiem także,
że wydał później bankom rekomendacje, które znacznie ograniczają możliwość udzielania kredytów hipotecznych w walucie obcej. Kiedy tak mocno zmieniło się podejście KNFu do takich kredytów?

Stało się to w 2010r., gdy kurs franka szwajcarskiego oscylował w granicach 2,8-3 zł, a więc było 33% wyższy, niż w lipcu 2008r, gdy tak wciskano ludziom kredyty w tej walucie. Ktoś, kto brał kredyt w wysokości 300 tyś. zł, do spłaty ma dzisiaj do spłaty ponad 400 tyś. zł.

Jak wiadomo, dla klienta najlepszym rozwiązaniem jest zaciągać kredyt walutowy, gdy wartość waluty obcej jest wysoka. Zmniejsza to znacznie (choć nigdy nie eliminuje) ryzyko związane ze wzrostem wartości
tego kredytu dla zarabiających w złotówkach. W złotówkach bowiem albo kredyt jest spłacany, albo kupowana jest waluta celem spłaty raty.

Jest to szwindel jeszcze większy niż obecne Amber Gold. Mamy rok 2012 i kredytobiorcy stawiani są często w sytuacji krytycznej spowodowanej:
1. Wzrostem wartości kredytu - spowodowane wzrostem wartości franka szwajcarskiego CHFPLN (i nie tylko, ale jako mój przypadek z tą walutą piszę artykuł wzorując się na franku).
2. Spadkiem cen nieruchomości wywołanym pęknięciem sztucznie napompowanej bańki, na której zyskali głównie deweloperzy dzięki wyżowi demograficznemu oraz programowi "Rodzina na Swoim".

Oba powyższe punkty sprawiły, że banki czują się mniej zabezpieczone i żądają nowych zabezpieczeń kredytu. Tutaj wystosuję kilka pytań:
1. Czy istnieje jeszcze pojęcie "ryzyko kredytowe". Jeśli tak, proszę o odpowiedź, na czym ono polega.
Obecne czasy pokazują, że nawet spekulacyjne banki mogą liczyć w ostateczności na podatników, gdyż są "za duże, żeby upaść" (ale o tym innym razem).
2. Czy banki przy udzielaniu kredytu nie zawarły z klientem umowy,  w której to obie strony świadome są podejmowanego ryzyka?
3. Czy żaden pan w białej koszuli (zwany analitykiem) w 2008r. nie przewidywał tego, co stanie się po 2010r. z rynkiem nieruchomości?

Jestem pewien, że doskonale wiedziały. Na polu walki pozostał osamotniony Kowalski, od którego żądać się będzie wykupienia dodatkowego ubezpieczenia kredytu. Mało jeszcze bankom zysków, które z roku na rok rosną. Trzeba wyciągać z Kowalskiego ile wlezie.

W przypadku odmowy wykupienia takiego dodatkowego - często bardzo kosztownego - ubezpieczenia jest wypowiedzenie kredytu przez bank. Stawia do klienta w sytuacji, w której musi od razu spłacić cały kredyt.
Jeśli to nie poczyni, następuje egzekucja z majątku i jego sprzedaż. Chodzi oczywiście o mieszkanie.
Różnica między kwotą pozyskaną ze sprzedaży mieszkania i wartości kredytu dalej ciąży na kliencie.
Pomijam tutaj niemałe koszty całego postępowania, które również obciążą klienta.

Do realizacji tego diabolicznego planu brakuje jednak jeszcze jednej rzeczy, ale o tym dalej.

Gdzie zatem jest KNF?

KNF nie próżnuje i leci z odsieczą. Komu?  Z pewnością nie Kowalskiemu.
Jak wyczytać możemy, instytucja ta zamierza "wymusić" na bankach, by stworzyły szczegółową bazę nieruchomości oraz jej wartości - włączając w to aspekty, czy też wydarzenia mające wpływ na wartość danej nieruchomości.

KNF tłumaczy to potrzebą monitorowania rynku kredytów. Ja jednak widzę inne dno (a nawet dwa) tego ruchu.

1. Bankom niełatwo jest egzekwować wykup dodatkowego ubezpieczenia. Wymaga do często kosztownego wykonania wyceny nieruchomości w sytuacji, gdy klient od razu nie ulega żądaniu banku.
Bank sam sobie ustala taką wartość. Klient, który nie z nią się nie zgadza, musi dokonać wycenę (a więc za nią zapłacić). Nie zawsze to jednak bankowi wystarcza, ale to inna kwestia. Na odsiecz idzie KNF, który zapewne za jakiś czas ustali wartości z tej przecudownej bazy za "wiążące" i znacznie ułatwi to egzekwowanie od biednych owieczek wykupu ubezpieczeń.

2. Od długiego czasu mówi się o podatku katastralnym. Byłby to niezły zastrzyk gotówki dla zrujnowanego na własną prośbę budżetu. Na drodze stoi jednak niemożność nałożenia takiego podatku, gdyż niełatwe jest
dokonanie wyceny nieruchomości, na podstawie to której naliczony ma być kataster.
Słyszy się pomysłach naliczania podatku w zależności od odległości od centrum miasta itd., ale raczej szybko zdają sobie rządzący sprawę, jak idiotyczne są to pomysły. Z resztą niebawem uzyskają pomoc powstałej w 2006r. instytucji.

Czy ważna jest afera Amber Gold w cieniu tej sprawy?. Bohater jednej z reklam rzekłby "pikuś", przy całej tej aferze związanej z rynkiem nieruchomości oraz udzielonymi kredytami i związanymi z tym działaniami instytucji finansowych, a także publicznych.

Mam nadzieję, że z czasem zacznie się o tym mówić coraz głośniej i zawita to na żółtych paskach w telewizji
informacyjnych.

Póki co życzę Wszystkim niskiego kursu waluty obcej, szczególnie franka szwajcarskiego.
Od gilotyny uratował wszystkich umoczonych szwajcarski bank centralny, "usztywniając" kurs franka do euro na poziomie 1.2. Gdyby nie ten ruch, zapewne zobaczylibyście niedawno "5kę" przy kursie tej waluty do polskiego złotego.

Wiem, że życzenie to nie spodoba się to coraz liczniejszej rzeszy polskich imigrantów, ale nie oszukujmy się - i tak nie macie zamiaru wrócić do kraju :-) więc tak mocno nie stracicie, bo nie będzie potrzeby przewalutowania swoich franków, funtów, koron norweskich na złote.

Eksporterzy jakoś sobie poradzą :) CHF w końcu to nie EUR :)

Umoczeni sprzedadzą nieruchomość, spłacą kredyt, albo po prostu go przewalutują i przestaną być narażonymi na skubanie przez banki spekulantami.

Tuesday, June 19, 2012

Django verbose_name in your template

There are many situations when you want to display model field verbose_name in template when displaying tables and other data. Ideally you would put this value in header section of your table for DRY purposes.
The problem is that it's unable to use model _meta in templates. You can achive the same using an example verbose_name_tags.py templatetag.
# verbose_name_tags.py

from django import template

register = template.Library()

def get_field_verbose_name(instance, arg):
    return instance._meta.get_field(arg).verbose_name
register.filter('field_verbose_name', get_field_verbose_name)

def get_queryset_field_verbose_name(queryset, arg):
    return queryset.model._meta.get_field(arg).verbose_name
register.filter('queryset_field_verbose_name', get_queryset_field_verbose_name)

you can get your header names rendered the DRY way.
Assuming your model looks like this:
class Product(models.Model):
    name = models.CharField(_(u"Name"), max_length=20)
    weight = models.PositiveIntegerField(_(u"Weight"))

Your template would be similar to the one below:
{% load verbose_name_tags %}

<table>
  <thead>
  <tr>    
    <th>{{ object_list|queryset_field_verbose_name:"name" }}</th>
    <th>{{ object_list|queryset_field_verbose_name:"weight" }}</th>
  </tr>
  </thead>
  <tbody>
{% for product in object_list %}    
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
{% endfor %}
</table>

What if you need sorting ?
For some time I've been using a django-sorting for sorting my tabular data.
The new code
{% load sorting_tags %}
{% autosort object_list %}

<table>
  <thead>
  <tr>    
    {# _("Name").. need to specify the display name all the time.. bad.. #}
    <th>{% autosort 'name' _("Name") %}</th>
    <th>{% autosort 'weight' _("Weight") %}</th>
  </th></tr>
  </thead>
  <tbody>
{% for product in object_list %}  
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
{% endfor %}
  </tbody>
</table>

Great, but this is not DRY! You can do this the other way. Note: I think {% autosort %} should take verbose_name by default (maybe will take a look into the code later, but for now we can achieve the same using a {% with %} and verbose_name_tags.py template tags.
{% load sorting_tags %}
{% load verbose_name_tags %}

{% autosort object_list %}

{% endfor %}
<table>
  <thead>
  <tr>
    {% with name=object_list|queryset_field_verbose_name:"name" %}    
    <th>{% autosort 'name' name%}</th>
    {% endwith %}
    {% with weight=object_list|queryset_field_verbose_name:"weight" %}
    <th>{% autosort 'weight' weight %}</th>
    {% endwith %}
  </tr>
  </thead>
  <tbody>
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
  </tbody>
</table>

Now we can just change verbose_name in models.py and we don't have to worry about the table headers.
You can also use a "field_verbose_name" for model instances:
{# assuming {{ product }} is an instance of Product model #}
{% load verbose_name_tags %}

{{ product|field_verbose_name:"name" }}: {{ product.name }}

Some might wonder why didn't I simply use one templatetag name which would identify if it checks for queryset or instance field. I decided to use different names to quickly see If I act with querysets or instances. Hope you like it.

Thursday, June 14, 2012

Twitter Bootstrap button radius (rounded corners) size

Twitter Bootstrap is a great tool from Twitter. It's a CSS framework (using lesscss) that lets you create your own CSS stylesheets easily.

In one of projects I needed to decrease the size of buttons radius, which is too huge by default (~5px).

Thanks to .border-radius() mixin you can change button radius.

Here's the code I put in my own .less file.
You can put this in bootstrap.less (where necessary imports are included) file and recompile.

The file below changes radius to 1px.

@new_radius: 2px;

.dropdown-menu {
        .border-radius(0px);
}
.btn {
        .border-radius(@new_radius);
}
.btn-group > .btn {
  .border-radius(@new_radius);
}
.btn-group > .btn:first-child {
        .border-radius(@new_radius 0 0 @new_radius);
}
.btn-group > .btn:last-child,
.btn-group > .dropdown-toggle {
        .border-radius(0 @new_radius @new_radius 0);
}
.btn-group > .btn.large:first-child {
        .border-radius(@new_radius 0 0 @new_radius);
}
.btn-group > .btn.large:last-child,
.btn-group > .large.dropdown-toggle {
        .border-radius(0 @new_radius @new_radius 0);                                                                         
}
.btn-group > .dropdown-toggle:first-child {
        .border-radius(@new_radius);
}
.btn-group > .btn-large:only-child,
.btn-group > .btn:only-child {
        .border-radius(@new_radius);
}

Hope you find it useful!

Wednesday, April 11, 2012

Clearly and IReader - Safari Reader in your Chrome and Firefox

If you are a Safari user and use the "reader" function you will find such functionality missing in Google Chrome / Firefox.

IReader @ Chrome

Safari Reader is a nice builtin function that makes reading long articles easy. It removes banners so the article looks more like reading a plain book.
There are 2 extensions I found nice.

1. IReader (Chrome / Firefox )
2. Clearly (Chrome / Firefox )


Clearly @ Chrome
Try both. Haven't read the code, but seems like IReader parses the HTML code on site load what makes site loading slower (it's just my assumption). When IReader is available for the page, new icon appears in omnibox.
Clearly icon is always visible in the extensions box.

Yeah.. I'm a strong chrome extensions opponent.. it makes Chrome slooower.





Tuesday, March 27, 2012

Gunicorn + Nginx - a much better way to deploy your Django website

I've been a lighttpd + FastCGI django user for a long time.
Now there's a better solution -> gunicorn.

What is a gunicorn?
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.
If you are a fastcgi user, please check gunicorn asap. I'm sure you will get much more from your hardware :-)
Ps: Note that you get the most if database is not the bottleneck. That's why you should use cache everywhere if possible.

Here's my script for gunicorn + daemontools for supervising:
#!/bin/sh

# SITE_PATH is a directory where settings.py is stored
SITE_PATH=/var/www/django/projects/my_project/; export SITE_PATH
USER=www-data
TCP=127.0.0.1:8888

exec setuidgid $USER /usr/bin/gunicorn_django -b ${TCP} -w 3 --max-requests=1000 ${SITE_PATH}settings.py 2>&1

Seems like wsgi is also recommended with the new 1.4 version of Django
If you’re new to deploying Django and/or Python, we’d recommend you try mod_wsgi first. In most cases it’ll be the easiest, fastest, and most stable deployment choice.
Check django site for more information.

UPDATE:
Forgot to mention that I've switched from lighttpd to Nginx.
You can use the script above with the following nginx config:
server {
    listen   xx.xx.xx.xx:80;
    server_name domain.com www.domain.com;
    root /var/www/django/projects/my_project/public;
    access_log /var/log/nginx/domain-access.log;

    rewrite ^/admin(.*) https://domain.com/admin$1 permanent;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /media {
        root /var/www/django/projects/my_project/django/contrib/admin;
    }
    location /static {
        root /var/www/django/projects/my_project/site_media;
        access_log off;
    }
    location /site_media {
        root /var/www/django/projects/my_project;
    }
    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-For  $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 10;
        proxy_pass http://127.0.0.1:8888/;
    }

}

There's a trick you need to apply to get the real ip of the request source as seen in the line:
proxy_set_header X-Forwarded-For  $remote_addr;

Here's a code I'm using:
class XForwardedForMiddleware(object):
       def process_request(self, request):
        if "HTTP_X_FORWARDED_FOR" in request.META:
            ip = request.META["HTTP_X_FORWARDED_FOR"]
            if ip.startswith('::ffff:'):
                ip = ip[len('::ffff:'):]
            request.META["REMOTE_ADDR"] = ip
            request.META["REMOTE_HOST"] = None

Add XForwardedForMiddleware to your settings.py MIDDLEWARE_CLASSES setting.

Friday, March 2, 2012

Django HTML5 date input widget with i18n support and jQueryUI DatePicker fallback

Here's my manual for creating a HTML5 form date input with jQueryUI Datepicker and multilingual support.

What we need is:
1. django-floppyforms
2. jQueryUI Datepicker + jQuery 3. Modernizr javascript library
and of course - Django
You need to have some basic python, django & jquery knowledge.
Make sure multilingual support is enabled:
USE_I18N = True

and request context available for template
#settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
  # ...
  'django.core.context_processors.request',
  # ...
)
In your base template, you can use LANGUAGE_CODE variable.
<html lang="{{ LANGUAGE_CODE }}">

There are 3 ways to get this running

1. Totally django way - prividing django form & widget with the "lang" variable"

Your calendar widget needs to gain information about the language, so you need to pass a variable to your form, which then sends this variable to the widget.
def add_view(request):
    
    form = SomeForm(data = request.POST, lang=request.LANGUAGE_CODE)
    if form.is_valid():
        do_some_stuff()

from django import forms
from common.widgets import DatePicker

class SomeForm(forms.ModelForm):
    data = forms.CharField()
    when = forms.DateField()

    def __init__(self, *args, **kwargs):
        lang = kwargs.pop('lang')
        super(SomeForm, self).__init__(*args, **kwargs)
        
        self.fields['when'].widget = DatePicker(lang=lang)
import floppyforms as forms
from django.conf import settings

class DatePicker(forms.DateInput):
    template = "common/datepicker.html"

    def __init__(self, *args, **kwargs):
        self.lang = kwargs.pop('lang')
        super(DatePicker, self).__init__(*args, **kwargs)
    
    def get_context(self, name, value, attrs):
        ctx = super(DatePicker, self).get_context(name, value, attrs)
        ctx['lang'] = self.lang
        ctx['STATIC_URL'] = settings.STATIC_URL
        return ctx
The below, could be attached to python render() method in the widget, but I think this is more elegant way, and thanks to floppyforms we get native HTML5 date widget if available. I am aware of that floppyforms has an example of html5 datepicker, but the way the floppyforms author checks for date type doesn't seem to work - that's why I've used modernizr.
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       $(function () {
         $.datepicker.setDefaults($.datepicker.regional['{{ lang }}']);
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>

What the above code does is check if
<input type="date" />
is available. If not, it runs the jqueryUI calendar widget.
The javascript attached is to the specified selector. I've decided to go this way as I needed the calendar widget on ajax loaded content.
This is a Django (well.. not the sortest way).

2. Javascript way. Less django form messing.

You can achieve the similar effect without prividing both SomeForm & DatePicker widget with the lang variable.
import floppyforms as forms

class DatePicker(forms.DateInput):
    template = "common/datepicker.html"
Let the form know the widget it should use
from django import forms
from common.widgets import DatePicker

class SomeForm(models.Model):
    data = forms.CharField()
    when = forms.DateField(widget=DatePicker)
In the widget template code, you need jQuery for language detection.
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       
         $(function () {
         var lang = $('html').attr('lang');
         $.datepicker.setDefaults($.datepicker.regional[lang]);
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>
but I'm affraid this might not work on ajax loaded content where you need your calendar widget.

3. Django way, without messing the widget code too much.

Another way could be simply providing the form with data-lang attribute. The form would then look as follows:
from django import forms
from common.widgets import DatePicker

class SomeForm(forms.ModelForm):
    data = forms.CharField()
    when = forms.DateField()

    def __init__(self, *args, **kwargs)
        lang = kwargs.pop('lang')
        super(SomeForm, self).__init__(*args, **kwargs)

        self.fields['when'].widget = DatePicker(attrs={'data-lang': lang})
and then in the datepicker.html
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       $(function () {
         $.datepicker.setDefaults($.datepicker.regional[$('#{{ attrs.id }}').attr('data-lang'));
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>

The 2 ways above have no {{ STATIC_URL }} in widget template available. You need to get it yourself, via templatetag, or simply using hardcoded url.

Hope someone gets some tips from this manual

There for sure are many other ways, feel free to share them. Remember to include javascript modernizr, jQuery and jQuery files!

Thursday, February 9, 2012

Google Chrome slow? Disable extensions and get the real speed

I'm a Google Chrome fan since early versions. With every new version it consumed more memory. Today, after upgrading it to v. 17 I have decided to try Firefox v.10 What I saw was faster site rendering! Though FF seems to be less responsive (especially when switching tabs) sites were loading faster. It also  consumed less memory.

Finaly, I have disabled all chrome extensions with --disable-extensions switch. Viva Chrome! It's fast again!

If your chrome gets slower, disable extensions. You won't get fast page loads with extensions enabled.

Here's the extension list I was using:

  • Last Pass, 
  • Page Rank, 
  • Gmail Checker+, 
  • Firebug Lite, 
  • Smooth Gestures, 
  • RSS Finder, 
  • FlashBlock (that's the most suspicious one regarding the page load imho).
Question is "Is Google Chrome worth using without some of these extensions?" ...

Edit: You can also speed up Chrome with RAMDisk .
In my Chrome shortcut I added flags as follows:

--disk-cache-size=26214400  --media-cache-size=26214400 --disk-cache-dir=g:/ --purge-memory-button

This limits cache size to ~26 Megabytes.





Wednesday, February 1, 2012

Django language set in the database field applied on login

In one of my Django projects available only for logged in users I needed to set language setting in the database, so user can change the language setting in his profile.

Here comes a handy "user_logged_in" (Django >= 1.3) signal which lets us do things after user is logged in. In our case, we modify session and change the language.

My user_details.models.py:
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.conf import settings

class UserProfile(models.Model):
    user = models.OneToOneField(User, blank=Flse)
    language = models.CharField(verbose_name=_(u"Interface Language"), 
                   max_length=4, choices=settings.LANGUAGES)

@receiver(user_logged_in)
def lang(sender, **kwargs):
    lang_code = kwargs['user'].userprofile.language
    kwargs['request'].session['django_language'] = lang_code


settings.py

gettext = lambda s: s

LANGUAGES = (
    ('pl',gettext('Polski')),
    ('en',gettext('English')),
    ('de',gettext('Deutsch')),
)

Sunday, July 5, 2009

django-sorting a generic way of queryset sorting


Just found a great project django-sorting. It's a generic way of queryset sorting.
It's based on middleware, and templatetag.

Please read the README.txt of the project.

In couple easy steps you get sorting:

1. Download & install django-sorting
2.In your settings.py
- add 'django-sorting' to INSTALLED_APPS
- add 'django_sorting.middleware.SortingMiddleware' to MIDDLEWARE_CLASSES
(make sure 'django.core.context_processors.request' is in your TEMPLATE_CONTEXT_PROCESSORS
3. In your template code: (the one below goes directly from README.txt

...
{% anchor first_name "First Name" %}
{% anchor last_name "Last Name" %}
{% anchor creation_date Creation flojax my_container %}
...

The first argument is a field of the objects list, and the second

one(optional) is a title that would be displayed. The previous

snippet will be rendered like this:


...
First Name
Last Name
Creation
...



It's BRILLIANT!


You should also use django-pagination for pagination support written by Eric as well.


Thank you ERIC!

Ps. Big thanks go to Karim (first author of django-sorting code) and other contributtors of these 2 great projects.

Monday, January 26, 2009

[PL] Authinfo i transfer domeny

Postanowiłem przenieść swoje domeny *.pl do innego registrara.

Jak wszyscy wiemy, niedawno NASK obniżył opłaty za przedłużenie domen,
która w przypadku domen *.pl wynosi 40 PLN netto. Obniżka cen miała miejsce niedawno.
Mowa o obniżce cen dla partnerów. Zwykły zjadacz domen dalej płaci 200 PLN netto, a raczej 244 pln brutto.

Jak można się domyśleć, nie przełożyło się to na obniżki cen domen u popularnych u nas firm rejestrujących domeny, jak home.pl itd - cena 99 pln netto (stan na 26.01.2009).

Wiadomo, każdy chce zarabiać.

Wcześniej transferowałem domenę *.com z firmy godaddy. Tam w 10 sekund, po wybraniu opcji "wyślij kod authinfo" otrzymałem emailem kod. Proste i przyjemne.

Niestety nie w Polsce.
Pierw wypełnienie formularza, wpisanie telefonu i powodu, dla którego chcemy authinfo. Można się domyśleć - bo chcę dokonać transferu (nie kłamałem, wpisałem).

Otrzymałem po 2 dniach emaila, z propozycją obniżenia opłaty za przedłużenie ze 100 do 80 pln netto. W emailu załączony był również wniosek. Wypełniłem, podpieczętowałem, przefaxowałem.

Następnie dostałem emailem info, że wniosek przyjęty i że mam zalogować się znowu do innej strony (podano mi login/hasło). Tam się zalogowałem i znowu kolejne 2 wnioski do wypełniania, podpisania, podpieczętowania i przefaxowania wraz dokumentami: NIP, regon, wpis do ewidencji.

Uwaga: wniosek dotyczy tylko jednej z dwóch domen *.pl
Do jednej dostałem już kod authinfo! Widocznie chcą mocnych dowodów, że ja to ja.

Szkoda tylko papieru, drzew. Zarabia na tym chociaż operator telekomunikacyjny,
bo faxy wysyłać trzeba.

Tuesday, January 13, 2009

Ćwicz po pracy!

Uważam, że człowiek może dostać kręćka od monotonnego trybu życia, w skład którego wchodziłyby tylko praca i sen.

Zachęcam każdego gorąco do aktywności ruchowej minimum 2 razy w tygodniu.
Gorąco polecam basen, piłkę nożną oraz sztuki walki :)

Dla mieszkających w okolicy Gdyni polecić mogę gdyńską szkołę TaiChi, oraz KungFu (a także Qigong) - YMAA GDYNIA.

Treningi odbywają się w poniedziałki oraz środy.
W pozostałe dni polecam basen :)

Thursday, October 16, 2008

SAY NO TO FLASH POPUPS!

I've been a Firefox user for a long time.

After Google Chrome has been published I decided to check it out.
After 1 day I had to switch back to Firefox.

The reason?
https://addons.mozilla.org/firefox/addon/10 (Firefox AdBlock plugin)

It's IMPOSSIBLE to watch the web without this plugin.
Flash plugins everywhere ... This is horrible.

People! Use this plugin, do not let manipulate and waste your time on
pages using this harmful type of advertising.

Boycote it! Let others know to not use it!
Admins, block ad hosts too!

I do not mean to fight with ads. I can watch ads, as long as it does not eat 100% of my CPU and some fuckin* flash poup appears! AARGH!!

THIS IS CRAZY!

Thursday, October 9, 2008

django-tables generic way for displaying tables (pagination included)

In thnis short post I would like to let you know about the great project
django-tables.

You can read more about it at:
http://blog.elsdoerfer.name/2008/07/09/django-tables-a-queryset-renderer/

In a couple of words it lets you:
- display the table using one template which lets you sort & paginate easily (filtering in plans!)

By reading the text below you will find out how to create table with sort & pagination quickly.

In usage it's very similar do django newforms (forms as of 1.0)
Example: (views.py)

class MyTable(tables.ModelTable):

  class Meta:
      model = SomeModel

def view_something(request):
  table = MyTable(queryset, order_by=request.GET.get('sort','some_field))

  return render_to_response('templates/table.html', {'table': table, 'rows' : table.rows})
# we need to return table, and  rows separately to use django-pagination (though django-tables provides its own pagination mechanism)

Now let's get to template:

{% load pagination_tags %}
{% autopaginate rows 10 %}



  {% for column in table.columns %}

  {% endfor %}


{% for row in rows %}

  {% for value in row %}

{% endfor %}


{% if column.sortable %}
{{ column }}
{% if column.is_ordered_reverse %}

{% else %}

{% endif %}
{% else %}
{{ column }}
{% endif %}
{{ value }}
{% endfor %}
{% paginate %}


That's it!

This way you get generic way of displaying tables + sorting ability.
By default, django-tables takes field names for table headers when using ModelTable (verbose_name is in plans as well).
You can exclude displaying fields too!

As for now, you can specify your own names in MyTable class.

Let's get back to our class:

class MyTable(tables.ModelTable):
  class Meta:
      model = SomeModel

  field1 = tables.Column(name="Name")
  field_we_want_to_exclude = tables.Column(visiable=Fale, sortable=False)


NOTE:
Django tables is also aware of ForeignKey relations.

Check it out!