• Яндекс-Бары

    rats

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

    Я в курсе, что установку бара можно отключить, но эти галки всегда отмечены и порой даже спрятаны. Неверный клик — и все браузеры окажутся заражены. У меня, как у веб-разработчика, в системе 4 браузера. Мне проще отказаться от установки, чем напряженно проверять, куда на этот раз запрятана галка отключения бара.

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

    Кто сказал, что бар — не вирус? Он проникает на компьютер обманным путем (пользователь не понимает, откуда взялся бар), устанавливает службу для самовосстановления (Мейл.ру, горите в аду), уведомляет центральный сервер обо всех ваших действиях (шпионит), занимает лишние ресурсы, не приносит никакой пользы — курсы валют есть на любом портале, виджет погоды у всех на рабочем столе, поле поиска есть во всех браузерах.

    Именно поэтому новость «Яндекс» закрывает «Бар» вызывает дикий, неистовый восторг, хотя на практике оказывается, что вместо старого трояна будут выпускать новый.

    Кстати, при установке в Фаерфокс бар изменяет параметры, не доступные для редактирования в интерфейсе. Исправить их можно только в about:config, а это значит, что рядовой пользователь фактически обречен жить с баром до следующей переустановки Винды.

    Удаляйте Аимпы, Панто-свитчеры и прочую хрень с барами.

  • Прием смс

    Рассылать смс сегодня нетрудно — регистрируемся в одном из многочисленных сервисах рассылок, вносим сумму и вперед. А как принимать смс от граждан в автоматическом режиме?

    Короткие номера не подходит, ибо они навек закрепили за собой репутацию лохотрона. К тому же нужно вводить префикс, а минимальная стоимость 4 рубля. Принимать только на короткие номера — верх свинства по отношению к абонентам.

    А ведь все можно сделать своими руками! Берем простой 3Г-модем, втыкаем в него местную симку (я взял Мегафон), подключаем к компьютеру, ставим драйвера. Проверяем, появился ли в системе модем.

    Качаем замечательную утилиту Гамму, а конкретно — виндовые бинарники (не Minimal, а обычные). Распаковываем.

    Утилита gammu может рулить модемом программно, например, читать и отправлять смс, совершать звонки, делать бекапы содержимого телефона и массу других полезных штук. В комплекте идет gammu-smsd — демон для чтения смс в фоновом режиме. Он постоянно проверяет наличие смс, складывает их в базу данных и вызывает заданную команду в момент прихода новой смски.

    Теперь о том, как все настроить. В папке bin создаем файл smsdrc следующего содержания:

    [gammu]
    device = COM6:
    connection = at
    
    [smsd]
    Service = sql
    Driver = native_mysql
    User = user
    Password = password
    PC = xxx.xxx.xxx.xxx
    Database = smsd
    PIN = 8730
    LogFile = smsd.log
    RunOnReceive = smsd_callback.bat
    

    Стартуем демона:

    gammu-smsd --config smsdrc
    

    Секция [gammu] в конфигурации отвечает за параметры соединения с устройством. device — номер КОМ-порта, узнать его можно в свойствах модема, connection — тип соединения, для 3Г-модема обычно at.

    Раздел [smsd] содержит параметры демона. Как видно, в качестве базы я использую Мускуль. Опция PIN задает пин-код симки (если защита ПИН не отключена). LogFile — текстовый файл логов.

    Самое интересное — RunOnReceive, это имя команды, скрипта или батника, который будет вызываться при получении смс. Параметры сообщения передаются в переменных окружения SMS_1_NUMBER, SMS_1_TEXT и др.

    В батнике у меня прописан вызов питоньего скрипта:

    start pythonw path\to\smsd_callback.py
    

    Почему бы сразу не вызвать скрипт? Потому, что вызов осуществляется в том же потоке, что и чтение смс, поэтому пока не завершится процесс команды, чтение будет приостановлено. А в данном случае батник вызывает скрипт и сразу закрывается.

    Фрагмент кода на питоне:

    number = os.environ["SMS_1_NUMBER"]
    message = os.environ["SMS_1_TEXT"]
    
    msg_pattern = r"\d+"
    account, value = re.findall(msg_pattern, message)[:2]
    
    statement = Statement(account=account, value=value)
    statement.save()
    
    answer = u"Принято. Абонент %s, показание %s" % (account, value)
    
    send_sms(answer, number)
    

    Получаем номер и сообщение, находим в нем пару чисел — лицевой счет и показание прибора. Сохраняем в базу и отвечаем абоненту, что все в порядке.

    Комментарии из старого блога

    11/04/13 Сергей: Статья интерсная, много нового узнал, пытаясь запустить. Но нет ничего про структуру базы. Мануалов гаммы на русском нет. Вот и как бы статья по прямому предназначению “Прием смс” (профи и без статьи организует) пока не очень полезна.

    11/05/13 Иван Гришаев: Структуру базы можно посмотреть в .sql-файле, который лежит где-то в папке с программой.

    11/07/13 Сергей: Спасибо. Теперь вот такая картина в логфайле (тут я баран)) поможете??? подключаю 3g модем - железка от алкател.

    Thu 2013/11/07 15:09:29 gammu-smsd[2452]: Connected to Database: smsd on localhost
    Thu 2013/11/07 15:09:29 gammu-smsd[2452]: Connected to Database native_mysql: smsd on localhost
    Thu 2013/11/07 15:09:29 gammu-smsd[2452]: Created Windows RW shared memory at 003A0000
    Thu 2013/11/07 15:09:29 gammu-smsd[2452]: Starting phone communication...
    Thu 2013/11/07 15:09:40 gammu-smsd[2452]: Error at init connection: No response in specified timeout. Probably phone not connected. (TIMEOUT[14])
    Thu 2013/11/07 15:09:40 gammu-smsd[2452]: Already hit 250 errors
    Thu 2013/11/07 15:09:40 gammu-smsd[2452]: Terminating communication: No response in specified timeout. Probably phone not connected. (TIMEOUT[14])
    Thu 2013/11/07 15:09:40 gammu-smsd[2452]: Starting phone communication...
    Thu 2013/11/07 15:09:50 gammu-smsd[2452]: Error at init connection: No response in specified timeout. Probably phone not connected. (TIMEOUT[14])
    Thu 2013/11/07 15:09:50 gammu-smsd[2452]: Already hit 250 errors
    Thu 2013/11/07 15:09:50 gammu-smsd[2452]: Terminating communication: No response in specified timeout. Probably phone not connected. (TIMEOUT[14])
    Thu 2013/11/07 15:09:50 gammu-smsd[2452]: Starting phone communication...
    

    11/07/13 Иван Гришаев: Пишет, что нет соединения с телефоном. Сначала нужно указать проге, какой порт слушать. Создайте файл Gammu\bin\gammurc (без расширения), в нем пишете:

    [gammu]
    device = COM6:
    connection = at
    

    только вместо COM6 - ваш порт. Двоеточие на конце нужно.

    11/07/13 Сергей: Я так и делал, модем откликается (в терминале) на сом3,4,5 в диспетчере оборудования модем=сом5.

    [gammu]
    device = COM
    

    пробовал все результат почти один, 3 и 5 сукунд через 10 закрывается гамми, ком4 каждые 10-20 сек перепроверяет соединение с телефоном, пока сам не закрою.

    11/07/13 Иван Гришаев: Вспомнил - для смс-демона нужен отдельный конфиг! Gammu\bin\smsdrc

    У меня он такой:

    [gammu]
    device = COM5:
    connection = at
    
    [smsd]
    Service = sql
    Driver = native_mysql
    User = username
    Password = passowrd
    PC = 172.27.86.8
    Database = smsd
    
    LogFile = smsd.log
    RunOnReceive = some_bat_file.bat
    CheckSecurity = 0
    CheckBattery = 0
    

    11/07/13 Сергей: Не помогло добавление Check ов. Нарыл гдето другой терминал, вот он АТ командами смски вытягивает. А можно както на питоне опросить АТ командами com порт? Думаю так проще будет.

    11/07/13 Иван Гришаев: Конечно можно, есть либа для ком-порта: http://pyserial.sourceforge.net/

    Кстати, вы пробовали http://www.simplesms.ru/ ? Прога очень хорошая, разработчик - русский, есть документация и форум.

    11/07/13 Сергей: Хороший вы (я не против и на ты) человек. С библиотекой буду разбираться (инглиша не знаю), а платные вещи отпадают я имено хочу разобраться и сделать - типа хобби). Можно вообще не париться - 800р/мес и прикрутить их API к своему сервису, я так и делал, но как то не уютно))).

    11/07/13 Сергей: надо же скрипт php заработал, раньше порты были 14,15,16 и не работало. Попробую на php получалку сделать!

    $telnumb='904*****37';
    $sms_text='poluchilos';
    $format = "AT+CMGF=1\r\n";
            $tel = "at+cmgs=\"+7".$telnumb."\"\r\n ";
            $text = $sms_text.chr(26)."\r\n ";
            $fp = fopen ("COM3:", "w");
            flock($fp,LOCK_EX);
            if(!$fp){
                    echo "COM-Port not opened";
            }
            else{
                    fwrite($fp, $format);
                                    sleep(1);
                    fwrite($fp, $tel);
                                    sleep(1);
                    fwrite($fp, $text);
                                    sleep(1);
                    flock($fp,LOCK_UN);
                    echo $format.$tel.$text;
            }
            $fp = fclose ($fp);
    

    11/07/13 Иван Гришаев: Сообщения с кириллицей нормально приходят? А сообщения из нескольких частей?

    11/07/13 Сергей: Нет под w1251 все усложняется, там меняется алгоритм сборки, но это решаемо. Мне прием нужен, его и ковыряю. На php отправить оказывается просто, а вот принять “одна темнота” пока, т.что гамми, питон не закрываю.

    11/07/13 Сергей: Так то все дожно быть просто. Всего то нужно в порт заслать "AT+CMGL=4\r\n" и получить все что он отдаст. А потом раскодить, распарсить. Ну нннникак.

    11/08/13 Иван Гришаев: Да, кириллица хранится в последовательностях UFT-16, кажется. Придется декодировать.

    11/07/13 Сергей: Еще об отправке, там без подтверждения получения смс абонентом и по той же причине - читать порт не получается.

    11/07/13 Сергей: simplesms пробовал скачать - антивирус заблокировал(((

    11/08/13 Иван Гришаев: А вы разблокируйте.

  • 1С и JSON в продакшене

    Еще раз обновил функцию перевода структур 1С в JSON: http://pastebin.com/3NwCDCTD

    Исправлены косяки:

    • нулевые числа форматируются корректно;
    • в строках корректно обрабатываются управляющие символы (\n, \t и др);
    • символы Юникода не переводятся в последовательности вида \uxxxx, потому что это долго (хотя позволяет избежать проблем с кодировкой).

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

    Комментарии из старого блога

    12/25/12 Alex: На вскидку Вы забыли про разделитель триад в числах. И я советовал бы XMLЗначение (пишу по памяти) вместо Формат - и быстрее и меньше ошибок будет.

    12/25/12 Иван Гришаев: Разделитель убран:

    ИначеЕсли Тип = Тип("Число") Тогда
                    Возврат Формат(Объект, "ЧРД=.; ЧН=0; ЧГ=0");
    

    ЧГ=0 убирает разделитель разрядов. Он же не нужен.

    Вы имели в виду XMLСтрока(). Можно и так, но, во-первых, исчезает ясность (что получается на выходе), а во-вторых, в некоторых случаях он не подходит, например, Неопределено преобразуется в пустую строку, а нужно в null. И не все типы данных поддерживаются.

  • Автоматическая выгрузка вложений

    Getmail — потрясающая утилитка, сэкономившая мне массу времени и нервов. Она сохраняет электронные письма на диск, попутно распаковывая вложения.

    Сказать, что это удобно, значит, ничего не сказать. Ведь, казалось бы, простую задачу — обеспечить автоматическую выгрузку вложений из писем — почтовые клиенты выполнить не в состоянии без установки плагинов и костылей. А с помощью getmail проблема решается несколькими строками в батнике:

    d:
    cd \.attachments
    getmail -u user@server.ru -pw "password" -s server.ru -xtract -delete
    del MSG*.TXT
    del Extract*.out
    

    Указываем, папку, выгружаем, чистим всякий мусор, далее работаем с аттачами.

  • 1С и JSON: работа над ошибками

    UPD: новая версия

    Пилотная версия функции преобразования данных 1С в JSON оказалась вполне удачной. Делюсь новой версией: http://pastebin.com/y2K5V3Xt

    Исправления:

    • типы Неопределено и Null преобразуются в null;
    • добавлен тип СписокЗначений;
    • исправлен ISO-формат даты;
    • строки преобразовываются в последовательности вида \uxxxx.

    Напомню, что код состоит из одной функции и не зависит от сторонних модулей и компонент.

    Пример:

    с = новый СписокЗначений;
    с.Добавить("Привет!");
    с.Добавить(42);
    с.Добавить(ТекущаяДата());
    с.Добавить(Неопределено);
    с.Добавить(Null);
    с.Добавить("!№2№%Ё~{}");
    с.Добавить(новый Структура("поле1, поле2", ВидДвиженияБухгалтерии.Дебет, новый Массив(3)));
    Сообщить(json(с));
    

    Результат:

    [
      "\u041f\u0440\u0438\u0432\u0435\u0442!",
      42,
      "2012-11-13T10:17:55",
      null,
      null,
      "!\u21162\u2116%\u0401~{}",
      {
        "\u043f\u043e\u043b\u04352": [
          null,
          null,
          null
        ],
        "\u043f\u043e\u043b\u04351": "\u0414\u0435\u0431\u0435\u0442"
      }
    ]
    

    Комментарии из старого блога

    11/21/12 Артемий Зайцев: А зачем все символы заменять на подстановки \u0414 ? Вместо “N” (1 байт) получим замену в 6 байт при передаче по интернету. Вся прелесть JSON теряется. XML становится выгоднее.

    11/21/12 Иван Гришаев: Заменяются не все символы, а только те, что не входят в диапазон 0x20-0x7f (A-z, 0-9). Если у вас английский текст, то разницы не будет.

    \uxxxx - это код символа Unicode. Согласно стандарту JSON строки должны быть представлены Unicode-последовательностями. Это правило можно нарушать, но тогда никто не гарантирует корректный разбор JSON-документа со строками, скажем, на украинском.

    Преимущество JSON перед XML не в меньшем объеме данных, а в удобстве сериализации и парсинга. Одной функцией структура сворачивается в JSON, другой – обратно. Генерация и парсинг XML занимают больше времени и сил.

    11/21/12 Артемий Зайцев: Проверил с разными языками. В UTF всё работает. Еще у вас подстановок спецсимволов в строке. Если в тексте есть знаки / \ “, то JSON ломается.

    Сделал как-то так:

    Если Тип = Тип("Строка") Тогда
    	Возврат """" + MyJSONМаскировать(Объект) + """";
    ....
    Функция MyJSONМаскировать(Знач Стр)
    	Стр=СтрЗаменить(Стр,"\","\\");
    	Стр=СтрЗаменить(Стр,Символы.ПС,"\n");
    	Стр=СтрЗаменить(Стр,Символы.ВК,"\r");
    	Стр=СтрЗаменить(Стр,Символы.Таб,"\t");
    	Стр=СтрЗаменить(Стр,"/","\/");
    	Стр=СтрЗаменить(Стр,"""","\""");
    	Возврат Стр
    КонецФункции
    

    11/21/12 Иван Гришаев: Да, спасибо, только что сам обнаружил, что с кавычками косяк, нужно их экранировать заменой, как у вас. Завтра выложу новую версию функции.

    05/31/13 Кирилл: Спасибо автору!

    05/31/13 Зайцев Артемий Анатольевич: Кирилл, рано автора хвалить. Он только начинает учить 1С. Есть еще ошибки: (Формат(Объект, "ДФ=yyyy-MM-ddThh:mm:ss") - после буквы T ничего не пишет.

    05/31/13 Иван Гришаев: Вы комментируете старую версию функции. Вот новая. Там много чего исправлено. Эта крутится в продакшене уже больше года.

    05/31/13 Зайцев Артемий Анатольевич: Иван. В новой тот же косяк с датой:

    Возврат json(Формат(Объект, "ДФ=yyyy-MM-ddThh:mm:ss"));
    

    надо как-то:

    Возврат json(Формат(Объект, "ДФ=yyyy-MM-dd") + "T" + Формат(Объект, "ДФ=hh:mm:ss") );
    

    05/31/13 Иван Гришаев: Похоже, вы правы, спасибо. В моем проекте часы и минуты не нужны, не обратил внимания.

  • Личный кабинет Читаэнергосбыта для Андроида

    icon

    Выложил в Маркет свое первое приложение — личный кабинет абонента Читаэнергосбыта.

    Веб-версия кабинета работает уже второй год, на момент написания заметки зарегистрировано 3500 абонентов, передано 4000 показаний приборов, зарегистрировано 250 заявок.

    Теперь все это удобство доступно в смартфоне.

    На создание, тестирование и отладку ушло 3 месяца. Приложение взаимодействует с нашим сервером HTTP запросами, трафик копеечный.

    Так что если вы наш абонент, милости прошу установить и пользоваться.

    Комментарии из старого блога

    01/12/13 obmorrock: А где кроме маркета можно скачать apk? А то у меня на смарте маркет выпилен, а установить хочется….

    01/12/13 Иван Гришаев: Выслал почтой.

    04/23/13 Иванов Сергей: Уважаемый Иван! Установил и пользовался вашим приложением несколько месяцев всё работало прекрасно, но более недели уже не могу им воспользоваться. При нажатии “Войти” выдаёт сообщение “Ошибка сервера” при этом приходит сообщение, что “выполнен вход в личный кабинет дата время”. Что случилось?

    04/23/13 Иван Гришаев: Здравствуйте! С самим приложением все в порядке, неполадки возникают на стороне сервера, который возвращает данные. Я уже не работаю в Читаэнергосбыте, поэтому исправить ошибку не смогу, но дам знать коллегам.

    04/23/13 Иванов Сергей: Спасибо.

  • JSON-сериализация в 1С

    UPD: новая версия функции.

    Быдлоплатформа 1С не умеет работать с JSON, MD5, SHA, чем огорчает веб-разработчика. Ничего, научим: http://pastebin.com/4tF96gQH

    Пример:

    Запрос = новый Запрос(
    	"ВЫБРАТЬ ПЕРВЫЕ 5
    	|	Договоры.Код,
    	|	Договоры.Ссылка,
    	|	Договоры.ДатаЗаключения,
    	|	Договоры.Комментарий,
    	|	Договоры.Предопределенный
    	|ИЗ
    	|	Справочник.Договоры КАК Договоры"
    );
    с = новый Структура("foo, bar, baz, tab", 1, "Привет!", ТекущаяДата(), Запрос.Выполнить().Выгрузить());
    Сообщить(json(с));
    

    Результат:

    {
        "foo": 1,
        "bar": "Привет!",
        "baz": "2012-11-09T10.50.29",
        "tab": [
            {
                "Код": "ВСООЦ0050133",
                "Ссылка": "ВСООЦ0050133",
                "ДатаЗаключения": "2012-09-14T12.00.00",
                "Комментарий": "",
                "Предопределенный": false
            },
            {
            ...
            }
        ]
    }
    

    TODO: добавить некоторые типы данных (выборка из результата запроса, наборы записей).

  • Степень схожести строк (коэффициент Танимото)

    Иногда нужно определить степень схожести двух строк, т.е., насколько одна строка похожа на другую. Например, у вас есть список названий фильмов. Источники разные, нет никаких уникальных идентификаторов, только заголовки. При этом стоит учесть, что даже самые простые названия могут быть написаны по-разному: «ЗАЛОЖНИЦА 2» и Заложница 2. Очевидно, что сравнение этих строк в лоб ничего не даст: лишние символы, разный регистр.

    Строки можно рассматривать как множество символов. Две строки — два множества. Коэффициент Танимото позволяет определить степень схожести двух множеств:

    screenshot

    , где:

    • k — сам коэффициент от 0 до 1;
    • a — количество элементов в первом множестве;
    • b — количество элементов во втором множестве;
    • c — количество общих элементов в двух множествах.

    Изящная функция на Питоне:

    def tanimoto(s1, s2):
        a, b, c = len(s1), len(s2), 0.0
    
        for sym in s1:
            if sym in s2:
                c += 1
    
        return c / (a + b - c)
    

    Пара примеров:

    print tanimoto('Иван Гришаев', 'Гришаев И.В.')
    0.846153846154
    
    print tanimoto('tanimoto test 1', 'testing Tanimoto #2')
    0.7
    

    В Питоне работает т.н. утиная типизация, когда важен не тип объекта, а его поля и методы. Нетрудно заметить, что код функции требует от объектов методов длины, вхождения элемента и итерации. Это значит, что вместо строк можно передавать списки, кортежи, словари и другие структуры:

    print tanimoto(
        (1, 2, 3, 4, 5, 4),
        (4, 2, 5, 4, 9, 3),
    )
    0.714285714286
    

    Конечно, расчет коэффициента нужно производить с умом. Например, вычистить регулярными выражениями лишние символы или передавать строки в одинаковом регистре. И конечно, лучше оперировать строками в юникоде.

    Возвращаясь к нашему примеру:

    print tanimoto(
        u'«ЗАЛОЖНИЦА 2»'.lower(),
        u'Заложница 2'.lower(),
    )
    0.846153846154
    

    Величина 0.85 говорит о том, что оба названия указывают на один и тот же фильм. Однако, не лишним будет на всякий случай сверить год выпуска.

  • Работа с DBF в Питоне

    Формат DBF, хоть и признан устаревшим, до сих пор широко используется в финансовых учреждениях. Он подходит для обмена табличными данными, такими, например, как отчеты и реестры платежей. Формат имеет простую логическую структуру, поэтому библиотеку для работы с ним можно написать самостоятельно. Классический DBF поддерживает основные типы данных — числа, строки, даты и булево. Благодаря этому дата, записанная в DBF, будет считата как дата, а не текстовая строка, которую будет необходимо парсить.

    Для работы с файлами DBF в Питоне потребуется библиотека dbfpy. Это, кстати, не единственное решение, есть еще библиотеки pyDBF и dbf. Сам я работал только с первой из трех, функционал полность устраивает.

    Приведу пару примеров: чтение и запись файла.

    from dbfpy import dbf
    
    # Открываем имеющийся файл.
    db = dbf.Dbf("dbfile.dbf")
    
    # Обход всех записей.
    for rec in db:
        print rec
    
    # Извлечение записи по номеру.
    rec = db[42]
    
    # Значения полей извлекаются по их именам.
    value = rec["VALUE"]
    account = rec["ACCOUNT"]
    date = rec["DATE"]
    
    # Всегда закрывайте файл.
    db.close()
    

    Запись нового файла немного сложнее, так как потребуется указать схему данных. Фрагмент кода из реального проекта:

    from dbfpy import dbf
    
    # Определяем набор полей файла. C — строка, N — число, D — дата, L — булево.
    # Для строк нужно указать длину, для чисел — количество разрядов целой и дробной частей.
    SCHEMA = (
        ("PSU",        "C", 64   ),
        ("TSO",        "C", 64   ),
        ("PAYMENT",    "N", 16, 0),
        ("SERVICE",    "N",  8, 0),
        ("DATE",       "D"       ),
        ("MRO",        "C", 64   ),
        ("TERMINAL",   "N",  8, 0),
        ("ACCOUNT",    "C", 16   ),
        ("SUMMARY",    "N", 16, 2),
        ("OPERATOR",   "N",  8, 0),
        ("SUPPLIER",   "N",  8, 0),
        ("TIME",       "C",  8   ),
        ("LAST_NAME",  "C", 64   ),
        ("FIRST_NAME", "C", 64   ),
        ("MIDDLE_NAM", "C", 64   ),
    )
    
    db = dbf.Dbf("dbfile.dbf", new=True)
    db.addField(*SCHEMA)
    
    # Обходим в цикле данные, которые необходимо записать в DBF.
    # В данном случае payments — объект QuerySet из Джанго.
    # На каждом шаге цикла создается новая запись и заполняются ее поля.
    for payment in payments:
        rec = db.newRecord()
        rec["ACCOUNT"] = payment.account
        rec["FIRST_NAME"] = payment.first_name.encode("cp866")
        rec["date"] = payment.date
    
        # Заполняем остальные поля...
    
        # Сохранение записи.
        rec.store()
    
    db.close()
    

    Готово, файл создан.

    Наверняка вы обратили внимание на кодирование поля payment.first_name. Это unocode-строка, и для того, чтобы записать ее в файл, необходимо сперва преобразовать в обычную строку в какой-то кодировке. Исторически сложилось так, что основной кодировкой для этого формата является OEM, она же DOS, она же cp866. Многие программы, например, офисные Эксель и Акцесс работают с DBF-файлами именно в этой кодировке. Чтобы созданные вами файлы читались в сторонних программах, преобразовывайте перед записью строки с кириллицей в cp866.

    Работать с DBF можно как с SQL-базой через драйвер ODBC. При этом именем таблицы является имя файла. Доступны все CRUD-операции на записями. Склеить два DBF-файла оператором JOIN, к сожалению, нельзя. Для работы с ODBC-источниками скачайте библиотеку pyodbc.

    import pyodbc
    
    # Строка, определяющая источник ODBC. DefaultDir — директория с DBF-файлом.
    s = "Driver={Microsoft dBASE Driver (*.dbf)};DefaultDir=d:\\temp"
    
    db = pyodbc.connect(s, autocommit=True)
    cursor = db.cursor()
    
    # Пример выборки.
    cursor.execute('''
    SELECT
        value,
        account,
        date
    FROM
        dbfile
    WHERE
        processed = 'false'
    ''')
    
    # Извлечение записи.
    row = cursor.fetchone()
    if row:
        print row
    
    # Закрываем курсор и ресурс.
    cursor.close()
    db.close()
    

    Вставка, удаление и обновление записей реализуются аналогично согласно SQL-синтаксису:

    cursor.execute('''
    DELETE FROM
        dbfile
    WHERE
        processed = 'false'
    ''')
    
    cursor.execute('''
    UPDATE
        dbfile
    SET
        account = 'test',
        value = 42
    WHERE
        id = 1
    ''')
    

    Какой способ работы с DBF задействовать в проекте — решать вам. dbfpy не требует сторонних зависимостей, написана на чистом Питоне, может работать в сервисах типа App Engine, однако, не столь быстра, как pyodbc. Последняя, напротив, быстрее, но зависит от платформы и иногда глючит на Винде (у меня были проблемы с ошибками в DLL-библиотеках). Для небольших проектов рекомендую использовать dbfpy, в промышленных масштабах — pyodbc.

    Комментарии из старого блога

    04/08/14 Городецкий: привет! получаю invalid syntax при from dbfpy import dbf. Подскажи пожалуйста, что не так?

    04/09/14 Иван Гришаев: Точка в конце не нужна. Или какая-то из букв кириллическая. Трейс должен показывать стрелочкой, где косяк.

    01/09/15 Ole: Отличный пост

    09/02/15 Дамир Аманов: в pyodbc как работать с кириллицой? в windows

    09/03/15 Иван Гришаев: Насколько помню, кодировать строки в cp2151, если не получается с юникодом.

  • Замечательный движок Эгея

    Этот блог работает на движке Эгея. Я узнал о нем из блога Ильи Бирмана (автора проекта). Поначалу мое отношение было скептическим — мало ли сейчас различных движков? Решив вести собственный блог, я перебрал несколько движков и КМС, и вышло так, что только Эгея не только удовлетворила все мои требования, но и приятно удивила в дальнейшем.

    Почему я не использовал Друпал? Его долго собирать. Это не готовая КМС, а конструктор. Установив Друпал, приходится еще полдня ставить модули и лазить в настройках. Эгея же просто работает. В ней нет хлама вроде опросов, древовидных тегов, визуального редактора, архива за прошлые года. Она минималистична и быстра.

    Эгея позволяет сконцентрироваться на содержимом блога, а не его техническом устройстве.

    Пока что я вижу в движке два недостатка: вики-разметка и кодировка cp1251.

    Использование разметки вместо обычного ХТМЛ является правильным решением: набирать теги руками замучаешься, а визуальные редакторы выдают отвратительный код. Вот только Илья не угадал с разметкой, лучше было бы взять не вики, а маркдаун, он проще и удобнее. Впрочем, к вики быстро привыкаешь.

    Кодировка cp1251 устарела и отовсюду вытесняется УТФ, так как не позволяет использовать символы юникода и не дружит со многими языками. Эгею нужно перевести на УТФ обязательно, cp1251 — преграда на пути проекта к широкой аудитории.

    И тем не менее, Эгея — лучший движок для блога из всех, что мне приходилось видеть. Помимо очевидных плюсов, вроде быстрой установки и высокой скорости работы, в ней есть сюрпризы. Например, картинки вставляются перетаскиванием файла в браузер. Это этого получаешь дикий, нестовый восторг! Не нужно больше искать изображения в дурацком диалоге выбора файла!

    Эгея легко поддается темизации, написать свою тему для нее нетрудно. Движок поддерживает пользовательские блоки — это ПХП-файлики, содержимое которых выводится в определенное место, например, перед статьей, после статьи, в сайдбаре, в подвале и т.д. Мне понадобилось всего три таких файла — сайдбар с меню, область после статьи для социальных кнопок и подвал для джаваскрипта (метрика, подствека кода).

    Словом, нисколько не ошибся с выбором Эгеи. Отличный движок, желаю Илье не забрасывать и развивать прект.

Страница 82 из 83