Ctrl+Alt+Dell

Strona firmy Dell jest specjalna. Pod koniec października złożyłem tam zamówienie, próbuję zatem ustalić jego aktualny stan. Zwłaszcza, że pieniądze zdążyli już wziąć. Po zalogowaniu na swoje konto otrzymuję taki oto komunikat:

Witaj, Customer (nie jesteś Customer?)

Po przejściu do listy zamówień zaś:

Witaj,      (to nie Ty)

Oczywiście, że to nie ja, nie mam wszak na imię <odgłos upadającej klawiatury>. Ale tuż niżej jest jeszcze lepiej:

Przykro nam, ale wygląda na to, że w podanym okresie nie mieliśmy od Państwa żadnych zamówień. Prosimy wybrać inny okres i spróbować ponownie. Jeśli uważasz, że ta wiadomość została dostarczona do Ciebie omyłkowo,

Jakim okresie? Jak wybrać inny okres? Jaka wiadomość? I co, jeśli została dostarczona omyłkowo? Klikam jednak „Wyświetl inne zamówienie”, wpisuję numer zamówienia i swój adres e-mail (ten sam, na który jestem zalogowany). Pojawia się zamówienie.

Stan: Wysłano

Szacowana data dostawy: Zamówienie wysłane

Numer śledzenia: JJD0000¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

Wysłano dnia: 2011-11-04

Po kliknięciu odnośnika do śledzenia zamówienia, sytuacja nieco się klaruje. Trafiam bowiem na stronę hiszpańskiego (!) DHL, który — o dziwo — mojej paczki nie ma:

Situación de un Envío

Este número de expedición no existe en nuestro sistema, vuelva a intentarlo.

Si no conoce el número de expedición, puede intentarlo introduciendo el número de referencia

Si no conoce ninguna de estas dos variables, puede utilizar la opción ‘selección’ del menú ‘Servicio Cliente’.

Ciekawe, czy odpiszą mi na maila?

Update: Odpisali. Zamiast męczyć się ze stroną Della, przesyłki można śledzić na dedykowanej stronie DHL. Trzeba tylko podać swój numer klienta Dell i numer zamówienia, który znajdziemy w temacie e-maila z potwierdzeniem zamówienia.

Pięć setek szaleństwa

Wtem!

— Leży!

Produkcja? Ale tutaj?

Traceback (most recent call last):
  File "/var/www/foo/catalogue/views.py", line 252, in get
    product = Product.objects.get(upc=tokens[0])
  File "/usr/lib/python2.6/site-packages/django/db/models/manager.py", line 132, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 351, in get
    % (self.model._meta.object_name, num, kwargs))
TypeError: 'DoesNotExist' object is not callable

Odśwież. Działa. Ki czort? Odśwież. Leży. Działa — działa — leży — działa. Wynik zdaje się zależeć od procesu, który akurat obsłuży zapytanie.

No dobrze, zobaczmy, co…

        raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"
                % (self.model._meta.object_name, num, kwargs))

Wyjątek. W trakcie rzucania wyjątku. Wbudowanego w klasę modelu.

Wdech, wydech. Drugi worker dostał. Dziwacznie poskręcane fragmenty jego wnętrzności zasypują mi skrzynkę.

Wdech, wydech, grep.

Zanim się przedarłem, straciliśmy jeszcze trzech. W sumie połowa zwijała się tam i charczała wewnętrznymi błędami. Wytrzymajcie, jeszcze tylko kilka plików. I jest.

        try:
            product = Product.objects.get(upc=product_upc)
        except Product.DoesNotExist, Product.MultipleObjectsReturned:
            # …

Zasadzka — sztuk trzy w projekcie — ładnie nas urządzili. Jeden koniec kodu powoli kopie dołek pod drugim. Pułapki udało się usunąć, ale tamtym już nikt nie pomoże. Przysłanie na produkcję posiłków zajmie minimum trzy dni. Pozostaje tylko patrzeć, jak jeden po drugim toną w tym dziwacznym morzu bezradności, serwując skomplikowane dokumenty, jednak dławiąc się na najprostszych rzeczach. Zostali sami. Oni i pięć setek szaleństwa.

Kurtyna. Niestosowne słowa cisną mi się na usta, ubliżać chcę składni, albowiem łaknę Pythona trzeciej inkarnacji.

Django: TemplateResponse

Przyszło Django 1.3, pojawiła się paskudna implementacja class-based views, direct_to_template zostało oznaczone jako przestarzałe. Ale pojawił się też godny następca, choć z class-based views nie ma nic wspólnego.

Nie piszemy więc tak:

from django.shortcuts import render_to_response
from django.template import RequestContext

def foo(request):
    # ...
    return render_to_response('foo.html', {'bar': baz},
            context_instance=RequestContext(request))

Nie piszemy też tak:

from django.views.generic.simple import direct_to_template

def foo(request):
    # ...
    return direct_to_template(request, 'foo.html', {'bar': baz})

Ani ­— tym bardziej — tak:

from django.views.generic import TemplateView

def foo(request):
    # ...
    return TemplateView.as_view(template_name='foo.html')(request, bar=baz)

Drodzy państwo, zamiast powyższych używamy:

from django.template.response import TemplateResponse

def foo(request):
    # ...
    return TemplateResponse(request, 'foo.html', {'bar': baz})

Google Talk: authentication failure

Od wczorajszego wczesnego popołudnia nie mogłem się zalogować na swoje konto GTalk za pomocą żadnego komunikatora. Nic, zero, nada. GMail nadal działał bez zarzutu, czat na stronie logował się momentalnie, słońce świeciło, a Elvis wciąż był martwy. Zacząłem podejrzewać najgorsze.

Kiedy przyjechałem do domu, okazało się, że miałem rację — kończyło mi się piwo. Niezrażony, spróbowałem mimo wszystko skorzystać z komunikatora. Niestety — z podobnym skutkiem. Powoli zaczynałem przypuszczać, że coś jest nie tak. Sprawa była grubymi nićmi szyta. Bez wątpienia komuś zależało, by zerwać moje kontakty z Karolem z warzywniaka pod oknem. Czyżby cena ogórków szklarniowych miała wkrótce przekroczyć cenę cukru, a ten biedny chłopak próbował mnie ostrzec?

Dziś rano wznowiłem dochodzenie. Już w tramwaju byłem pewien, że ktoś mnie śledzi. Jakie jest bowiem prawdopodobieństwo, by sześćdziesiąt trzy osoby jechały akurat w tym samym kierunku, co ja? Dotarłem do biura, by zaraz po wejściu wytoczyć najcięższe działa: sięgnąłem do działu pomocy Google.

Jeśli zatem i ciebie chcą pozbawić kontaktu z Karolem, rychło wnijdź pod adres wskazany poniżej:

https://www.google.com/accounts/DisplayUnlockCaptcha

Reanimacja współczesnego systemu

Plotki na temat mojej śmierci były oczywiście grubo… aaruuaaaarrrghhhblargh. Niestety, równie dobrze poczuł się mój laptop po ostatniej aktualizacji pakietu upstart w PLD.

Ku pamięci potomnych przedstawiam zatem przepis na doprowadzenie do w miarę działającego stanu systemu wyposażonego we “wszystkie te niepotrzebne wodotryski¹”.

¹ Tak niektórzy pieszczotliwie określają udev, a właściwie cokolwiek powstałego po statycznej strukturze /dev, może z wyjątkiem epoki lodowcowej.

Zaczynamy tradycyjnie, od pominięcia procesu init (przykład dla grub2):

linux /boot/vmlinuz-2.6.37.4-1 root=/dev/sciezka/do-root init=/bin/sh

Następnie odzyskujemy podstawowe funkcje życiowe:

mount / -o remount,rw
mount /proc
mount /sys
export PATH=$PATH:/sbin:/usr/sbin
udevd &
udevadm trigger
dbus-daemon --system &
NetworkManager &

Uruchomiona w ten sposób konsola może odmawiać przerywania zadań przez Ctrl+C, ale zawsze można do tego celu użyć Alt+SysRq+I.

Django, except IntegrityError

Jeden mały, niewinny szczeniaczek o maślanych oczkach zostaje gdzieś na świecie żywcem przemielony na karmę dla kotów za każdym razem, kiedy piszesz coś takiego:

try:
    # ...
except IntegrityError:
    return None

Łapanie IntegrityError jako metoda wykrywania problemów jest równie dobrym pomysłem, co wymiana czujników dymu na urządzenie wykrywające odgłos syreny nadjeżdżającej straży pożarnej.

Błąd na poziomie bazy danych jest nieodwracalny. Nie można nic naprawić. To jak pójść pośmiertnie do lekarza. Połknięcie wyjątku bez dalszej propagacji to tylko dodatkowe punkty za styl. Dzięki temu wszystkie otwarte transakcje (z izolującą włącznie) właśnie stały się bezużyteczne, a najbliższa operacja na bazie danych spowoduje spektakularny zgon całej aplikacji.

Wersja obrazowa

Twój kod próbuje dywanikiem z kibla gasić ogień w domu, w którym dawno złuszczyły się tapety i zniknęły meble. W domu, który sam podpalił. Niezbyt zadowolony z rezultatów wychodzi więc przed budynek i dusi strażaków wężem. Po wszystkim, gwiżdżąc, z rękoma w kieszeniach, odchodzi w stronę zachodzącego słońca, po drodze mijając sąsiada — powoli spopielającego się we własnym ogródku.

Jeśli masz telefon w Plusie…

Zacznij od wysłania na (darmowy) numer 8010 SMS o treści STOP. Położy to kres kretyńskim reklamom z rodzaju:

MATKO BOSKA, KONIEC ŚWIATA, POTWIERDŹ SWÓJ NUMER i wygraj batonik.

Sukces zostanie potwierdzony przez wiadomość:

Numer zostal wypisany z listy wysylkowej. Dziekujemy.

Teraz czas wypisać się z ofert i promocji. W tym celu wyślij na (również darmowy) numer 8000 SMS o treści SPRZECIW. Kiedy i ten przyczółek wroga się podda, otrzymasz list kapitulacyjny:

Twoj SPRZECIW na komunikacje marketingowa kanalami elektronicznymi zostal zarejestrowany w systemie.

For great justice!

O DHL i najdroższych koszulkach na świecie

Gram. Całkiem sporo. Postanowiłem sobie sprawić koszulki, którymi promowany był Portal 2. Jasną i ciemną. Na polskie warunki tanio nie jest, ale koszulki wyglądają naprawdę fajnie.

Zamawiam więc. $39.98 plus dostawa. Za $20, bo z Ameryki. Do mojego zamówienia mogą zostać doliczone dodatkowe podatki, akcyzy i opłaty celne. Kontynuuj do płatności.

Płacę. A raczej próbuję. Płatność kartą kredytową została odrzucona przez mojego operatora. Ponów. Płatność kartą kredytową została odrzucona ponownie. Trudno, zapłacę przez PayPal. Klik. Klik. Tak pozbyłem się 197,91 PLN. W teorii.

W praktyce pozbyłem się bowiem 591,91 PLN. Bank płatności odrzucił, ale pieniądze uznał za wydane. Cóż, Valve mnie raczej nie okradnie. Minie czas blokady środków i oddadzą. Tak kończy się środa.

Jest piątek. Dzwoni telefon.

Anna z DHL przygotowuje się do odprawy celnej mojej paczki, która lada chwila trafi z lotniska w Lipsku do Izby Celnej we Wrocławiu. Musi wiedzieć, z czego zrobione są koszulki i jak zamierzam ich używać. Podaję adres e-mail, dostaję listę dokumentów do przedłożenia.

Podpisuję upoważnienie dla DHL do reprezentacji przed organami celnymi. Gdybym wiedział, co mnie czeka, zaprezentowałbym także swój organ. Nie uprzedzajmy jednak faktów. Podpisuję świstek, że zobowiązuję się koszulek nie sprzedać w celu zarobkowym. Została deklaracja zawartości, materiałów i intencji. Napisałem zgodnie z prawdą:

Są to dwie koszulki z krótkim rękawem. Mam nadzieję, że zrobione są z bawełny. Będę w nich dziarsko hasał po mieście, niczym rączy rumak po bezkresnych stepach w porannej rosie.

W poniedziałek o siódmej pięćdziesiąt cztery rano (słownie: w nocy) dzwoni telefon. Odbiera Asia. Anna z DHL dzwoni spytać, z czego zrobione są koszulki (sic!). Jest to tak prawdopodobny scenariusz, że — patrząc na swoją kobietę — sam siebie zaczynam podejrzewać o romans. O godzinie 11:35 odbieram kolejny telefon. Anna nie może wytrzymać bez mojego cudownego głosu, tym razem chce poznać także kształt rzeczonych koszulek. Nie pyta o rozmiar, co niestety niweczy próby wyobrażenia sobie mojej boskiej figury. Mają już wszystko potrzebne do odprawy.

I tak trafiamy na środę, czyli dziś. Rano wita mnie informacja, iż odprawa celna została zakończona, cła importowego nie naliczono i na konto Izby Celnej winien jestem przelać należny podatek w kwocie 88 PLN. Kurwa, wróć! Osiemdziesiąt osiem? Przez zero koma dwa dwa daje… cztery stówy jak nic. Chwytam za telefon.

Dwie kolejne godziny spędzam dzwoniąc do mojej niedoszłej kochanki. Jest gorzej, niż zwykle. Jeszcze nie byliśmy w łóżku, a już nie chce ze mną rozmawiać. Po dwóch godzinach słuchania sygnału wolnej linii, Ania (teraz to już prawie rodzina, nawet do rodziców tak często nie dzwonię) odbiera telefon. Mówi, że wszystko się zgadza, bo przecież stawka podatku jest 22%. Z tym, że ja stawki VAT znam. Pytam o podstawę.

Przecież 22% z 402 PLN to 88 PLN. Czuję się jak pilot myśliwski na manewrach, krew w moim mózgu osiąga przyspieszenie rzędu 9G. Czterysta. Dwa. Złote. Ania sprawdza, skąd. Okazuje się, że na fakturze dołączonej przez Amerykanów nie było ceny dostawy, więc DHL wpisał, co uważał.

Zaczynam się gotować. O kolor metki i ściegi na szwach to by mieli kogo spytać. Pewnie o szóstej w nocy dzwoniliby z pytaniem o temperaturę prania. W obliczu tragedii, jaką jest brak ceny dostawy na fakturze, stanęli bezradni i w wyniku rzutu kostką wpisali 283 PLN. Nie mogli się dowiedzieć. Nie, żeby na ten przykład byli w stanie ustalić, na ile sami skasowali Valve, przecież DHL nie udzieli takiej informacji sam sobie (“ale oni mówią do nas po angielsku!”).

To oczywiste, że inteligentny człowiek zamówiłby koszulki o wartości 119 PLN i kazał je dostarczyć kurierowi za 283 PLN. A przynajmniej każdy tak inteligentny, jak agenci celni DHL.

Złożyłem reklamację, jednak jestem prawie pewien, że do czasu jej rozpatrzenia minie termin uiszczenia opłat celnych. Wobec tego będę musiał te pieniądze założyć. Będzie to moment, w którym chwilowa cena dwóch kawałków bawełny osiągnie 679,91 PLN. Zakładając, że DHL do tego czasu nie naliczy “dodatkowych opłat według aktualnie obowiązującego cennika”. Koszulek jeszcze nie widziałem.

DRM

— Wszystko ta sama sieczka — mruknął pod nosem, sam do siebie, odkładając kolejną płytę na półkę.

Dopiero po chwili wyłowił z gąszczu szarych, zastępczych okładek singiel, o który mu chodziło.

— Dzień dobry, chciałem dziesięć kopii tego — rzucił przyjaźnie w stronę kasjerki.

— Dziesięć? — Choć nadal uśmiechnięta, dziewczyna zrobiła zdziwioną minę. — Zwykle ludzie biorą trzy — zaczęła wymieniać — na kino domowe, na iPoda i na komputer…

— Cóż, mówią na mnie Rock, jestem didżejem — wypowiedział te słowa z dumą, podając dziewczynie plastikową kartę RIAA.

— Łał, to cudownie! Proszę. — Podała mu papierową torbę z płytami. — Numery seryjne płyt zostały przypisane do twojej karty. Kolejny przegląd licencyjny twojej karty w kwietniu. Czy mogę zainteresować cię czymś z naszej oferty specjalnej?

— Nie, dzięki.

Gdy wypowiadał te słowa poczuł wibracje w wewnętrznej kieszeni kurtki. Próbował szybko schować pakiet singli do kieszeni, ale było już za późno. Telefon zaczął dzwonić. Popatrzył jeszcze zrezygnowanym wzrokiem na pulsującą w rogu ekranu liczbę. Pozostało mu siedem odtworzeń dzwonka. Pamiętał kiedy wprowadzono te restrykcje. Jeden ze znanych wokalistów w publicznym wywiadzie przyznał się, że ustawił hit swojej kapeli jako dzwonek, dzięki czemu nie musi sam sobie płacić za jego słuchanie. Senatorzy RIAA byli wściekli, kongres zmienił ustawę w jeden weekend, nie chcąc dopuścić do sytuacji, kiedy młodzież pójdzie w ślady swojego idola. Odebrał rozmowę.

— Robert, człowieku, doprowadzisz mnie do ruiny. Czemu nie odbierasz zwyczajnie, kiedy to ja do ciebie dzwonię?

— Cześć Rock, wybacz, ale zdjąłem na chwilę słuchawki, a wiesz przecież, że mój dzwonek nie ma licencji na publiczne odtwarzanie. Jak tam zakupy, gotowy na dzisiejszy show?

— Taa, wiesz, że nigdy nie zawodzę. Właśnie kupiłem ostatni singiel, to będzie prawdziwa wisienka na torcie. W sumie mam już na wieczór cztery godziny muzyki.

— Będziesz coś miksował na żywo?

— Na pewno, ale jeszcze nie wiem, ile. Chcemy przecież jeszcze coś zarobić.

— Dobra, to nie przeszkadzam, do–zo wieczorem.

— No to strzałka, tylko się nie spóźnij.

Minął róg sklepu i wsiadł do samochodu. Zapalił silnik i z trudem powstrzymał chęć wpakowania jednego ze świeżych nabytków w szczelinę odtwarzacza. Zamiast tego włączył radio i w towarzystwie najnowszych informacji z życia gwiazd ruszył w stronę domu. Podekscytowany zbliżającą się imprezą podśpiewywał w rytm muzyki z rzadka pojawiającej się w przerywających audycję reklamach.

– ❧ –

Wiedział, że jest gotowy. Czuł, jak każdy mięsień drga z podniecenia. Zatrzymał auto pod klubem, na parkingu dla vip–ów i ponownie przejrzał kuferek z płytami, po raz ostatni zamieniając niektóre z nich kolejnością. Pewnym krokiem podszedł do drzwi dla wykonawców. Odruchowo podał plastik karkowi w nieco przyciasnym garniturze.

— Może pan wejść. Karta ważna do kwietnia.

Zamienił jeszcze parę słów z menadżerem klubu i udał się do baru po “jednego na rozpęd”.

Pół godziny później wychodził już na scenę. Czuł pulsującą adrenalinę, kiedy oświetleniowiec wskazał go reflektorem. Ujął w rękę mikrofon i słowa popłynęły same.

— Jak się bawicie, Nowy Yorku? — Poczekał, aż ucichną wrzaski. — Jak pewnie wszyscy wiecie, jestem Rock. — Wzrokiem odnalazł wśród widowni poznaną tego poranka kasjerkę. — Obok mnie stoi pan Miles Welles z komisji RIAA, który będzie czuwał nad dzisiejszą imprezą. Za trzy minuty włączymy systemy przeciwdziałania nagrywaniu, osoby z aparatami słuchowymi prosimy o opuszczenie sali. A teraz przygotujcie się, bo dziś wieczór damy czadu!

zsh + git

Brian Carper opublikował niedawno bardzo praktyczny sposób na prezentację stanu systemu kontroli wersji w zsh. Poniżej wariacja na modłę domyślnego wiersza poleceń w PLD:

zsh + git

autoload -Uz vcs_info
 
zstyle ':vcs_info:*' stagedstr '%F{green}∙'
zstyle ':vcs_info:*' unstagedstr '%F{yellow}∙'
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat '%b%F{1}:%F{11}%r'
precmd () {
    if [[ -z $(git ls-files --other --exclude-standard 2> /dev/null) ]] {
        zstyle ':vcs_info:*' formats ' %F{green}%b%c%u%f'
    } else {
        zstyle ':vcs_info:*' formats ' %F{green}%b%c%u%F{red}∙%f'
    }
 
    vcs_info
}
 
setopt prompt_subst
PS1='[%n@%m %~${vcs_info_msg_0_}]$ '