Ostatnio jest cicho, więc dla poddierżenia razgawora kilka drobiazgów.
@classmethod
def import_object(cls, obj):
return False, None
Zimno. Odkąd wróciłem z Grecji jest mi nieustająco zimno. Niestety, w te wakacje raczej nie uda mi się dostać 10 dni urlopu, więc jeżeli już pojedziemy do Grecji, to na tydzień.
Ustaliliśmy z żoną, że w przyszłym roku Escort idzie do ludzi. Jakby to wyczuł, bo zaczął się sypać: najpierw jakiś wyciek z układu hamulcowego, teraz problemy ze sprzęgłem i skrzynią biegów.
Po raz pierwszy od ponad miesiąca przyjechałem dziś do pracy komunikacją publiczną. Bez stania w korkach i czekania na autobusy (akurat dziś podjeżdżały w mgnieniu oka) zajęło mi to 70 minut (85 od wyjścia z domu). Samochodem w 70 minut dojeżdżam stojąc w korkach (Piłsudskiego/Chełmżyńska i Marsa). Na szczęście to był jednorazowy wybryk, już jutro znowu zamieniam bus na wóz.
Do koncertu Nine Inch Nails w Poznaniu pozostało 20 dni.
Dziś wystawiłem moją pierwszą fakturę. Gdyby nie to, że dziś znowu się wożę (pierwszy raz wiozłem moją żonę, umierała ze strachu...), to pewnie poszedłbym coś chlapnąć na to konto.
Dziś, 22 kwietnia 2009 dostałem wreszcie mój REGON. Po 3 (trzech!) tygodniach mogę zacząć normalną działalność gospodarczą. W stosunku to gorszej przeszłości jest to zysk na poziomie -14 dni (minus czternastu), co oznacza 14 dni straty.
Lepsze jutro było wczoraj.
Recently I had to make a widget for Django admin to display movies (H.264 encoded, MPEG-4 and FLV). We already have a widget for displaying the movie inline within the admin change form, but I had to create a widget that opens separate entity (lightbox-like overlay or popup browser window). For a start I took the popup browser — this seemed easier because one of the requirements was that the movie player will not load with the page (I am not that good in javascript, mind you...). What it takes to have a widget that shows some content in separate browser window?
-
you can open window by manually appropriate javascript code, but I prefer to use some ready-made plugin for jQuery, like jquery-popupwindow;
-
you'll need Django view (and template) that displays actual page with embedded player;
-
to embed the player (which is Flash object) you'd need swfobject and the player itself, like JW Player (it plays both mp4 and flv movies — but watch out, it's free only for strictly non-commercial use);
-
a Django's forms widget that will have something that can be clicked to open new window.
Now for the fun part: actual code. The widget code is a mish-mash of my previous experiences in writing custom Django widgets:
class AdminPopupMovieWidget(forms.FileInput):
class Media:
js = (
'js/jquery.js',
'js/jquery.popupwindow.js',
)
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, 'url'):
width = getattr(value, 'width', 600)
height = getattr(value, 'height', 500)
ctx = {
'url': value.url,
'href': '%s?url=%s' % (reverse('movie-show'), value.url),
'class': 'popupwindow',
'rel': 'width:%(width)d,height:%(height)d' % locals(),
}
output.append(u'<a href="%(href)s" class="%(class)s" rel="%(rel)s">%(url)s</a>' % ctx)
output.append(u'<br />')
output.append(super(AdminPopupMovieWidget, self).render(name, value, attrs))
output.append('''<script type="text/javascript">
$(".popupwindow").popupwindow();
</script>''')
return mark_safe(u''.join(output))
The code above produces a widget that has a clickable link with the title of movie URL. The anchor is linked to the URL of the view that produces HTML to display, like the code below:
@require_GET
def view_movie(request):
movie_url = request.GET.get('url')
if not movie_url:
raise Http404
ctx = {
'url': movie_url,
}
return render_to_response('movie/view.html', ctx,
context_instance=RequestContext(request))
The only variable that is passed in the context is the URL of the movie, but you are free to include as much as you'd like. Of course, nothing forces you to use urls, you can call the view with a PK to the movie-holding object, but this will require changes to the above view code.
But that's not all. You have to register your model in Django admin application. If you just register it with default modelform, the instances will display themselves with default admin widget. Two more classes are required:
class MovieAdminModelForm(forms.ModelForm):
movie = forms.FileField(widget=widgets.AdminPopupMovieWidget)
class Meta:
model = Movie
class MovieAdmin(admin.ModelAdmin):
form = MovieAdminModelForm
Is this all? Yes, I think.
Jeff Atwood napisał kolejny tekst, który trafia do mnie, tym razem o ludziach, którzy nie powinni zajmować się programowaniem. Zarzewiem był pewien wątek na pewnym forum, w którym ktoś zastanawiał się nad opuszczeniem branży programistycznej. Trudno mi się z tym artykułem nie zgodzić — w końcu zajmuję się programowaniem od ponad 10 lat. W tej branży 10 lat to jak od epoki brązu do atomu.
Wielokrotnie w ciągu tych 10 lat spotykałem ludzi, którzy programowaniem zajmowali się tylko przejściowo, między studiami a pójściem w managery, na szczęście nie musiałem z nimi współpracować, więc nie napsuli mi wiele krwi. Zawsze jednak dziwiło mnie to, że najpierw poświęcili 5 lat na uczenie się inżynierii oprogramowania na studiach i potem przez 5 lat pisali kod tylko po to, żeby wreszcie wskoczyć na najniższe stanowisko w managemencie IT, mając w dodatku perspektywę, że od najwyższego stanowiska, które będą w stanie osiągnąć na normalnej drodze kariery, dzieli ich tylko jeszcze jedno. Coś mi się tu nie zgadzało: poświęcać 15 lat na to, by osiągnąć szczytowe stanowisko w branży? Przecież można je osiągnąć szybciej, powiedzmy w 10 lat. I co ważniejsze, co dalej? Co potem?
OK, w porządku, nie jestem w stanie myśleć tak, jak myśli 98% ludzi, uważających się za normalną większość — od ciągłego obcowania z komputerami (a pewnie i z powodu szczątków klasycznego wykształcenia) wszystko co robię musi być przemyślane i mieć sens. Artykuł Jeffa Atwooda jest oparty nie tylko na przemyśleniach, sporo w nim emocji, co bynajmniej nie odbiera mu wartości, bo dobrze określa tych, którzy zostają w branży jako ludzi, którzy po prostu kochają pisać kod. Dla niektórych dziwnie wygląda 40-letni programista, dla innych to człowiek, którego w zawodzie trzyma pasja (w ciągu tych 15 lat pracy w zawodzie na pewno trafiają się lepsze propozycje, choćby ze zwykłego rachunku prawdopodobieństwa to wynika).
Ja w każdym razie zostaję. Przez pewien czas próbowałem managerzyć i cieszę się, że już tego nie robię. Moja wątroba ma się lepiej, żona jest mniej nerwowa i jakbym siwiał trochę wolniej.
Do tej pory pracowałem przy poważnych aplikacjach dla poważnych ludzi. Aplikacje te sprawiały poważne problemy i walka z nimi była naznaczona ich powagą. No i kiedyś musiała nadejść ta chwila, że zająłem się aplikacją dla pokemonów — robię wyszukiwarkę dla jednego z serwisów przeznaczonych dla publiczności w wieku gimnazjalno-licealnym (tytułowe pokemony).
Byłem przygotowany na koszmarny, niczego nie przypominający język. Byłem przygotowany na wszelkie zbrodnie przeciwko ortografii, gramatyce i interpunkcji. Spodziewałem się dziwactw, które są w stanie wywalić w kosmos aplikację niezabezpieczoną przed wstrzykiwaniem SQL. Przeglądając dane z forum tego serwisu natknąłem się jednak na coś, co przesunęło moje granice percepcji.
Podczas próby indeksowania silnik indeksowy updarcie odmawiał przyjęcia pewnego zbioru danych twierdząc, że znajduje się w nich znak kontrolny Unicode o numerze 14, czyli Shift Out. WTF?! Skąd tam się wziął niedrukowalny (i niewprowadzalny z klawiatury!) znak kontrolny? Nie da się go wpisać, nie da się go wkleić w okienko do wprowadzania tekstu (tak mi się przynajmniej wydaje), bo nie można go też skopiować. Prawdę mówiąc zupełnie nie interesuje mnie, jak tamta aplikacja wpuściła ten znak do swojej bazy (to jest nazwa użytkownika, więc jest używana w wielu miejscach), za to interesuje mnie, jak ktoś go przeprowadził przez formularz HTML, żądanie HTTP POST i bibliotekę kliencką bazy danych (znak ten zapisany był w zwykłym polu znakowym typu varchar, nie powinien był tam się znaleźć bez odpowiedniego wyeskejpowania).
Wiele lat pracy z poważnymi aplikacjami przyzwyczaiło mnie do tego, że aplikacja jest linią frontu, na której muszą zostać powstrzymane śmiecie — powstrzymane lub zneutralizowane. W wyniku konsekwentnego chronienia rodowych sreber (dane) przed zalewem gówna, można mieć pełne zaufanie do danych aplikacji. No i okazało się, że są aplikacje, którym tak naprawdę jest wszystko jedno, którą częścią ciała szczekają psy...
Z ogromną przykrością i z prawdziwym żalem odkryłem, że Hacknot.info (nie linkuję, bo domenę przejęli już squatterzy) jeden z najciekawszych serwisów z esejami dotyczącymi produkcji oprogramowania zniknął z sieci. Zniknęła masa fantastycznych artukułów (często bardzo kontrowersyjnych, ale zawsze ciekawych i pozwalających przemyśleć poważnie różne sprawy). Pozostała książka (do ściągnięcia za darmo w postaci PDF lub do kupienia za parę €), ale zawiera ona tylko wybór esejów Eda Johnsona.
Szkoda, naprawdę wielka szkoda. Z drugiej strony, nie mogę uwierzyć w to, że cała zawartość tego serwisu zniknęła bez śladu (przecież była na CC!), więc przez cały czas można mieć jakąś nadzieję, że gdzieś uchowała się lokalna kopia...
Dziś ma zostać wydane Django 1.0.
Będzie?
Nie będzie?
Dziś w pracy miałem robotę głównie poszukiwawczą — było kilka rzeczy, których nie wiadomo było jak zrobić, choć wiadomo było, że się da. Na przykład na początek: jak w adminie Django wyświetlić obrazek na formularzu edycji obiektu, zamiast zwykłego linku do niego, jak to jest domyślnie? Na szczęście wiedziałem jak się to robi, więc nie miałem zbyt trudnego zadania na początek.
W nowym Django (czyli beta-1 lub nowszym) w adminie jest to o tyle proste, że dla każdego indywidualnego pola formularza można przypisać odpowiedni widget, czyli reprezentację w HTML. Wystarczyło utworzyć sobie odpowiednią klasę dla takiego widgetu:
class AdminImageWidget(forms.FileInput):
"""
Widget do wyświetlania obrazka w adminie (change form)
"""
def render(self, name, value, attrs=None):
output = []
file_name = str(value)
if value and hasattr(value, 'url'):
output.append('<img src="%(url)s" alt="image" /><br />' % {'url': value.url})
output.append('%s ' % _('Change:'))
output.append(super(AdminImageWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
i następnie użyć jej w klasie administracyjnej dla modelu:
class MovieImageAdmin(admin.ModelAdmin):
list_display = ('movie', 'width', 'height', 'shot_at')
search_fields = ['movie__title']
save_on_top = True
list_filter = ('movie', 'width')
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'image':
kwargs['widget'] = AdminImageWidget
return db_field.formfield(**kwargs)
Metoda formfield_for_dbfield jest wywoływana dla każdego pola formularza i w tym przypadku sprawdzeniu podlega nazwa pola, ale można równie dobrze sprawdzić jego typ lub inny atrybut, aby dopasować właściwy widget. W ten sposób można także dostosować wyświetlanie plików video czy reprezentację dla plików dźwiękowych.
No dobrze, to było proste i nie zajęło mi całego dnia. Ale następna robota nie była już taka oczywista. Kolega chciał, żeby główne okno aplikacji administracyjnej (to, co jest pod / w adminie) dostało kilka dodatkowych zmiennych w kontekście. W starszym Django (sprzed newforms-admin) nie byłoby to możliwe bez patchowania samego Django (co nie jest kłopotliwe, jeżeli używa się Virtualenv, ale zawsze jest uciążliwe), ale teraz dostaliśmy wszystkie potrzebne narzędzia.
Jak wiadomo, za sterowanie aplikacją administracyjną odpowiada obiekt klasy AdminSite, który jest o tyle fajny, że jest najzwyklejszym obiektem najzwyklejszej klasy. Klasy, którą oczywiście można nadpisać. Jak to może wyglądać? Na przykład tak:
class AdminSite(admin.AdminSite):
def index(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
ctx = {
'my_item': u'my_value',
}
extra_context.update(ctx)
return super(AdminSite, self).index(request, extra_context)
Nieprzyjemy aspekt używania obiektu site własnej klasy AdminSite jest taki, że nie działa funkcja autodiscover() i wszystkie klasy administracyjne trzeba rejestrować własnoręcznie w tym samym miejscu, gdzie znajduje się obiekt klasy AdminSite. Ale to chyba stosunkowo mała niedogodność w porównaniu z dostarczoną mocą.
W nowej pracy dostałem nowe zadanie do zrobienia — zdeployować gotową aplikację w Javie na kilka maszyn w kilku odmianach. Jako że do tej pory takimi rzeczami zajmował się dla mnie dział deploymentu (słodkie czasy), było to dla mnie coś zupełnie nowego. Do zrobienia była dość prosta rzecz:
-
skopiować kilka szablonów plików z jednego drzewa katalogów do drugiego,
-
podmienić odpowiednie ciągi znaków w plikach szablonów,
-
spakować wszystko do tar.gz,
-
wrzucić na zdalną maszynę przez scp,
-
rozpakować na zdalnej maszynie,
-
uruchomić aplikację pod screenem.
Zacząłem od poszukiwania narzędzia, które by mi to ułatwiło.
Skrypty w shellu odpadły na samym początku. Nie umiem ich pisać, poza paroma najbardziej podstawowymi zastosowaniami.
W pracy używamy Pavera, więc naturalną koleją rzeczy zasiadłem do klepania pliku pavement.py. Szybko okazało się, że to potwornie żmudna robota, gdy aplikacja nie jest napisana w Pythonie i wszystkie ułatwienia Pavera można sobie wsadzić głęboko.
Przypomniałem sobie wtedy, że parę lat temu robiłem podobne rzeczy przy użyciu anta, więc napisałem sobie odpowiedni plik build.xml. Było z tym trochę roboty, ale działa. Wreszcie. Ale niestety, to jest Java i XML, więc piętno zostało wybite.
Zacząłem się rozglądać za innym narzędziem i wtedy wpadł mi w oko Fabric. Z opisu wynika, że może być tym, co zastąpi mi anta... ale może też wcale tak nie być. Projekt jest w bardzo wczesnej fazie rozwoju, ale będę miał na niego oko, gdyby ant za bardzo mi dopiekł.
Minął pierwszy tydzień mojej pracy w (nazwa usunięta dla porządku). Zaczęło się w poniedziałek leniwie, a zakończyło dziś sprintem, gdy w około 5 godzin zrobiliśmy aplikację. Klient do tego stopnia nie wierzył w to, że nam się uda, że przed planowanym wdrożeniem w niedzielę nawet nie przygotował szaty graficznej dla serwisu... :)
Dojazd do nowej pracy mam koszmarny, ale jakoś to przeżyję. Gorzej, że nie ma tam w pobliżu żadnej przemawiającej do mnie knajpy, w której moglibyśmy się spotykać.
Claudiu Pap, (obecnie już były) portal manager Commune.ro wpisał w komentarzu do mojego pożegnania z Sensisoftem, że spodziewa się w tej sytuacji rychłej śmierci projektu. Bardzo to miłe z jego strony, ale to nie działa w ten sposób.
Wiele razy już byłem ogłaszany człowiekiem, którego nie da się zastąpić i wiele razy okazywało się, że z perspektywy firmy lub projektu nie jest to prawdą. Faktycznie, z pewnej perspektywy jest to prawda, bo każdy człowiek jest unikalny na skalę światową i ma niepowtarzalny zestaw umiejętności, osobowości, charakteru, wiedzy i doświadczeń. Patrząc pod tym kątem to i owszem, próba zastąpienia mnie jest skazana na pewną porażkę. Ale przecież w projekcie nie chodzi o cały ten komplet cech, a jedynie o pewien zestaw cech, który powodował że coś się udawało. Patrząc z tej strony, niezastępowalność nie jest już tak oczywista. Prawdopodobnie będzie trudno znaleźć kogoś, kto będzie w stanie przejąć moje obowiązki w sposób niezauważony, ale nie chodzi nawet o to, żeby się to odbyło w sposób niezauważony. Mój sposób dowodzenia nie był jedynym, który mógł doprowadzić do sukcesu. Przypadek sprawił, że był dość charakterystyczny i ekstremalny, przez co łatwo zapadł w pamięć i szybko został skojarzony z owym sukcesem na zasadzie analogii. Trudno oczekiwać, żeby akurat w tym przypadku (w odróżnieniu od tego, jak reszta świata jest skonstruowana) istniała tylko jedna dobra recepta na powodzenie (abstrahując od stopnia dobrości różnych recept).
Nasze drogi z Sensisoftem rozeszły się i teraz każdy musi sobie poradzić sam. Jestem przekonany, że tak jak ja potrafię zadbać o siebie, Sensisoft będzie w stanie zadbać o przyszły sukces projektu Commune.
Wczoraj, w godzinach popołudniowo-wieczornych, dokonałem pożegnania z Sensisoftem, oficjalnie — to znaczy przy użyciu piwa. Wypiliśmy, pogadaliśmy, w nieco szerszym gronie wypowiedziałem to, co mówiłem już wiele razy przy nieco mniejszych spotkaniach towarzyskich: że sposób, w jaki ta firma produkuje oprogramowanie jest chory (co nie przekłada się wprost na jakość tego oprogramowania, a jedynie wtórnie, mam nadzieję). Porozmawialiśmy też trochę na temat zadań, jakie powinien wypełniać lider techniczny projektu, zakresu jego odpowiedzialności i związanego z nią zakresu władzy.
Tak, nie ma co się wstydzić tego słowa. Zgłaszanie się do roli technicznego lidera projektu z powodów ambicjonalnych czy prestiżowych mija się z celem, biorąc pod uwagę to, za co ten człowiek odpowiada — czyli za terminowe dostarczenie oprogramowania zgodnego ze specyfikacją. A niosąc taką odpowiedzialność, musi mieć też władzę zarządzania czasem członków swojego zespołu (władzę na szczeblu taktycznym), musi mieć też możliwość wpływania na decyzje operacyjne typu "co ma zostać zrobione wcześniej, a co później". Ja taką władzę miałem i nie pozwalałem jej sobie odebrać, co, jak zauważyłem, nie było szczególnie dobrze przyjmowane. Nie uzurpuję sobie większych zasług, niż mi się należą, ale wydaje mi się, że dzięki mojemu chłodzącemu wpływowi rozwój mojego projektu odbywał się w postaci kontrolowanego chaosu. Odszedłem z firmy, gdy przestałem panować nad tym chaosem.
Mam nadzieję, że w nowym miejscu będzie mi lepiej. Ale może po prostu wystarczy mi, że będę w stanie kontrolować chaos.
No i będę miał w pracy MacBooka. Niby zwykłego, ale tak nie do końca, bo czarnego. Nigdy nie miałem Maca, nigdy też go nie używałem, więc zacząłem od czytania, co też mnie czeka po przesiadce z linuksa. Nie zapowiada się różowo, głównie z powodu problemów z bibliotekami. Ale może jakoś to przeżyję...
Po miesięcznym zapieprzu związanym z zakończeniem kontraktu na aplikację, wziąłem tydzień wolnego od firmy. Zamierzam nie myśleć o pracy i zajmować się swoimi sprawami, w swoim tempie. Nie wiem jeszcze, co będę robił, niczego nie planuję. Jest wiele rzeczy, które mam ochotę zrobić — chcę sobie trochę poczytać, przećwiczyć parę sztuczek z javascriptem, może napisać trochę kodu na własne potrzeby.
W pracy używamy kilku różnych bibliotek third-party. W ogniu walki często okazuje się, że mają one jakieś mankamenty, albo brakujące funkcjonalności, od których zależy działanie naszej aplikacji. Czasem kończy się to zgłoszeniem błędu, ale częściej razem ze zgłoszeniem dostarczamy poprawkę. Czuję się szczególnie doceniony, gdy moja poprawka trafia do upstream — mam wtedy megalomańskie uczucie, że dzięki mnie świat staje się lepszy. Chociaż w niewielkim zakresie... ;)
Po raz kolejny zastrzegam: to nie jest miłość (bo miłość może być tylko jedna). Ale jQuery mi się podoba.
Dzisiaj miałem zrobić taki pokręcony formularz z trzema select-ami, gdzie środkowy przyjmuje elementy z dwóch naokoło niego na kliknięcie. Biorąc pod uwagę moją niechęć (i co tu dużo ukrywać, nieznajomość też) do JavaScriptu, planowałem sobie to na co najmniej dzień roboty, a najprawdopodobniej półtora. Tymczasem usiadłem do roboty około 10, a o 12 miałem już to zrobione. 35 linijek kodu i żadnego znużenia. Więcej takich ułatwiaczy poproszę.
Gdy kilkanaście lat temu zaczynałem pisać programy o nieco większym stopniu skomplikowania, obowiązującą mantrą było: przerzuć obliczenia na serwer bazy danych, on ma więcej mocy niż aplikacja. Stąd wzięły się wszystkie moje umiejętności optymalizacyjne, które dobrze robiły przez wiele lat. A teraz dowiedziałem się od naszego bazodanowca, że muszę chronić serwer przed przegrzaniem i jak najwięcej robić na serwerze aplikacyjnym, bo... serwer z bazą nie wyrabia.
Mam dysonans poznawczy. Wolałbym, żeby okazało się, że z powodów oszczędnościowych firma postawiła bazę na jakimś Celeronie 1.7GHz z 256MB RAM, niż że moje umiejętności optymalizacyjne nadają się do lamusa... ;)
Po ponad roku pracy z młodzieżą zebrało mi się na kilka refleksji dotyczących całej branży robienia softu. Raczej przygnębiających.
Po pierwsze, i najważniejsze, większość z programistów przed 30-tką, jakich spotkałem w ciągu tego roku, nie ma pojęcia o projektowaniu aplikacji. Umieją napisać kod, który robi to, co założyli, ale zazwyczaj cała aplikacja nie robi tego, co miała robić. Ich horyzont widzenia kodu zamyka się w jednym, najwyżej w dwóch modułach, więc każda większa zmiana skutkuje zwykle załamaniem się innego fragmentu aplikacji. A o czymś takim, jak zastanawianie się nad konsekwencjami zmian w kodzie zwykle sobie nie pozwalają. Powiedziałbym ogólnie, że myślenie nie jest ich najmocniejszą stroną, albo inaczej — są świetni w rozwiązywaniu problemów, które sami sobie stworzyli (by sparafrazować Kisiela). I to jest ta przygnębiająca konkluzja.
Przyczyna tego stanu rzeczy wydaje się leżeć głębiej, a jest ona chyba jeszcze bardziej przygnębiająca. Otóż, według mnie, głównym czynnikiem, który sprawia, że młodzi programiści nie dojrzewają do projektowania aplikacji, jest nacisk managementu na to, żeby jak najszybciej dostarczyć działający kod, byle by był. Management żyje w świecie złudzeń (podtrzymywanych przez samych programistów), jakoby zawsze można było poprawić źle działający kod później, ale z drugiej strony, żeby się z tych złudzeń wyleczyć, to trzeba napisać sporo kodu, którego nikt nigdy nie poprawił z powodu braku czasu. Autor nie poprawi, bo nie ma kiedy (zajmuje się przecież czymś innym, również na już), a jego następca też nie poprawi, bo kodu nie rozumie (autor mu nie wytłumaczy, bo nie ma czasu, zresztą prawdopodobnie sam już nie pamięta).
I co teraz? Założę się, że nic, będzie tak, jak poprzednio.
Powoli żegnam się z pięknym miastem Debreczynem (pogoda piękna, miasto... no cóż, na pewno ma swoich miłośników). Spotkanie nie było tak straszne, jak się obawiałem, chociaż do przyjemnych nie należało. Zjedzony nie zostałem i to się dla mnie liczy. ;)
O 20:00 mój samolot wystartuje z Ferihegy i mam nadzieję około 23:00 znaleźć się we własnym łóżku.
No, nie takim znowu pięknym, przypomina raczej Piotrków Trybunalski... Ale Piotrków też pewnie ma swoich amatorów.
Najpierw godzina samolotem z Warszawy do Budapesztu, potem 2 godziny czekania na samochód w hali przylotów lotniska Ferihegy 2A, a potem jeszcze dwie godziny samochodem z Budapesztu do Debreczyna (mają autostrady... zazdrość!). Jestem półżywy. A jutro szykuje się nieprzyjemne spotkanie z klientami. Mam nadzieję, że mnie nie zjedzą, ale może być różnie...
Dopiero wyjazdy na kilka dni uświadamiają mi, jak bardzo tęsknię za Kasią i ZIZ. Gdy jestem w domu, to z niecierpliwością czekam na każdą godzinę-półtorej, kiedy zostawią mnie samego i będę mógł sobie trochę pokodować, ale gdy wyjadę na dłużej, to mi ich brakuje. Bardzo.
Co jakiś czas na pl.comp.lang.python pojawiają się ogłoszenia o pracy dla programistów w Pythonie. Abstrahując od dyskusyjnej właściwości tej grupy dla wysyłania ogłoszeń o pracy (niby jest pl.praca.oferowana, ale ogłoszeń o pracy dla Pythoniarzy nie jest aż tak wiele, więc ma to pewien walor...), to reakcje na takie ogłoszenia doprowadzają mnie do białej gorączki.
Przykład z ostatnich dni, pewna firma z Wrocławia zamieściła ogłoszenie o pracy. Napisali czym się zajmują, czego oczekują i tradycyjnie enigmatycznie co oferują. I od razu zaczęło się wydziwianie: a to że polszczyzna odbiega od wzorca metra prof. Miodka, a to że znowu nie podali proponowanych zarobków, a to że wymagają bardzo dobrej znajomości biblioteki standardowej. Takie pitu-pitu, żeby sobie tylko popyszczyć. Dużo osób narzeka, że gdzie indziej pracodawcy w ofercie podają kwoty, ale nikt jakoś nie zauważa, że dotyczy to ofert zamieszczanych w serwisach jobsowych, a nie w usenecie. I pewnie aby dopełnić obrazu nikt się nie pofatygował, żeby popatrzeć jak gdzie indziej wyglądają reakcje na takie ogłoszenia. Podpowiem — odpowiedzią jest milczenie.
Some people would argue that not all developers are morons or assholes, but they are mistaken. For example, some people posit the existence of what I will call the “angel” developer. “Angels” read specs closely, write code, and then thoroughly test it against the accompanying test suite before shipping their product. Angels do not actually exist, but they are a useful fiction to make spec writers to feel better about themselves
(za diveintomark)
Jest kilka firm, w których chciałbym pracować. Na poniżej liście nie ma mojego obecnego pracodawcy, bynajmniej nie dlatego, że nie chcę już pracować w Sensisofcie, ale dlatego, po pierwsze jest to lista marzeń, a nie planów, a po drugie, gdybym tam nie chciał pracować, to już bym nie pracował.
-
Google. To oczywiste. Każdy programista czy projektant chciałby pracować w Google, a ja nie jestem w tej dziedzinie wyjątkiem. Ta firma robi rzeczy wielkie, coś na miarę projektu Manhattan, ale w skali oprogramowania. Chciałbym (chociaż przez pewien czas) być jednym z nich.
-
Xerox. Dziwnym może się wydać umieszczenie firmy Xerox już na drugim miejscu, bo dla większości ludzi oblatanych w IT kojarzy się ona z kopiarkami, potwornie drogimi drukarkami, a dla niektórych także z niezwykle innowacyjną historią Palo Alto Research Center (Xerox PARC). Jest jednak coś innowacyjnego w tej firmie także i dziś, ale niewiele osób o tym wie, bo jej najbardziej innowacyjne produkty mają wąskie grono odbiorców, jak np. VIPP. Poznałem VIPP dość dobrze i doceniłem jego klasę.
-
Canonical. Robienie na linuksie jest dla mnie teraz tak oczywiste, że trudno mi sobie wyobrazić, że mógłbym w pracy używać innego systemu operacyjnego, a w dzisiejszych czasach coraz częściej jest tak, że mówisz linux, myślisz Ubuntu.
-
IBM, Sun i Microsoft, ex aequo. Każda z tych firm robi ciekawe rzeczy, choć te, które mnie naprawdę interesują, stanowią margines ich działalności. Gdyby udało mi się popracować przy tych ciekawych rzeczach, to czemu nie?
Jak widać, nie ma tego szczególnie wiele. Jedyną nieoczywistą pozycją w tym zestawieniu wydaje się być Xerox, więc może jednak zostanę w Sensisofcie... ;)
A tak całkiem na poważnie, to nie wiem, czy te koszta są tak poważne.
Ten projekt to webshop (a jakże), z dość egzotycznym rodzajem muzyki. Nie obliczam go na jakiś ogromny ruch typu miliony wejść dziennie, ale z 10 000 przydałoby się obsłużyć. Oprogramowanie napiszę sobie sam (koszt: 0), pozostaje to, czego sam nie zrobię, czyli maszyna (najcieńszy Root Server na hetzner.de to około 2500 złotówek rocznie), storage (S3, z pewnym okładem to 250 złotówek miesięcznie) i opłaty dla firmy rozliczającej płatności. Razem daje to około 5500 złotówek samego kosztu utrzymania, kosztów administracyjnych jeszcze nie udało mi się policzyć.
Dużo? Czy nie dużo? Trudno mi powiedzieć, chociaż tak na oko nie wydaje się to jakimś ogromnym wydatkiem.
Bardzo, bardzo dokładnie przyglądam się wszystkiemu, co związane z cache na poziomie obiektów w Django (np. projektowi django-orm-cache). Znalazłem blogowy wpis (po rosyjsku, a jakże!), który opisuje rozwiązanie bardzo podobne do tego, którego i my używamy, ale o wiele bardziej kompletne. Wypadałoby to teraz gdzieś przetestować, ale w środowisku, w którym miałoby to jakikolwiek sens, nie za bardzo możemy — po prostu nie ma na to czasu...
Ku pamięci, jak to Aleksiej Koszeliew załatwił sprawę odświeżania obiektowego cache w Django. Polecam uwadze, jeżeli kogoś też interesuje to, jak inni dają sobie radę z wygaszaniem cache...
A my nie! (My, czyli kilka naszych aplikacji)
Iwan Sagalajew, pracujący dla yandex.ru, podzielił się kilkoma spostrzeżeniami po nieudanym odpaleniu nowego serwisu społecznego. Czytałem to z niekłamanym zadowoleniem — większość problemów, które tam opisał, nie ma nawet szans, żeby nas dotyczyć. Po kolei:
-
zbyt długi czas zapisu danych sesji, nie dotyczy nas, bo sesje trzymamy w memcache;
-
efekt "dog-pile", nie dotyczy nas, bo rzeczy kosztowne robimy poza aplikacją (poniekąd asynchronicznie);
-
pomimo nacisków naszego DB-speca, nie normalizujemy naszego modelu nadmiernie.
Nie mam złudzeń, że w pewnym momencie będziemy musieli troszkę przyciąć nasz radosny bałaganik, ale aplikacja została zaplanowana z tak dużym zapasem, że to na pewno nie nastąpi w ciągu najbliższych kilku miesięcy...
Mała aktualizacja, pod wpływem komentarza Bluszcza — to on to wymyślił. A żeby nie rozpłynął się w samozachwycie, to wymyślił też parę marnych rzeczy, ale tego co marne pozbędziemy się prędzej czy później...
Ktoś (imienia nie wymienię) wysłał klientowi teksty do przetłumaczenia... w Excelu. Klient zadowolony, że nie musi używać żadnych hackerskich narzędzi typu poEdit, oczywiście przetłumaczył teksty w tymże Excelu, a nam przyszło załamać ręce. Ale tylko na chwilę.
Dzięki bibliotece polib załatwiliśmy sprawę w pół godziny. A żeby zaoszczędzić stękań na przyszłość, następne partie tłumaczeń też będziemy wysyłać w Excelu. A co!
Pół dnia spędziłem na szukaniu błędu, który ukrył się w poniższym fragmencie kodu:
try:
contactsIndex = self.names.index('Contact')
contacts = items[contactsIndex]
mail = email_re.findall(contacts)[0]
if mail:
logger_main.debug('Email %s found within ad data' % mail)
report_counters['ads_with_emails'] = report_counters['ads_with_emails'] + 1
except:
mail = False
Wszystko wydawało się w porządku, ale nie było. Klauzula except bez wyspecyfikowania wyjątku zachowuje się tak, że wyłapuje wszystko. Dopiero zapisanie jej w postaci except Exception, e: i zalogowanie wyjątku pokazało, gdzie tak naprawdę ten błąd był. Zupełnie nie tam, gdzie się spodziewałem — klucz słownika report_counters wcale nie nazywał się 'ads_with_emails' (mniejsza o to, jak się nazywał).
Po prostu się nie spodziewałem. Perfidia.
Po Pro Django, Web Development Done Right szykuje się kolejna książka o tej ramówce — James Bennet zaanonsował, że pisze książkę, która omawia Django od strony praktycznej. Książka jest już listowana na Amazon.com, więc sprawa wygląda na poważną. Wypada się tylko cieszyć. Przyda się taka książka wszystkim początkującym.
Jednym z zagadnień przez tę książkę poruszanych ma być pierwsza aplikacja w Django, czyli właśnie silnik blogowy. Ja swój napisałem w ciągu kilku wieczornych sesji, właśnie jako projekt szkoleniowy z migracji na nową wersję Django (choć wcale nie był pierwszą aplikacją, moją pierwszą aplikację można już podziwiać od dłuższego czasu w Rumunii, na Węgrzech i w Wielkiej Brytanii). Trudno jednak wymagać, by ktokolwiek zaczynał zaznajamianie się z Django od dużego, komercyjnego projektu na zlecenie międzynarodowego klienta...
Coraz bardziej niecierpliwię się tym, że Django wciąż nie ma oficjalnego wydania wersji, która miałaby pełne wsparcie dla unikodu i działające newforms. Pojawiły się pogłoski, że następne wydanie to nie będzie oczekiwane przez wszystkich 0.97, ale od razu 1.0 — to by oznaczało, że ilość rzeczy, jakie trzeba będzie zrobić podczas migracji będzie podobna, jak przy 0.91. Tutaj nie ma to wielkiego znaczenia, ale w pracy będziemy mieli dylemat...
Porównując do jednostek pływających, moja poprzednia praca przypominała supertankowiec — wszystko tam szło powoli i majestatycznie, trudno było zmienić kierunek, a każde niepowodzenie było katastrofą, po której ofiary liczono w setkach (jednostek umownych), a jednocześnie statek był w stanie przyjąć ogromną ilość uszkodzeń, zanim miał pójść na dno.
Teraz pracuję w firmie, którą można porównać do motorówki. W porównaniu do innych jednostek pływających zakręca praktycznie w miejscu, porusza się z relatywnie ogromną prędkością, najdrobniejsze uszkodzenie kadłuba spowoduje zatonięcie, a i tak nikt tego nie zauważy.
Jak widać z porównania, trudno wybrać, co lepsze...
Kiedyś o tym ktoś już napisał (może Joel Spolsky, a może ktoś inny, nie mogę sobie przypomnieć). A teraz dotknęło to mnie osobiście.
Są rzeczy proste i są rzeczy skomplikowane. Naturą rzeczy prostych jest to, że łatwo jest ich używać (my to nazywamy, że mają prosty interfejs). Są jednak także rzeczy, które są skomplikowane same z siebie. A jak jest z ich używaniem?
Podobnie jak z rzeczami prostymi, rzeczy skomplikowane są... skomplikowane w użyciu. Rzeczy skomplikowane mają skomplikowany interfejs właśnie dlatego, że ze swej natury są skomplikowane i ich funkcji nie da się wyrazić w sposób uproszczony. Można to porównać do dwóch narzędzi, które służą do wyciągania gwoździ: obcęgów i tzw. łapki. Obcęgi są dość skomplikowanym narzędziem, które spełnia kilka funkcji (na kilka sposobów), więc jego interfejs jest nieco bardziej skomplikowany, niż łapki. Oczekiwanie, że obcęgi będą miały prostszy interfejs, doprowadzi do degeneracji obcęgów do poziomu łapki. Podobnie, nikomu nie wpadnie do głowy, by żądać od 50-tonowej lokomotywy, by miała interfejs Hondy Civic.
A teraz zdążam na skróty ku poincie. Dużo ludzi uważa, że zadaniem nas, czyli developerów, jest zapewnianie prostego interfejsu. Abstrahując od tego, że jest to piramidalną bzdurą, jest to także niemożliwe. Rzeczy skomplikowane nie mogą mieć prostego interfejsu, dopóki są skomplikowane. I nie będą miały prostszego interfejsu, dopóki będą realizować skomplikowane funkcje w skomplikowany sposób. A takie działanie jest chyba immanentne dla aplikacji webowych...
Dzień w plecy z powodu kreatywności firmy Dell. Moja maszynka w pracy to niestety Dell Optiplex 320 — tak, niestety, właśnie ten szajs. Zainstalowanie na niej jakiegokolwiek linuksa graniczy z cudem, a Ubuntu 7.10 zainstalować się na nim nie da wcale z powodu buga w jądrze 2.6.22 (7.04 się udało, pomimo gimnastyki z grub2). Wydaje się, że przyczyną większości problemów jest kontroler SATA firmy (a jakże!) ATi, ale regresja względem poprzedniego wydania to nie jest coś, czego bym się spodziewał...
Każdy, kto programuje w Pythonie dłużej niż kilka godzin, na pewno kiedyś zetknął się z tym:
Python 2.5.1 (r251:54863, Oct 5 2007, 13:36:32)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Często zdarza mi się zapomnieć o jednej z tych zasad: że practicality beats purity. Mamy teraz taki drobiazg do zrobienia, przydałoby się wyjąć z jednego modelu wszystko, co jest związane z jego wyświetlaniem. Oczywiście, nie tylko dlatego, że to łamie podstawową zasadę MVC i miesza model z jego wyświetlaniem — z tego powodu jest to tylko brzydkie, a poza tym to jest też dość niezręczne w zarządzaniu. Wiele już myślałem nad tym, jak wyjąć konfigurację wyświetlania tego modelu żeby wyglądało to ładnie no i chyba właśnie wygrało to practicality — zrobimy oddzielny model na DisplayProperties.
Jak wiadomo, Django używa pakietu Flup żeby uruchamiać aplikacje w środowisku serwera oferującego FastCGI (Apache + mod_fastcgi, nginx, lighttpd). Trafiłem na coś, czego jeszcze do tej pory nie widziałem na oczy. Nigdzie i nigdy.
Nasza aplikacja nigdy nie wyrzuca błędu. Nie mówię o 500, w końcu istnieją aplikacje doskonałe, ale nawet 404, i to pomimo tego, że w kodzie w wielu miejscach leci wyjątek django.http.Http404. W takiej sytuacji aplikacja wyświetla jakąś statyczną stronę (nie ma to nic wspólnego z naszym szablonem 404.html czy 500.html) i zwraca 200. Dogrzebałem się, że jest to efekt działania ErrorMiddleware z Flupa, który w ten sposób próbuje zwrócić na siebie uwagę (przy okazji wysyła maila do administratora, ale u nas to nie występuje...). Podobno występuje to wtedy, gdy błąd taki nie zostanie obsłużony przez aplikację, co w naszym przypadku jest o tyle dziwne, że podobno przecież Django obsługuje wszystkie wyrzucone wyjątki przy użyciu odpowiedniej dla typu wyjątku funkcji.
Co ciekawsze, okazało się, że najnowsza wersja Flup-a (1.0) nie zawiera już tego middleware, więc gdyby się okazało, że Django bez problemu działa z tą wersją, to może oznaczać tylko głębokie problemy z naszą konfiguracją na styku mod_fastcgi i Django.
Zdarza się tak, że domyślny sposób buforowania, używany przez Django (buforowanie całych stron, buforowanie całego serwisu) nie wystarcza i trzeba buforować pojedyncze obiekty lub ich listy. Gdy się już zacznie, trudno jest przestać i wtedy okazuje się, że nie sposób dojść do tego, pod jakim kluczem co jest zbuforowane. Sytuacja komplikuje się dodatkowo, gdy używa się memcache, a już całkiem, gdy w buforze trzyma się dane sesji. Najłatwiej byłoby zrestartować memcached, ale przecież polecą sesje użytkowników... Zaczyna się gorączkowe szukanie klucza i strzelanie w ciemno, ale to naprawdę głupiego robota, bo klient memcache zawsze zwraca 1, gdy nie wystąpi żaden krytyczny błąd. Rozwiązaniem byłoby przejrzenie listy kluczy (np. przeiterowanie przez nie), ale czegoś takiego nie ma.
Wpadłem na pomysł prostego rejestru kluczy w postaci zbioru (set). Zdaje się to działać na tym serwisie (który jest trochę poligonem...), a jak działa w naprawdę dużym serwisie, to się dopiero okaże jutro, jak sprawdzę to w pracy. Na razie jestem dobrej myśli, bo idea zdaje się nie mieć słabych punktów... ;)
Spotkałem się w niedzielę z pewnym człowiekiem... Zaproponował mi wzięcie udziału w dużym, celującym wysoko międzynarodowym projekcie serwisu, oczywiście nie za darmo. Sama propozycja bardzo mile podłechtała moją próżność (podobnie jak kilka miesięcy wcześniejsza propozycja wzięcia udziału w rekrutacji do Google), więc bardzo się nie opierałem przed osobistym spotkaniem, pomimo tego, że miało ono nastąpić w sobotę lub w niedzielę. Katalizatorem dobrego nastawienia do propozycji było to, że człowiek ten przyjechał specjalnie w niedzielę z Krakowa, żeby spotkać się ze mną i z jeszcze jedną osobą z nieodległej firmy (technologicznie i geograficznie), którą też chciał w ten projekt wciągnąć. Umówiliśmy się na 17:00 w Złotych Tarasach, żeby omówić projekt i kilka wstępnych ustaleń.
Człowiek okazał się bardzo miłym młodzieńcem, naprawdę zapalonym do swojego pomysłu. Przedstawił swoją wizję dość klarownie, jednak próba pokazania mi działającego dema zakończyła się katastrofą i nie zobaczyłem nic, oprócz strony z debugiem błędu w Django. Pierwsze skojarzenie, jakie mi się nasunęło, to katastrofa, której doznaliśmy w lipcu, gdy próbowaliśmy pokazać klientowi aplikację na Węgrzech — wtedy przez 5 minut było OK, ale potem wszystko zaczęło się walić. Tutaj nie było nawet tych pięciu minut, co przy założeniu, że aplikacja ma być gotowa za miesiąc, oznaczać może tylko jedno — stan aplikacji jest bardziej niż niepokojący. Z innych rzeczy pozaproduktowych, kwestię finansową przemilczę, bo moja matka twierdzi, że przewróciło mi się w głowie i straciłem kontakt z rzeczywistością. Wystarczy że napiszę, że nie miałbym lepiej, niż mam teraz.
A teraz o samym produkcie. Nie poruszyła mnie jego idea. Być może jestem za stary na takie gwizdki, być może przez cały czas myślę jak człowiek z trzeciego świata, ale faktem jest, że nie poruszyła mnie wizja milionów ludzi płacących po $10 rocznie za coś, co tylko podbudowuje ich ego. Bardzo możliwe, że w ten sposób działają nastolatki, bardzo też możliwe, że postępujące otumanienie społeczeństw konsumpcyjnych idzie w parze z zatrzymaniem rozwoju mentalnego na poziomie nastolatków. Wiem, że można na tym robić pieniądze (vide przykład MySpace czy Facebook) i to nawet ogromne, jednak nie przekonuje mnie to. Wydaje mi się, że na długi czas zatrzymałem się na poziomie Web1.0 i z dużym trudem doszlusowałem do poziomu Web2.0, nie połykając go w całości jak leci, a jedynie przyswajając sobie kluczowe idee. Z każdą kolejną iteracją główna idea internetu wydaje mi się odchodzić w kierunku coraz głębszego autyzmu, a projekt przedstawiony przez tego człowieka wydaje mi się tak autystyczny, że bardziej już nie można. Więcej o filozoficznych podstawach mojego poglądu na rozwój internetu pewnie jeszcze kiedyś napiszę.
Konkudując w skrócie, mam jeszcze kilka godzin na zastanowienie się, ale nie przypuszczam, żeby cokolwiek było w stanie zmienić moją decyzję. Nie wchodzę w to.
Zawsze mnie zastanawiało, skąd się biorą badziewne serwisy WWW, zrobione na zasadzie prawie jak..., po których natychmiast widać, że są marną podróbką czegoś-tam. Wydawało mi się, że pomimo różnic w przymiotach intelektualnych (nie, nie jesteśmy sobie równi pod tym względem), istnieje pewien minimalny poziom dobrego smaku połączony z minimalnym poziomem rozsądku, którym posługuje się przytłaczająca większość ludzkości i takie wpadki nie są wynikiem celowego działania, bo przecież nikt specjalnie nie strzelałby sobie w stopę. No i właśnie niedawno doznałem zderzenia z rzeczywistością. Nie ma czegoś takiego. Zasada wydaje się prosta — jeżeli jest jakaś głupota, której klient może sobie zażyczyć, to sobie zażyczy. Można próbować mu wyperswadować taki pomysł, można próbować podsuwać mu rozsądne rozwiązania, które zmniejszą poziom głupoty jego propozycji, ale trzeba założyć, że skutek tego będzie żaden. W dodaku, raz wszedłszy na ścieżkę produkcji g*wna, nie da się z niej zejść tak łatwo i dodawanie kolejnych ficzerów powoduje tylko narastanie tej sterty. W takim przypadku trzeba się cofnąć o te kilka kroków, naprawić popełnione wcześniej błędy (najczęściej przez usunięcie tego, co kontrowersyjne) i dopiero wtedy można mówić o uwolnieniu się.
Jestem rozgoryczony, to prawda. Niewiele brakowało, a byłby serwis WWW, którym mógłbym się pochwalić znajomym i być z niego dumny na zasadzie jam ci to sprawił. I g*wno. Z powodu idiotycznych żądań klienta musieliśmy zrobić z niego koszmarek i nie dało się tego wyperswadować, nie dało się też przekonać klienta do naszych propozycji. Patrząc od strony biznesowej wszystko niby jest w porządku, pieniądze przepłynęły z jednego konta na drugie, klient jest zadowolony, firma jest zadowolona — ale ja nie jestem. Przez cały czas mam wrażenie, że przyłożyłem rękę do produkcji szajsu. którego jeszcze długo będę się wstydził, bo przecież nikogo tak naprawdę nie interesuje, ile włożyłem w to zaangażowania i pracy, przecież tak naprawdę liczy się efekt końcowy. W tym przypadku — opłakany i jest to oczywiste dla każdego, kto w internecie spędza trochę więcej czasu, niż trzeba na przejrzenie newsów na BBC, CNN czy Onecie. Pół roku pracy bez efektu, z którego można być zadowolonym już raz doprowadziło do tego, że zmieniłem pracę (a warunki na rynku pracy wtedy były znacznie gorsze niż teraz). Niestety, po około 10 latach pracy w branży nie spodziewam się, by gdziekolwiek sytuacja przedstawiała się lepiej. No, może tylko w Google, ale też pewien nie jestem...
A co gorsza, utknąłem w tym szajsie na dobre, trochę przez swoje wcześniej wspomniane zaangażowanie, a trochę z powodu braku zasobów. Pociesza mnie tylko to, że to wciąż Python.
Jeśli edytor, to wiadomo: Vim. A jeśli pisanie w nim kodu, to wiadomo, co jest jedną z podstaw: podświetlanie składni tak, żeby wszystko było dobrze widoczne, a jednocześnie w takim zestawie kolorów, który nie męczy oczu, nawet wtedy, gdy trzeba patrzeć w ekran po kilka godzin. W takich przypadkach wygrywają zestawy raczej mało kontrastowe i raczej te z ciemnym tłem. Oto trzej moi faworyci, w kolejności niekoniecznie przypadkowej:
-
oceandeep, utrzymany w zielono-niebieskiej tonacji, prawie bez mankamentów;
-
desert, brązowo-beżowy, cokolwiek nudny, ale bardzo spokojny;
-
zenburn, faworyt wielu, dla mnie trochę więcej niż akceptowalny, jakoś nie przekonuje mnie aż tak niski kontrast, ale czasem (szczególnie późno w nocy) jest ukojeniem dla oczu.
Szukam, bo mamy w firmie znaczące niedobory kadrowe. Od razu zaznaczę -- wystarczy pobieżna znajomość Django (w szczególnych przypadkach nawet żadna), podstawy Pythona, wskazane jedynie jest dość dobre obeznanie w sprawach programowania aplikacji webowych (obojętne w czym: Java, .Net, RoR, chodzi o znajomość tematu).
Szczegółowe ogłoszenie wraz z danymi kontaktowymi znajduje się w archiwum Google Groups (po kliknięciu w skrót adresu trzeba wpisać kod z captchy, żeby obejrzeć pełny email kontaktowy). Ogłoszenie jest sprzed miesiąca, ale tym się przejmować nie należy. ;)
Zamierzam (i to poważnie) przejść na zawodową emeryturę za około 20 lat, więc wypadałoby już teraz zacząć się zastanawiać, czym się wtedy zajmę. Mam kilka wizji, głównie opartych na przeświadczeniu, może błędnym, a może jednak nie, że inni ludzie mogą lubić to, co i ja. Więcej nawet, że są skłonni zapłacić za te przyjemności, które ja im z przyjemnością będę dostarczał. Pisania programów w to nie wliczam, pozbyłem się już złudzeń...
Może jedzenie? Weekendowe biesiady na Podlasiu, pełne naturalnej żywności. Świeże warzywa, świeże mięso, świeży nabiał. I atrakcje (nie tylko kulinarne), jakich nie zazna się w normalnej restauracji na mieście — jak pieczenie chleba czy kolacja przy lampach naftowych. W ciągu najbliższych 20 lat najprawdopodobniej zmieni się zarówno stan polskiej gospodarki, jak i okolice, w których chciałbym otworzyć to przedsięwzięcie, jednak pomyśleć nie zaszkodzi. W każdym razie, mając dziś perspektywę nieodległej emerytury (moja jeszcze jest odległa), wziąłbym to rozwiązanie pod uwagę, jestem w stanie wyobrazić siebie w takiej roli.
Na pewno będę miał jeszcze wiele wizji i pomysłów, a ten jest raptem drugi czy trzeci...
Ja muszę być jakimś fanatykiem, czy innym zboczeńcem... Kiedy kilka tygodni temu zastanawialiśmy się z moim PM-em, czy mam zająć się nowym projektem, czy może pozostać w obecnym i trzymać go za mordę, sam zaproponowałem, że zostanę, bo znam go dobrze i szkoda byłoby marnować taki kawał wiedzy. Teraz mam za swoje — na trzech instancjach produkcyjnych ciągły pożar, w planie rozwoju dwa duże kawały kodu do napisania, a ja spędzam większość czasu robiąc jako administrator i release manager jednocześnie, z otwartymi kilkunastoma sesjami SSH.
Ale spoko, jest dobrze. Większość kodu to Python + Django, więc i tak jest lepiej, niż w Generali. Pod względem ilości pracy sytuacja tam była dość luksusowa, ale co to była za praca... ;)
Niewielka pociecha z tego piątku... Po całym tygodniu ciągłego stresu nie mam siły zająć się swoimi sprawami. Przyjdę do domu i może zrobię archiwum do tego bloga. O ile wystarczy mi na to siły.
Jeszcze dziś rano miałem masę pomysłów na zabawne ficzery, które mógłbym tutaj dorobić... ale po 17 (i po całym dniu pracy) już mi się nie chce. Może sobota lub niedziela będą bardziej łaskawe dla Pythona i Django. ;)
Zakończyliśmy wreszcie aktualizowanie aplikacji. Trwało to 4 dni, od poniedziałku po południu do czwartku po południu. Kiedy wreszcie nam się udało, po trzech nieudanych podejściach, miałem mózg jak gąbka. Nie takie coś do kąpieli, lecz jak to stworzenie, co żyje w morzu. Gąbka, jedno z najbardziej prymitywnych stworzeń wielokomórkowych. A potem poszedłem się spotkać z kolegą, żeby go przekonać do pracy u nas i zupełnie się już wyluzowałem.
Nie obchodzi mnie, co będzie jutro, na pewno nie będzie gorzej, niż było przez ostatnie 3 dni. To znaczy, że będzie całkiem dobrze.
Nie miałem dotąd wielkiej możliwości nadzorowania aktualizacji oprogramowania na serwerach u klienta... ale zawsze musi być ten pierwszy raz. W końcu jestem dyrektorem aplikacji. ;)
Na szczęście aplikacja nie jest duża. Na szczęście nie jest bardzo poważna. Na nieszczęście jest dość skomplikowana w swojej architekturze.