Tag Archives: Інструменти

Hunspell

Hunspell розшифровується як “угорський правопис”, і це найбільш просунута програма перевірки правопису якщо не враховувати Grammarly. А все тому що угорська мова – найбільш скажена в плані морфології. Тому якщо щось підходить для угорської – для інших європейських мов точно підійде.

sudo apt install hunspell hunspell-uk hunspell-de-de libhunspell-dev
sudo pip install hunspell

hunspell має доволі простий інтерфейс для використання в мовах програмування (хоча й складні словники (читати man hunspell.5)):

>>> import hunspell
>>> spellchecker = hunspell.HunSpell('/usr/share/hunspell/uk_UA.dic', '/usr/share/hunspell/uk_UA.aff')
>>> spellchecker.spell('ласка')
True
>>> spellchecker.spell('ласкає')
False
>>> spellchecker.suggest('ласкає')
['ласка', 'ласкам', 'лускає', 'ласках', 'ляскає', 'ласка є', 'скалатає']

І враховуючи що метод spell по суті просто визначає чи належить слово до множини правильних слів мови, ми можемо побудувати наприклад функцію для розкладу слів на прості.

import hunspell

EXAMPLES = '''
Landtagsabgeordneter,30039
Nationalsozialistischen,25081
Rechtswissenschaften,24718
Auseinandersetzungen,23109
Bundesverdienstkreuzes,22208
Rechtswissenschaftler,21113
Einwohnerentwicklung,20432
Landwirtschaftlichen,19085
Fußballnationalmannschaft,16235
Landschaftsschutzgebiet,13565
Bundesverdienstkreuz,12084
Reichsdeputationshauptschlusses,698
Sehnsuchtsland,13
'''

def test():
    for line in EXAMPLES.splitlines():
        if not line:
            continue
        word = line.split(',')[0].strip()
        print(word, '=', decompound(word))

spellchecker = hunspell.HunSpell('/usr/share/hunspell/de_DE.dic', '/usr/share/hunspell/de_DE.aff')

def decompound(word):
    variants = list(decompound_(word))
    variants.sort(key=lambda w: w.count('+'))
    return variants[-1]

def decompound_(word):
    '''yield decompositions of word or if not found whole word if spelled correctly or None if spelled incorreclty'''
    word = word.capitalize()
    if len(word) < 6:
        if spellchecker.spell(word):
            yield word
        return
    for i in range(3,len(word) - 3):
        p1, p2 = word[:i], word[i:]
        ok1 = spellchecker.spell(p1) or (
            p1.endswith('s') and spellchecker.spell(p1[:-1])
        )
        if ok1:
            for dp2 in decompound_(p2):
                yield p1 + '+' + dp2

    if spellchecker.spell(word):
        yield word


if __name__ == '__main__':
    test()

Sehnsuchtsland нема в словниках hunspell, бо воно у вікіпедії згадується лише 13 разів, зате Sehnsuchts (і очевидно Land) – є:

Landtagsabgeordneter = Land+Tags+Abgeordneter
Nationalsozialistischen = National+Sozialistischen
Rechtswissenschaften = Rechts+Wissen+Schaften
Auseinandersetzungen = Aus+Einander+Set+Zungen
Bundesverdienstkreuzes = Bundes+Verdienst+Kreuzes
Rechtswissenschaftler = Rechts+Wissenschaftler
Einwohnerentwicklung = Einwohner+Entwicklung
Landwirtschaftlichen = Land+Wirtschaftlichen
Fußballnationalmannschaft = Fuß+Ball+National+Mann+Schaft
Landschaftsschutzgebiet = Land+Schafts+Schutz+Gebiet
Bundesverdienstkreuz = Bundes+Verdienst+Kreuz
Reichsdeputationshauptschlusses = Reichs+Deputat+Ions+Haupt+Schlusses
Sehnsuchtsland = Sehnsuchts+Land

Ще воно неправильно розклало Deputat+Ions, але побачило можливість – розклало. Може іони мають бути виняток, бо вони часто трапляються як суфікс.

Скільки слів треба щоб написати вікіпедію? І які зустрічаються частіше?

Виявляється відтоді як я рахував символи вікіпедії пройшло вже більше року. Рахував я їх за допомогою Go, хоча можна було сильно спростити собі життя і рахувати їх за допомогою Python і pywikipediabot. Сьогодні розкажу як, і як можна побачити з назви – рахуватимемо слова.

Я чомусь боявся що щоб порахувати слова пам’яті не вистачить, тому треба якусь базу даних. Або пробувати все в пам’яті, але аби комп’ютеру не стало погано якось обмежити доступну пам’ять. Але мої 4Gb використовувались лише щось трохи більше ніж на 40% для підрахунку всіх слів включно зі сторінками обговорень, категорій, шаблонів, сторінок опису файлів, і т.п. німецької вікіпедії.

В модулі pywikibot.pagegenerators є об’єкти XMLDumpOldPageGenerator і XMLDumpPageGenerator. Вони приймають назву архіву з XML дампом в конструкторі, а після створення по них можна ітеруватися отримуючи об’єкти сторінки. Не ведіться на слово “Old” в назві першого об’єкта, це означає не депрекацію, а те що текст сторінки буде братись з дампа, а в другому випадку з дампа буде братись лише заголовки, а за свіжим текстом зроблять запит, що сповільнить обробку разів в 500. Тобто замість кількох годин ви будете чекати рік. 🙂

Я спробував проаналізувати німецьку вікіпедію (код буде пізніше), на це пішло 694 хв (трохи менше ніж 12 годин) і вийшло що в ній на 6,425,028 сторінках використовується 2,381,457,397 слів (приблизно 371 на сторінку), з них різних слів 18,349,393. В кінцевому результаті CSV з частотним словничком виходить на 300MB.

Серед тих що зустрічаються лише раз є слова типу PikettdienstPikettdienst (помилка парсера який видаляв розмітку), слово – це юридичний термін швейцарської німецької і перекладається як “служба за викликом”. І є слова на зразок Werkshöfe – подвір’я фабрик.

Топ 50 слів виглядає так, і складає 28% всіх слів загалом:

der 58761447 sich 9169933
und 49084873 wurde 9114619
die 44536463 CET 8614461
in 35684744 an 8385637
von 24448221 er 7835324
ist 20614114 dass 7550955
den 19454023 du 7435099
nicht 17519638 bei 7420172
das 17302844 Diskussion 7237855
zu 16167971 aus 7065523
mit 15906145 Artikel 6967243
im 15167140 oder 6824420
des 14661593 werden 6508092
für 14016308 war 6449858
auf 13957013 nach 6426826
auch 12849476 wird 6117566
eine 11903977 aber 6052645
ein 11780352 am 6017703
Kategorie 11369651 sind 5953632
als 11167157 Der 5623930
dem 11124726 Das 5545595
CEST 11104741 einen 5465687
ich 10886406 noch 5409154
Die 10761776 wie 5293658
es 10204681 einer 5228368

До списку потрапили такі вікіпедійно специфічні слова як Kategorie (сторінки без категорій вважаються не комільфо), CEST і CET (центральноєвропейський літній час і центральноєвропейський час, в підписах в обговореннях).

Ну а що без сторінок обговорень? Проблемав тому, що при створенні об’єкту сторінки XMLDumpOldPageGenerator бере з дампа лише текст і заголовок, простір імен залишається не заповненим і за замовчуванням 0 (основний). Є ще поле isredirect так при спробі доступу до нього знову здійснюється запит. Тому, краще перейти на рівень нижче і використати XmlDump з pywikibot.xmlreader, він використовується так само, просто дає об’єкти не Page, а попростіші, які не вміють робити запити до вікіпедії і не мають методу save. Але нам його й не треба, правда?

Ось код який ігнорує перенаправлення і всі сторінки крім статтей:

"""Count word frequencies in wikipedia dump"""
import csv
from collections import Counter
from itertools import islice
import re
import sys

import mwparserfromhell
from pywikibot.xmlreader import XmlDump

def main():
    """Iterate over pages and count words"""
    if len(sys.argv) < 2:
        print('Please give file name of dump')
        return
    filename = sys.argv[1]

    pages = 0
    words = 0
    words_counts = Counter()
    print('Processing dump')

    for page in XmlDump(filename).parse():
        if (page.ns != '0') or page.isredirect:
            continue
        try:
            text = mwparserfromhell.parse(page.text).strip_code()
        except Exception as e:
            print(page.title, e)
            continue

        text = text.replace('\u0301', '') # remove accents
        # Ukrainian: 

        # page_words = re.findall(
        #     r'[абвгґдеєжзиіїйклмнопрстуфхцчшщьюя'
        #     r'АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ’\'-]+',
        #     text
        # )
        
        # Any language:
        page_words = re.findall(r'\b[^\W\d]+\b', text)

        pages += 1
        words += len(page_words)
        words_counts.update(page_words)
        if pages % 123 == 0:
            print('\rPages: %d. Words: %d. Unique: %d. Processing: %s' % (
                pages, words, len(words_counts), (page.title + ' ' * 70)[:70],
            ), end='')

    print('Done. Writing csv')
    with open('common_words.csv', 'w', newline='') as csvfile:
        csvwriter = csv.writer(csvfile)
        for item in words_counts.most_common():
            csvwriter.writerow(item)

if __name__ == '__main__':
    main()

Він працює майже вдвічі швидше, 381 хвилину, бо обробляє лише 2,295,426 сторінок (обсяг німецької вікіпедії цього року). На цих сторінках є 1,074,446,116 слів (в середньому 468 на сторінку), з них різних – 12,002,417. (Виявляється є аж 6 мільйонів всяких слів які вживаються на всіляких службових сторінках німецької вікіпедії, і яких нема в статтях).

Якщо ж взяти українські статті, то на них треба ще менше часу – 131 хвилину (забув уточнити що в мене SSD), їх є 923238 (скоро мільйон!), слів 238263126 (в середньому 258 на сторінку, треба доповнювати 😉 ). З них різних – 4,571,418. Отак, в мене тепер є частотний словник української на 4.5 мільйони слів. І німецької на 12 мільйонів.

Хоча не спішіть з висновками що українська мова бідніша, бо мої методи потребують вдосконалення. По перше, так як Morgen (ранок) і morgen (завтра) – різні слова, то я не приводив букви в німецькій до одного регістру. (Правда й в українській забув це зробити).

По друге, в німецькому словнику 350590 разів зустрічається слово “www”, бо я вважав словом будь-яку послідовність літер латинки, а в українській відфільтрував кирилицю. Слово youtube зустрічається 8375 разів, а значить є ризик знайти якесь рідкісне слово на зразок “fCn8zs912OE”. 🙂

На WordPress глючить додавання картинок, тому нате вам відео:

А, і ось топ-10 української вікіпедії:

в,4551982
на,3730686
і,3475086
у,3353796
з,3053407
-,2695783
Категорія,2417267
та,2350573
до,1815429
року,1553492

Частота “року” наводить на думку що в українській вікіпедії якийсь перекос на історичні методи викладу. 🙂

Як створити блог з Hugo

Hugo – це такий генератор статичних сайтів. Статичні сайти – це сайти що складаються з набору фіксованих сторінок і не генеруються з шаблонів і запитів до бази даних при кожному завантаженні. Це з одного боку менш зручно бо немає можливості наприклад опублікувати щось автоматично встановивши час публікації, а з іншого боку – менш вимогливо до ресурсів, і краще з точки зору комп’ютерної безпеки. Крім того, wordpress.com зі своїми оновленнями інтерфейсу починає трохи дратувати. Хочеться markdown, свого javascript і стилів. А ще він не підсвічує синтаксис go. 🙂 Але ця стаття публікується на WordPress, яка іронія… Тому що я ще не вирішив що публікуватиму там.

До цього, мій статичний сайт на github генерувався самописним скриптом на python, який перетворював шаблони Mako в HTML, дозволяв вставляти javascript разом з залежностями, і так як я коли це придумував не знав ні про який node.js з npm (точніше знав, але не дуже цікавився), то залежності в мене описувались не в package.json, а в external_assets.py, і збирав їх не webpack чи gulp, чи browserify чи bower, а requirejs.py.

Юний я і мій велосипед.

Але це я відхиляюсь від теми. Мова про те що велосипеди треба не винаходити, треба брати і їздити. Тому поїхали!

Якщо у вас встановлене go, я зараз розкажу як встановити hugo, інакше читайте інструкцію для своєї системи.

Виконуємо:

go get github.com/magefile/mage
go get -d github.com/gohugoio/hugo
cd ${GOPATH:-$HOME/go}/src/github.com/gohugoio/hugo
mage vendor
HUGO_BUILD_TAGS=extended mage install

HUGO_BUILD_TAGS=extended потребує встановлених gcc, та g++, щоб скомпілювати libsass. Я сам писатиму просте CSS без всяких там SASS, але не знаю чи якимось темам його не треба буде, тому на всяк випадок скомпілював з його підтримкою.

Перевіряємо версію:

$ hugo version
Hugo Static Site Generator v0.48-DEV-FFF13253/extended linux/amd64 BuildDate: 2018-08-22T22:49:10+0300

Свіже щойно збілджене.

$ cd ~/projects # чи де ви там свій код тримаєте
$ hugo new site blog # створюємо новий сайт що називається "blog".
$ cd blog

Додаємо тему. Вибрати можна тут.

git init # можна звісно й без git її скачати, але так зручніше
git submodule add https://github.com/Vimux/Mainroad.git themes/mainroad
echo 'theme = "mainroad"' >> config.toml

Додаємо якусь публікацію:

hugo new posts/my-first-post.md

Запускаємо сервер

hugo server -D

Можна запускати без -D, але тоді щоб побачити публікацію, треба забрати з неї рядок “draft: true” (його рано чи пізно варто буде забрати, а то нащо сайт, якщо на ньому нема закінчених публікацій?). Всі публікації знаходяться в директорії content (ну а потім posts/my-first-post.md).

В браузері дивимось як виглядає наш сайт. В темі Mainroad ви публікацій спершу не побачите, бо вона їх чомусь за замовчуванням шукає в директорії content/post, хоча документація Hugo каже створювати в posts. Це не страшно, в файлі config.toml додаємо ще секцію “Params” з наступним вмістом:

[Params]
  postSections = ["posts"] # the section pages to show on home page and the "Recent articles" widget

Було б добре ще додати якусь сторінку about і т.п. Це теж просто:

hugo new about/_index.md

Редагуємо content/about/_index.md і додаємо вгорі menu: main, інакше посилання на нашу сторінку не буде в меню. Тепер є меню з одного пункта, але нема посилання назад на головну зі списком публікацій.

Щоб виправити це – додайте наступне в конфіг:

[menu]

  [[menu.main]]
    identifier = "home"
    name = "Home"
    url = "/"
    weight = -110

Можна посилання ще кудись, додавши ще секцію:

  [[menu.main]]
    identifier = "bunyk"
    name = "Блог одного кібера"
    url = "https://bunyk.wordpress.com/"
    weight = 100

“weight” (вага) – це число за яким сортуються пункти меню в зростаючому порядку.

Ще, мені не подобається що в цій темі посилання червоні і не підкреслені. Я люблю щоб були сині з підкресленням. Для цього створюємо файл static/style.css, який перевантажуватиме стилі нашої теми:

.content a {
	color: blue;
	text-decoration: underline;
}

Правда його існування ще нічого не міняє, бо тема про нього не в курсі. Аби була в курсі, треба скопіювати файл теми themes/mainroad/layouts/_default/baseof.html в layouts/_default/baseof.html, і додати там після рядка:

	<link rel="stylesheet" href="{{ "css/style.css" | relURL }}">

рядок:

	<link rel="stylesheet" href="{{ "style.css" | relURL }}">

Тепер досить бавитись на localhost, давайте опублікуємо все десь в інтернеті. Я спробую на Github Pages, бо там в мене вже був велосипед.

Виявляється, це майже елементарно. Якщо у вас нема репозиторію що називається .github.io – створіть. Там буде публікуватись відрендерений сайт. Тоді видаліть з проекту директорію public (потім hugo її перестворить), і створіть на її місці підмодуль що вказуватиме на репозиторій сайту:

git submodule add -b master git@github.com:<USERNAME>/<USERNAME>.github.io.git public

Підмодуль – це таке посилання на конкретний комміт в іншому git репозиторії. Щоб його оновити – перебудовуємо сайт (команда hugo без параметрів), переходимо в public, комітимо і пушимо. Готово. Можна автоматизувати останні дії таким скриптом:

#!/bin/bash

echo -e "\033[0;32mDeploying updates to GitHub...\033[0m"

hugo 

cd public

git add .
msg="rebuilding site `date`"
if [ $# -eq 1 ]
  then msg="$1"
fi
git commit -m "$msg"

git push origin master

cd ..

Моніторинг випадкової змінної за допомогою Telegraf -> InfluxDB -> Grafana

В цій публікації я розкажу про те як побудувати графік зміни якоїсь змінної в реальному часі. Наприклад якоїсь ціни, чи кількості запитів до сервера. Ключові слова: Docker, Docker compose, time series database, InfluxDB, Grafana, Telegraf. Всі крім докера будуть пояснені детально, докер – лише використовуватись для економії часу на інсталяцію.

В тренді зараз криптовалюти, тому давайте для прикладу будемо моніторити курс Litecoin до гривні. Для цього достатньо зробити GET запит https://api.coinmarketcap.com/v2/ticker/2/?convert=UAH. Для Bitcoin замініть id після /ticker/ з 2 на 1. (Документація з API). Він повертає JSON, формат якого розберемо трохи пізнішео. Бо нам ще треба встановити, налаштувати і запустити три програми для того щоб вони одна з одною працювали. Ну звісно в наш час це вручну ніхто не робить, тому ось вам готова конфігурація docker-compose.yml:

version: '3'
services: 
    influxdb:
      image: influxdb:latest
      container_name: influxdb
      ports:
        - "8086:8086"
      networks:
        - back-tier

    telegraf:
      image: telegraf:latest
      container_name: telegraf
      volumes:
        - ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
      networks:
        - back-tier


    grafana:
      image: grafana/grafana:latest
      container_name: grafana
      ports:
        - "3000:3000"
      networks:
        - back-tier


networks:
  back-tier:

Записуєте його в директорію проекту, командуєте docker-compose up, і насолоджуєтесь логами всіх трьох сервісів. Правда вискочить помилка, бо конфігурація

volumes:
        - ./telegraf.conf:/etc/telegraf/telegraf.conf:ro

означає “покласти файл telegraf.conf з поточної директорії, в контейнер за шляхом /etc/telegraf/telegraf.conf”, а ми цей файл не написали. Для того треба спершу розібратись що таке Telegraf, чим він займатиметься, і як.

Telegraf, як пишуть на його сайті – це агент для збирання метрик і запису їх в InfluxDB, чи якісь інші можливі місця. Його файл конфігурації довгий, але важливі лише два місця:

[[outputs.influxdb]]
  # Конфігурація виведення даних в InfluxDB
  urls = ["http://influxdb:8086"] # HTTP інтерфейс InfluxDB. 
  ## Ім'я домену influxdb буде показувати на контейнер influxdb, тому що docker-compose так робить мережі

  ## База даних в яку писати метрики (telegraf її створить якщо буде потреба).
  database = "telegraf"

... 

[[inputs.http]]
  ## Брати дані http запитами
  urls = [ # звідки
    "https://api.coinmarketcap.com/v2/ticker/2/?convert=UAH"
  ]
  method = "GET" # методом GET
  data_format = "json" # розшифровувати як JSON

Якщо такий файл в нас є, то композ запустить всі три сервіси успішно, і Telegraf почне писати щось в InfluxDB. Пора подивитись що з того вийде. Щоб зайти в інтерфейс командного рядка Influxdb треба виконати команду

docker exec -it influxdb influx

А тоді:

> SHOW DATABASES
name: databases
name
----
_internal
telegraf
> use telegraf
Using database telegraf
> SHOW MEASUREMENTS
name: measurements
name
----
http

Бачим що Telegraf пише все в одну “таблицю” (measurement) – http. Але це насправді не страшно, бо в InfluxDB важливі не так measurements, як series – measurement з унікальним набором тегів (полів що індексуються). Крім них ще є fields (поля, які містять дані і не індексуються). Подивимось які в нас теги і поля (це майже те саме що схема таблиці в реляційних БД):

> SHOW FIELD KEYS FROM http 
name: http
fieldKey                           fieldType
--------                           ---------
data_circulating_supply            float
data_id                            float
data_last_updated                  float
data_max_supply                    float
data_quotes_UAH_market_cap         float
data_quotes_UAH_percent_change_1h  float
data_quotes_UAH_percent_change_24h float
data_quotes_UAH_percent_change_7d  float
data_quotes_UAH_price              float
data_quotes_UAH_volume_24h         float
data_quotes_USD_market_cap         float
data_quotes_USD_percent_change_1h  float
data_quotes_USD_percent_change_24h float
data_quotes_USD_percent_change_7d  float
data_quotes_USD_price              float
data_quotes_USD_volume_24h         float
data_rank                          float
data_total_supply                  float
metadata_timestamp                 float
> SHOW TAG KEYS FROM http
name: http
tagKey
------
host
url
> SHOW TAG VALUES FROM http WITH KEY IN ("host", "url")
name: http
key  value
---  -----
host 42bdec9c8910
url  https://api.coinmarketcap.com/v2/ticker/2/?convert=UAH

Бачимо що теги – це хост на якому запущений агент телеграфа що прислав дані (дивне в нього id, але це id контейнера). Не знаю чому не ім’я, думаю якось можна змінити, але це не дуже важливо якщо в нас один сервер з Telegraf. І адреса ресурсу який моніторить Telegraf. Тому можна бути спокійним з “таблиці” http можна буде вибрати окремі значення за тегом.

А от fields – дійсно багато. Яке з них – ціна Litecoin? Ну, для цього треба подивитись який JSON нам віддав coinmarketcap:

{
    "data": {
        "id": 2, 
        "name": "Litecoin", 
        "symbol": "LTC", 
        "website_slug": "litecoin", 
        "rank": 6, 
        "circulating_supply": 57387708.0, 
        "total_supply": 57387708.0, 
        "max_supply": 84000000.0, 
        "quotes": {
            "UAH": {
                "price": 2041.6651371095, 
                "volume_24h": 6550472764.2681465, 
                "market_cap": 117166483500.0, 
                "percent_change_1h": 0.08, 
                "percent_change_24h": 1.43, 
                "percent_change_7d": -6.39
            }, 
            "USD": {
                "price": 77.8638, 
                "volume_24h": 249818000.0, 
                "market_cap": 4468425048.0, 
                "percent_change_1h": 0.08, 
                "percent_change_24h": 1.43, 
                "percent_change_7d": -6.39
            }
        }, 
        "last_updated": 1531505650
    }, 
    "metadata": {
        "timestamp": 1531505337, 
        "error": null
    }
}

Ціна лежить в data.quotes.UAH.price, тому думаю нас цікавить поле data_quotes_UAH_price. Спробуємо запит:

> SELECT data_quotes_UAH_price FROM http WHERE time >= now() - 1h
name: http
time                data_quotes_UAH_price
----                ---------------------
1531595740000000000 2007.3576069685
1531595750000000000 2007.3576069685
1531595760000000000 2007.3576069685
1531595770000000000 2006.7466581361
1531595780000000000 2006.7466581361
1531595790000000000 2006.7466581361
1531595800000000000 2006.7466581361
1531595810000000000 2006.7466581361
...

О, це щось з чого можна будувати графік! І цим займеться Grafana.

Вона в нашій системі працює на порті 3000, тому заходимо на http://localhost:3000/ , входимо як USER: admin, PASSWORD: admin, змінюємо пароль, натискаємо “Create datasource”, заповнюємо форму для InfluxDB:

Заповнення джерела даних в Grafana

Внизу треба ще не забути вибрати базу даних “telegraf”, і натиснути “Save & test”. Якщо вискочило зелене повідомлення (а не червоне про помилку), то можна продовжувати.

Натискаємо плюсик -> Create -> Dashboard, додаємо панель “Graph”. У вкладці “Metrics” вибираємо датасорс InfluxDB і пишемо запит. Там є конструктор запитів, виглядає все так:

Побудова графіка за запитом

Але при бажанні можна справа натиснути кнопку меню, вибрати “Toggle edit mode”, і відредагувати запит як SQL:

SELECT mean("data_quotes_UAH_price") FROM "http" WHERE $timeFilter GROUP BY time($__interval) fill(null)

Бачимо що Grafanа вставляє в запит свої змінні, що дозволяє інтерактивно перебудовувати графік. Змінна $timeFilter містить щось на зразок now() - 1h залежно від того що користувач вибере в полі вгорі дашборда:

Вибір інтервалу часу

Ну як, почуваєтесь трошки фінансистами? Я ні, я аналіз даних в універі проспав :(, і взагалі мені це все для того аби рахувати запити до сервера.

А, ну і ввесь код, можна взяти на https://github.com/bunyk/docker-influxdb-grafana

Геренуємо пару ключів для цифрового підпису за допомогою RSA в Python

Для тих кому викликати openssl надоїло. Це дивно, але цього нема в стандартній бібліотеці python, тому:

sudo pip install pycrypto

Тоді:

from Crypto.PublicKey import RSA
from Crypto import Random

private_key = RSA.generate(1024, Random.new().read)
public_key = private_key.publickey()

print(private_key.exportKey().decode('ascii'))
print(public_key.exportKey().decode('ascii'))

Що дасть нам:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCFO0e8pxFV5Niq9Kjkn7HpX5xCbsh2oP56t2goNw/qZnddzW1x
... blablabla ...
dB6mvhutUqKRZDaA1o4y1kytKTG42RfEtdm8t1Z/77dS
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFO0e8pxFV5Niq9Kjkn7HpX5xC
bsh2oP56t2goNw/qZnddzW1xW3rWxYI2/Jxp/hv7EGapg12EcViF/C8Uv2WbCDEM
LIRaMqtHKFNaniscMgZKgaohkjXcLk5dIrVXuuxY7sk07BZqj+Jsv6xgR6GZ0CmG
Q3ZOmGAKksC/YA3gYwIDAQAB
-----END PUBLIC KEY-----

В іншій публікації було показано як це робити допомогою openssl, і як цими ключами підписати токен.

Побудова “скриньок з вусами” львівських квартир що здаються на сьогодні

Я ще минулого року помітив що в питаннях про Python на StackOverflow обговорюють якісь панди. Це, як виявилось обгортка навколо matplotlib, numpy і подібних гарних речей. А ще, лазячи по своїх документах в Google знайшов скачану вже позаминулого року стіну групи пошуку нерухомості вконтакті. І так співпало що я і мій колега-аналітик зараз шукаємо квартиру у Львові. Я йому показав цей файл, і він загорівся бажанням проаналізувати ще якийсь сайт оголошень.

При всій повазі до lun.ua, але тут я прорекламую dom.ria.com. Передовсім, там є українська версія. А ще, можливість скачати результати пошуку як електронну таблицю, хоч і в xls форматі, і лише одну сторінку.

В python читати xls вміє бібліотека xlrd, тому треба доставити ще й її. Pandas взагалі має багато необов’язкових залежностей:

sudo pip3.5 install jupyter pandas xlrd matplotlib
jupyter notebook # дуже модний графічний інтерпретатор

Якщо все поставити як вище і запустити “jupyter”, то можна робити обчислення в отакому документі: https://github.com/bunyk/mypandas/blob/master/dom.ria/dom.ria.ipynb

І можна побудувати графік скринька з вусами:


От, недаремно я деякі лекції з АнДану все таки не проспав! Хоча, який висновок робити з цього графіка – не знаю. Знаю лише що половина квартир потрапляють всередину прямокутника.

А ось гістограми по цінах для однокімнатних і двокімнатних:

Однокімнатні

Однокімнатні

Двокімнатні

Двокімнатні

Який з цих гістограм робити висновок окрім того що квартир дешевших за 2000 грн (окрім викидів) не буває (а я зараз живу за 700 грн/міс, хоча це пів квартири) – теж не знаю. Може ви самі якийсь зробите. І так, до речі, я шукаю одно чи двокімнатну квартиру десь в другому або третьому квартилі цін в районі вулиці Липинського.


Filed under: Інструменти, Кодерство, Павутина Tagged: графіка, математика, Python

Встановлення Python 3.5 з джерельного коду, встановлення Django

Ок, продовжу спроби підготуватись до DjangoGirls так, щоб там ми вчили найпередовіші технології. :)

Такі експерименти краще робити у захищеному середовищі, тому бажано щоб у вас були VirtualBox та Vagrant:

sudo apt-get install virtualbox vagrant

Поки вони ставляться, раджу коротко ознайомитись як користуватись тим Vagrant-ом.

В директорії з кодом створюємо такий файл:

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.network "forwarded_port", guest: 8000, host: 8000
end

Це поки що він такий. Пізніше поміняю, і вся інсталяція має більш автоматизуватись. Тепер команда vagrant up дозволяє підняти чисте середовище. vagrant ssh – зайти в його термінал.

Ми хочемо Python 3.5, а його інакше як з сорсів не отримаєш, тому качаємо з сайту: https://www.python.org/downloads/

sudo apt-get update
sudo apt-get install build-essential libsqlite3-dev sqlite3 bzip2 libbz2-dev
sudo apt-get install libreadline-dev libncurses5-dev tk-dev libssl-dev
wget -c https://www.python.org/ftp/python/3.5.0/Python-3.5.0.tar.xz
tar xJf Python-3.5.0.tar.xz
cd Python-3.5.0
./configure
make
sudo make install

Тепер нарешті можна створювати віртуальне середовище (всередині віртуальної машини, ага), і ставити Django:

mkdir djangogirls
cd djangogirls
python3.5 -m venv myenv
source myenv/bin/activate
pip install django

Та-дааммм!!!

Successfully installed django-1.9

Не знав що вже є Django 1.9. Хоча, звісно що не знав, його вчора випустили. Кажуть там змінили дизайн адмінки. Давайте швиденько подивимось:

django-admin startproject mysite .
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
Адмінка як адмінка.

Адмінка як адмінка.


Filed under: Інструменти, Кодерство Tagged: linux, Python

PostgreSQL & PostGIS “Hello world”

Інсталяція:

sudo apt-get install -y postgresql postgresql-contrib postgis

Після чого від імені користувача postgres (це суперкористувач для postgres), створюємо користувача gisuser та базу для нього, яку наприклад назвемо gis:

sudo -u postgres createuser gisuser
sudo -u postgres createdb --encoding=UTF-8 --owner=gisuser gis
# і створимо пароль для користувача:
sudo -u postgres psql -d gis -c "ALTER USER gisuser WITH PASSWORD 'password';"

# і увімкнути розширення postgis:
sudo -u postgres psql -d gis -c "CREATE EXTENSION postgis;"
sudo -u postgres psql -d gis -c "CREATE EXTENSION postgis_topology;"
CREATE EXTENSION

Тепер ми можемо з’єднуватись з нашою базою за допомогою клієнта, вказавши базу і користувача:

psql -d gis -U gisuser

У випадку отримання помилки:

psql: FATAL:  Peer authentication failed for user

Треба відредагувати файл sudo vim /etc/postgresql/9.4/main/pg_hba.conf, чи який там буде для вашої версії, і зробити таку заміну десь ближче до кінця:

- local   all             all                                     peer
+ local   all             all                                     md5

Тоді можна буде логінитись (дивно, але в мене навіть пароль не питає), і виконувати запити:

select postgis_full_version();
-- В мене дає щось схоже на:
-- POSTGIS="2.1.5 r13152" GEOS="3.4.2-CAPI-1.8.2 r3921"
-- PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.11.2, released 2015/02/10"
-- LIBXML="2.9.2" LIBJSON="UNKNOWN" RASTER

Можна ще поставити гарний GUI-клієнт:

sudo apt-get install -y pgadmin3

Гаразд, давайте тепер створимо табличку з містами:

CREATE TABLE cities ( 
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    location GEOGRAPHY(POINT,4326)
);

4326 це ідентифікатор системи координат (SRID, Spatial Reference system ID) і означає що ми використовуватимемо систему координат WGS 84. Здається це популярна система координат.

Тепер заповнимо таблицю якимись даними:

INSERT INTO cities (name, location) VALUES
 ('Львів', ST_GeographyFromText('SRID=4326;POINT(49.83 24.014167)') ),
 ('Київ', ST_GeographyFromText('SRID=4326;POINT(50.45 30.523611)') ),
 ('Івано-Франківськ', ST_GeographyFromText('SRID=4326;POINT(48.922778 24.710556)') )
;

І спробуємо зробити якийсь запит, наприклад скільки метрів від Львова до Києва:

select ST_Distance(
    (select location from cities where name='Львів'),
    (select location from cities where name='Київ')
);

І цей запит дає мені 723904.293225235, що приблизно 723 км, в той час як до Києва по прямій менш ніж 500 км. Я вибрав неправильну проекцію, або переписав з вікіпедії неправильні координати, або передав функції якісь неправильні параметри. :(

Але на сьогодні напевне просто піду спати. До речі, може ви підкажете в чому помилка?

Посилання


Filed under: Інструменти, Кодерство Tagged: linux

Шпаргалка по Docker

Docker в порівнянні з гіпервізором другого типу.

Docker в порівнянні з гіпервізором другого типу.

Докер – штука для керування лінукс-контейнерами. А Лінукс-контейнери – це особливий вид гіпервізора, який дозволяє створювати на лінуксі віртуальні лінукси. Це мінус що тільки лінукси, але плюс що ядро операційної системи для кожного контейнера спільне, тому ці контейнери набагато легші в порівнянні з повноцінними віртуальними машинами.

Віртуальні машини корисні для ізоляції середовища. Наприклад середовища розробки. Хоча цим може займатись і Vagrant. А ще Vagrant може керувати не тільки машинами на VirtualBox, а й контейнерами Docker. Коли що використувати – здається справа особистих вподобань. Хоча мені кажуть що так як контейнери більш легковісні, тут інша філософія роботи, наприклад “кожному процесу свій контейнер”. Ось цікаве обговорення питання що коли варто використовувати, в якому беруть участь автор Vagrant та автор Docker.

Але краще раз попробувати ніж сто разів прочитати:

Інсталяція

На Linux найпростіше, хоч і не безпечно:

wget -qO- https://get.docker.com/ | sh

Запуск контейнера

bunyk@ubuntu:~$ docker run docker/whalesay cowsay boo 
Post http:///var/run/docker.sock/v1.19/containers/create: dial unix 
/var/run/docker.sock: no such file or directory.
Are you trying to connect to a TLS-enabled daemon without TLS?

Якщо бачите таку помилку – значить або докер ще не запущений:

bunyk@ubuntu:~$ sudo service docker start

Або ваш користувач не знаходиться в групі докера:

sudo usermod -aG docker bunyk

Різні інші дії

# скачати образ (щоб він не качався коли ми будемо робити йому run)
docker pull

# список всіх скачаних образів
docker images

# всі запущені контейнери
docker ps

# всі (не лише запущені) контейнери
docker ps -a

# показати лише ідентифікатори контейнерів
docker ps -q

 # видалити всі контейнери
docker rm $(docker ps -aq)

# останній запущений контейнер
docker ps -l

# видалити контейнер
docker rm

# видалити образ
docker rmi

# запустити інтерактивну програму в контейнері
docker run -t -i debian /bin/bash

# запустити демона в контейнері
docker run -d debian /bin/sh -c "while true; do echo hello world; sleep 1; done"

# запустити контейнер так, що директорію хоста /host/dir буде змонтовано як /container/dir
docker run -v /host/dir:/container/dir debian

# слідкувати за логами демона в контейнері
docker logs -f ecstatic_lovelace

# збудувати образ з Dockerfile поточної директорії
docker build -t ouruser/ourrepo .

Filed under: Інструменти, Кодерство Tagged: linux

Шпаргалка по Docker

Docker в порівнянні з гіпервізором другого типу.

Docker в порівнянні з гіпервізором другого типу.

Докер – штука для керування лінукс-контейнерами. А Лінукс-контейнери – це особливий вид гіпервізора, який дозволяє створювати на лінуксі віртуальні лінукси. Це мінус що тільки лінукси, але плюс що ядро операційної системи для кожного контейнера спільне, тому ці контейнери набагато легші в порівнянні з повноцінними віртуальними машинами.

Віртуальні машини корисні для ізоляції середовища. Наприклад середовища розробки. Хоча цим може займатись і Vagrant. А ще Vagrant може керувати не тільки машинами на VirtualBox, а й контейнерами Docker. Коли що використувати – здається справа особистих вподобань. Хоча мені кажуть що так як контейнери більш легковісні, тут інша філософія роботи, наприклад “кожному процесу свій контейнер”. Ось цікаве обговорення питання що коли варто використовувати, в якому беруть участь автор Vagrant та автор Docker.

Але краще раз попробувати ніж сто разів прочитати:

Інсталяція

На Linux найпростіше, хоч і не безпечно:

wget -qO- https://get.docker.com/ | sh

Запуск контейнера

bunyk@ubuntu:~$ docker run docker/whalesay cowsay boo 
Post http:///var/run/docker.sock/v1.19/containers/create: dial unix 
/var/run/docker.sock: no such file or directory.
Are you trying to connect to a TLS-enabled daemon without TLS?

Якщо бачите таку помилку – значить або докер ще не запущений:

bunyk@ubuntu:~$ sudo service docker start

Або ваш користувач не знаходиться в групі докера:

sudo usermod -aG docker bunyk

Різні інші дії

# скачати образ (щоб він не качався коли ми будемо робити йому run)
docker pull

# список всіх скачаних образів
docker images

# всі запущені контейнери
docker ps

# всі (не лише запущені) контейнери
docker ps -a

# показати лише ідентифікатори контейнерів
docker ps -q

 # видалити всі контейнери
docker rm $(docker ps -aq)

# останній запущений контейнер
docker ps -l

# видалити контейнер
docker rm

# видалити образ
docker rmi

# запустити інтерактивну програму в контейнері
docker run -t -i debian /bin/bash

# запустити демона в контейнері
docker run -d debian /bin/sh -c "while true; do echo hello world; sleep 1; done"

# запустити контейнер так, що директорію хоста /host/dir буде змонтовано як /container/dir
docker run -v /host/dir:/container/dir debian

# слідкувати за логами демона в контейнері
docker logs -f ecstatic_lovelace

# збудувати образ з Dockerfile поточної директорії
docker build -t ouruser/ourrepo .

Filed under: Інструменти, Кодерство Tagged: linux