Tag Archives: python - Page 2

Пишемо переглядач молекул з Pyglet

Я хотів створити серію уроків про графіку в OpenGL по слідах NeHe, але отримав іншу пропозицію, і пріоритети змінились. Ну й графіка в наш час людей не так цікавить. Але так як задачу я почав робити, просто витирати її з списку проектів буде не цікаво, краще опублікувати те що є і перенести в список закінчених проектів. Чим я зараз й займусь.

Ідея програми – намалювати атоми сферами різних кольорів і розмістити їх в різних місцях простору, таким чином отримавши молекулу. Для цього нам треба знати координати. Для цього ми використаємо Open Babel – хімічну експертну систему. Ось інструкції з інсталяції, apt-get install python-openbabel якщо кому лінь їх читати.

Глюкоза

Молекула глюкози

Користуючись нею, ми можемо перетворити формулу SMILES, на список координат атомів:

import pybel

smile = raw_input('Enter SMILE molecule:')
molecule = pybel.readstring('smi', smile)
molecule.make3D()

for atom in molecule.atoms:
    print atom.type, ' '.join(map(str, atom.coords))

SMILES можна знайти в статтях вікіпедії про різні речовини. Ось наприклад глюкоза: OC[C@H]1OC(O)[C@H](O)[C@@H](O)[C@@H]1O. І її координати:

O3 3.08232699168 1.41136753836 1.97383867659
C3 2.63605783234 0.116724346362 2.37092125466
C3 2.87897272901 -0.854338624684 1.21070216538
H 2.47296741411 -0.36351143938 0.319191621385
O3 4.29331227545 -1.03408976253 1.03052197614
C3 2.18198168708 -2.2062426467 1.4123437219
H 2.52686439483 -2.70828022178 2.32628341407
O3 0.757046712076 -2.07375697965 1.48816465135
C3 2.4834103428 -3.09613160782 0.198042289604
H 2.03158506981 -2.66605342385 -0.704362586236
O3 1.84366255476 -4.36331585678 0.373985237387
C3 3.99532722341 -3.24522583591 0.00523555683618
H 4.41250004827 -3.77444425583 0.871020029013
O3 4.29626886035 -4.03252960027 -1.15195954921
C3 4.62954125722 -1.84273135567 -0.0947112115177
H 5.71887138656 -1.95252927548 -0.108137148969
O3 4.3079363412 -1.18316983652 -1.31789119536
HO 2.91473185309 2.02688340774 2.71060639162
H 1.56956736943 0.19992674022 2.5985040442
H 3.17829013287 -0.18365904297 3.27321534863
HO 0.532102901531 -1.64213847492 2.33074495499
HO 0.907383263524 -4.15051515566 0.55676359202
HO 3.89297302828 -3.59188323274 -1.91963488602
HO 3.3506978517 -1.05986229308 -1.37211748251

Ок, залишилось написати програму що бере оці координати і створює таку картинку як у цій публікації (увага, ввесь код звідси і аж до кінця публікації – це одна програма):

#! /usr/bin/python3
from random import random

import pyglet
from pyglet.window import key, Window
from pyglet.gl import *
from pyglet.gl.glu import *

window = Window()

Об’єкт наступного класу просто буде повертати кортеж з кольором для кожної назви атома. Деякі атоми ми задамо вручну, щодо правильної палітри – дивіться статтю CPK coloring.

class Palette(object):
    def __init__(self):
        self.colors = {
            'H': (0.0, 0.5, 0.5),
            'HO': (1.0, 0.5, 0.5),
            'C3': (0.1, 0.1, 0.1),
            'Car': (0.1, 0.1, 0.1),
            'O3': (1.0, 0.0, 0.0),
        }

    def get_color(self, name):
        if name not in self.colors:
            print(name)
            self.colors[name] = (random(), random(), random())
        return self.colors[name]

palette = Palette()

Молекула – це по суті список атомів (кожен з яких четвірка з назви і трьох координат), що буде завантажувати себе з файлу при створенні екземпляру класу, і вміє малювати себе:

class Molecule(object):
    def __init__(self, fn):
        self.atoms = []
        with open(fn) as f:
            for l in f:
                el, x, y, z = l.split()
                self.atoms.append(
                    (el, float(x), float(y), float(z))
                )

    def draw(self):
        for atom in self.atoms: # для кожного атома
            glPushMatrix() # зберегти матрицю моделі
            glTranslatef(*atom[1:]) # змістити матрицю моделі в координати атома
            # намалювати сферу радіусу 1 і кольору відповідного типу атома
            draw_sphere(1, palette.get_color(atom[0]))
            glPopMatrix() # завантажити збережену матрицю моделі

molecule = Molecule('glucose.dat') # створити молекулу глюкози

def draw_sphere(radius, color):
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
    
    # довго пояснювати що таке колір матеріалу, я й сам не до кінця знаю.
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (GLfloat * 3)(*color))
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,
        (GLfloat * 3)(*map(lambda x: x/2, color))
    )
    # glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat * 3)(*color))

    sphere = gluNewQuadric()
    gluSphere(sphere, radius, 50, 50) # 50, 50 - це кількість меридіанів та паралелей. 
    # якщо потрібно багато атомів - зменшіть їх кількість для збільшення продуктивності.

Тепер займемось власне перемальовуванням екрану:

@window.event
def on_draw():
    update_frame(0)

rotation = 0 # Глобальна зміна з поточним поворотом моделі
def update_frame(dt):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity() # завантажити матрицю ідентичності 

    global rotation
    rotation += dt * 10 # чим більше часу пройшло - тим більше повертаємо
    glRotatef(rotation, 0, 1, 0) # навколо осі y
    molecule.draw() # і малюємо нашу молекулу.

При зміні розмірів вікна (і при його створенні) ініціалізуємо всілякі налаштування OpenGL:

@window.event
def on_resize(width, height):
    glClearColor(0.0, 0.3, 0.0, 0.0) # задаємо колір фону

    glEnable(GL_DEPTH_TEST) # вмикаємо буфер глибини

    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glLightf(GL_LIGHT0, GL_POSITION, 1, 5, 4) # ставимо одне світло

    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, width / height, .1, 1000) # перспективна проекція з кутом 45
    gluLookAt( # ставимо камеру і націлюємо її в цент сцени
     1, 4, 15, # eye
     0, 0, 0, # target
     0, 1, 0  # up
    )
    glMatrixMode(GL_MODELVIEW) 
    return pyglet.event.EVENT_HANDLED

При натисканні клавіш “вліво” і “вправо” оновлюємо кадр, повернувши трішки модель. А також оновлюємо 50 разів на секунду. І запускаємо цикл подій:

@window.event
def on_key_press(symbol, modifiers):
    if symbol == key.LEFT:
        update_frame(-1)
    elif symbol == key.RIGHT:
        update_frame(1)

pyglet.clock.schedule_interval(update_frame, 0.02)

pyglet.app.run()

На цьому і все. Можна було звісно написати набагато краще, без глобальних змінних, з кращими поворотами камери і кращим освітленням і т.п. Але поки що є важливіші речі. (Хоча, якщо ви захочете онлайн курс, і зможете зробити так що мені не треба буде ходити на роботу – можемо щось придумати ;) ).

Ах, і стаття з якої взято інформацію про те як отримати координати для атомів молекули: Patrick Fuller – Molecules in Blender


Filed under: Графіка, Кодерство Tagged: освіта, OpenGL, Python

Пишемо переглядач молекул з Pyglet

Я хотів створити серію уроків про графіку в OpenGL по слідах NeHe, але отримав іншу пропозицію, і пріоритети змінились. Ну й графіка в наш час людей не так цікавить. Але так як задачу я почав робити, просто витирати її з списку проектів буде не цікаво, краще опублікувати те що є і перенести в список закінчених проектів. Чим я зараз й займусь.

Ідея програми – намалювати атоми сферами різних кольорів і розмістити їх в різних місцях простору, таким чином отримавши молекулу. Для цього нам треба знати координати. Для цього ми використаємо Open Babel – хімічну експертну систему. Ось інструкції з інсталяції, apt-get install python-openbabel якщо кому лінь їх читати.

Глюкоза

Молекула глюкози

Користуючись нею, ми можемо перетворити формулу SMILES, на список координат атомів:

import pybel

smile = raw_input('Enter SMILE molecule:')
molecule = pybel.readstring('smi', smile)
molecule.make3D()

for atom in molecule.atoms:
    print atom.type, ' '.join(map(str, atom.coords))

SMILES можна знайти в статтях вікіпедії про різні речовини. Ось наприклад глюкоза: OC[C@H]1OC(O)[C@H](O)[C@@H](O)[C@@H]1O. І її координати:

O3 3.08232699168 1.41136753836 1.97383867659
C3 2.63605783234 0.116724346362 2.37092125466
C3 2.87897272901 -0.854338624684 1.21070216538
H 2.47296741411 -0.36351143938 0.319191621385
O3 4.29331227545 -1.03408976253 1.03052197614
C3 2.18198168708 -2.2062426467 1.4123437219
H 2.52686439483 -2.70828022178 2.32628341407
O3 0.757046712076 -2.07375697965 1.48816465135
C3 2.4834103428 -3.09613160782 0.198042289604
H 2.03158506981 -2.66605342385 -0.704362586236
O3 1.84366255476 -4.36331585678 0.373985237387
C3 3.99532722341 -3.24522583591 0.00523555683618
H 4.41250004827 -3.77444425583 0.871020029013
O3 4.29626886035 -4.03252960027 -1.15195954921
C3 4.62954125722 -1.84273135567 -0.0947112115177
H 5.71887138656 -1.95252927548 -0.108137148969
O3 4.3079363412 -1.18316983652 -1.31789119536
HO 2.91473185309 2.02688340774 2.71060639162
H 1.56956736943 0.19992674022 2.5985040442
H 3.17829013287 -0.18365904297 3.27321534863
HO 0.532102901531 -1.64213847492 2.33074495499
HO 0.907383263524 -4.15051515566 0.55676359202
HO 3.89297302828 -3.59188323274 -1.91963488602
HO 3.3506978517 -1.05986229308 -1.37211748251

Ок, залишилось написати програму що бере оці координати і створює таку картинку як у цій публікації (увага, ввесь код звідси і аж до кінця публікації – це одна програма):

#! /usr/bin/python3
from random import random

import pyglet
from pyglet.window import key, Window
from pyglet.gl import *
from pyglet.gl.glu import *

window = Window()

Об’єкт наступного класу просто буде повертати кортеж з кольором для кожної назви атома. Деякі атоми ми задамо вручну, щодо правильної палітри – дивіться статтю CPK coloring.

class Palette(object):
    def __init__(self):
        self.colors = {
            'H': (0.0, 0.5, 0.5),
            'HO': (1.0, 0.5, 0.5),
            'C3': (0.1, 0.1, 0.1),
            'Car': (0.1, 0.1, 0.1),
            'O3': (1.0, 0.0, 0.0),
        }

    def get_color(self, name):
        if name not in self.colors:
            print(name)
            self.colors[name] = (random(), random(), random())
        return self.colors[name]

palette = Palette()

Молекула – це по суті список атомів (кожен з яких четвірка з назви і трьох координат), що буде завантажувати себе з файлу при створенні екземпляру класу, і вміє малювати себе:

class Molecule(object):
    def __init__(self, fn):
        self.atoms = []
        with open(fn) as f:
            for l in f:
                el, x, y, z = l.split()
                self.atoms.append(
                    (el, float(x), float(y), float(z))
                )

    def draw(self):
        for atom in self.atoms: # для кожного атома
            glPushMatrix() # зберегти матрицю моделі
            glTranslatef(*atom[1:]) # змістити матрицю моделі в координати атома
            # намалювати сферу радіусу 1 і кольору відповідного типу атома
            draw_sphere(1, palette.get_color(atom[0]))
            glPopMatrix() # завантажити збережену матрицю моделі

molecule = Molecule('glucose.dat') # створити молекулу глюкози

def draw_sphere(radius, color):
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
    
    # довго пояснювати що таке колір матеріалу, я й сам не до кінця знаю.
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (GLfloat * 3)(*color))
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,
        (GLfloat * 3)(*map(lambda x: x/2, color))
    )
    # glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat * 3)(*color))

    sphere = gluNewQuadric()
    gluSphere(sphere, radius, 50, 50) # 50, 50 - це кількість меридіанів та паралелей. 
    # якщо потрібно багато атомів - зменшіть їх кількість для збільшення продуктивності.

Тепер займемось власне перемальовуванням екрану:

@window.event
def on_draw():
    update_frame(0)

rotation = 0 # Глобальна зміна з поточним поворотом моделі
def update_frame(dt):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity() # завантажити матрицю ідентичності 

    global rotation
    rotation += dt * 10 # чим більше часу пройшло - тим більше повертаємо
    glRotatef(rotation, 0, 1, 0) # навколо осі y
    molecule.draw() # і малюємо нашу молекулу.

При зміні розмірів вікна (і при його створенні) ініціалізуємо всілякі налаштування OpenGL:

@window.event
def on_resize(width, height):
    glClearColor(0.0, 0.3, 0.0, 0.0) # задаємо колір фону

    glEnable(GL_DEPTH_TEST) # вмикаємо буфер глибини

    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glLightf(GL_LIGHT0, GL_POSITION, 1, 5, 4) # ставимо одне світло

    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, width / height, .1, 1000) # перспективна проекція з кутом 45
    gluLookAt( # ставимо камеру і націлюємо її в цент сцени
     1, 4, 15, # eye
     0, 0, 0, # target
     0, 1, 0  # up
    )
    glMatrixMode(GL_MODELVIEW) 
    return pyglet.event.EVENT_HANDLED

При натисканні клавіш “вліво” і “вправо” оновлюємо кадр, повернувши трішки модель. А також оновлюємо 50 разів на секунду. І запускаємо цикл подій:

@window.event
def on_key_press(symbol, modifiers):
    if symbol == key.LEFT:
        update_frame(-1)
    elif symbol == key.RIGHT:
        update_frame(1)

pyglet.clock.schedule_interval(update_frame, 0.02)

pyglet.app.run()

На цьому і все. Можна було звісно написати набагато краще, без глобальних змінних, з кращими поворотами камери і кращим освітленням і т.п. Але поки що є важливіші речі. (Хоча, якщо ви захочете онлайн курс, і зможете зробити так що мені не треба буде ходити на роботу – можемо щось придумати ;) ).

Ах, і стаття з якої взято інформацію про те як отримати координати для атомів молекули: Patrick Fuller – Molecules in Blender


Filed under: Графіка, Кодерство Tagged: освіта, OpenGL, Python

OpenGL в Python

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

Інсталяція та перше вікно

Найперше що потрібно графічним програмам – вікно. Щоб створити вікно, нам треба якусь бібліотеку, наприклад PyQt, PySide, PyGtk, WxPython чи PyGame – їх купа. Потрібно також щоб це вікно підтримувало контекст OpenGL (тобто могло дозволити відеокарті виводити свої дані в область вікна). З цим може справитись багато бібліотек, але ми виберемо Pyglet. Тому що в ній мало зайвого, і вона ставиться традиційно:

pip install pyglet

Ну, і як годиться – почнемо з найпростішої програми:

import pyglet

window = pyglet.window.Window(width=640, height=480, caption="Hello OpenGL!")
pyglet.app.run()

Отримаємо вікно заданої ширини та висоти, і з заданим заголовком:

Наше перше вікно

Наше перше вікно

Елементарно, правда?

Фарби

Давайте ще зафарбуємо вікно в білий колір. Для цього потрібно знати що кольори задаються переважно інтенсивністю світла в моделі RGB (червоний, зелений, голубий), числами від 0 до 1. Тобто білий – це 1.0, 1.0, 1.0, сірий – 0.5, 0.5, 0.5, і т.п. Детальніше на вікіпедії.

import pyglet
from pyglet.gl import * # імпортуємо всі функції OpenGL
# вони починатимуться з префіксів gl або glu, тому простір імен надто не засмічуватимуть

window = pyglet.window.Window(width=640, height=480, caption="Hello OpenGL!")

# я не буду довго пояснювати що таке декоратор. Просто знайте, що 
# @window.event позначає функції що відповідають за обробку подій

@window.event
def on_draw(): 
    # викликатиметься, коли операційна система вирішить що вікно треба перемалювати
    # наприклад, коли ми забрали вікно що було над нашим, або вперше виводимо його на екран  

    glClearColor(1.0, 1.0, 1.0, 1.0) # Задати колір яким ми будемо очищати екран. 
    # Четверте число - прозорість.
    # Я його сам не дуже розумію, але обов’язково треба чотири параметри.

    glClear(GL_COLOR_BUFFER_BIT) # очистити буфер кольору 
    # (бувають і інші буфери, але про це пізніше)

pyglet.app.run()

To be continued

В цьому уроці я хотів ще написати про те як намалювати трикутник, але часу мало (тобто є інші пріорітети). Зате ми створили вікно і навчились змінювати його колір. Ну й краще напевне писати менше але частіше. Якщо пілотний епізод цього курсу буде популярний – подумаю чи випускати перший сезон.

P.S. Май на увазі, якщо ти не хочеш навчити свою дівчину програмувати – вона може знайти когось хто схоче. :P Або взагалі сама з допомогою інтернету звикне вчитись.


Filed under: Графіка, Кодерство Tagged: OpenGL, Python

OpenGL в Python

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

Інсталяція та перше вікно

Найперше що потрібно графічним програмам – вікно. Щоб створити вікно, нам треба якусь бібліотеку, наприклад PyQt, PySide, PyGtk, WxPython чи PyGame – їх купа. Потрібно також щоб це вікно підтримувало контекст OpenGL (тобто могло дозволити відеокарті виводити свої дані в область вікна). З цим може справитись багато бібліотек, але ми виберемо Pyglet. Тому що в ній мало зайвого, і вона ставиться традиційно:

pip install pyglet

Ну, і як годиться – почнемо з найпростішої програми:

import pyglet

window = pyglet.window.Window(width=640, height=480, caption="Hello OpenGL!")
pyglet.app.run()

Отримаємо вікно заданої ширини та висоти, і з заданим заголовком:

Наше перше вікно

Наше перше вікно

Елементарно, правда?

Фарби

Давайте ще зафарбуємо вікно в білий колір. Для цього потрібно знати що кольори задаються переважно інтенсивністю світла в моделі RGB (червоний, зелений, голубий), числами від 0 до 1. Тобто білий – це 1.0, 1.0, 1.0, сірий – 0.5, 0.5, 0.5, і т.п. Детальніше на вікіпедії.

import pyglet
from pyglet.gl import * # імпортуємо всі функції OpenGL
# вони починатимуться з префіксів gl або glu, тому простір імен надто не засмічуватимуть

window = pyglet.window.Window(width=640, height=480, caption="Hello OpenGL!")

# я не буду довго пояснювати що таке декоратор. Просто знайте, що 
# @window.event позначає функції що відповідають за обробку подій

@window.event
def on_draw(): 
    # викликатиметься, коли операційна система вирішить що вікно треба перемалювати
    # наприклад, коли ми забрали вікно що було над нашим, або вперше виводимо його на екран  

    glClearColor(1.0, 1.0, 1.0, 1.0) # Задати колір яким ми будемо очищати екран. 
    # Четверте число - прозорість.
    # Я його сам не дуже розумію, але обов’язково треба чотири параметри.

    glClear(GL_COLOR_BUFFER_BIT) # очистити буфер кольору 
    # (бувають і інші буфери, але про це пізніше)

pyglet.app.run()

To be continued

В цьому уроці я хотів ще написати про те як намалювати трикутник, але часу мало (тобто є інші пріорітети). Зате ми створили вікно і навчились змінювати його колір. Ну й краще напевне писати менше але частіше. Якщо пілотний епізод цього курсу буде популярний – подумаю чи випускати перший сезон.

P.S. Май на увазі, якщо ти не хочеш навчити свою дівчину програмувати – вона може знайти когось хто схоче. :P Або взагалі сама з допомогою інтернету звикне вчитись.


Filed under: Графіка, Кодерство Tagged: OpenGL, Python

Анонс Lvivpy4

Попередні три я пропустив бо не читаю новини, але от на цьому буду, тому що там я буду нести людям світло науки і знання.

Цитую сам себе:

Ви дізнаєтесь що таке компонент, інтерфейс, інваріант, утиліта, фабрика, адаптер (це не тільки шаблони), реєстр компонентів, і купу інших розумних слів. А також як можна застосовувати штуки що ці слова позначають щоб побудувати систему яку можна розширювати новими компонентами, і реалізувати “статичну типізацію” та “слабку типізацію”.

Одним словом те що ви могли почитати в трактаті про ZCA, тільки тепер в авторській озвучці, + можна буде задавати питання, і побачити мене в 3D. Головне руками не чіпати! :)

Тому якщо в кого виникне таке бажання – заходьте в Офіс Lohika Systems, Львів, вул. Лемківська 15а, 2-й поверх, 30-го травня. Реєстрація: http://www.meetup.com/uapycon/events/222342688/

І не переживайте, там доповідаю не тільки я.

Робота і життя після відпустки починається шалено (правда якщо врахувати що під час відпустки я більшість часу лише спав, їв і дихав), я навіть 10% всього цікавого зараз не розповів, але те що розповів – одне з найголовнішого. :)


Filed under: Кодерство, Нещоденник Tagged: Python

Анонс Lvivpy4

Попередні три я пропустив бо не читаю новини, але от на цьому буду, тому що там я буду нести людям світло науки і знання.

Цитую сам себе:

Ви дізнаєтесь що таке компонент, інтерфейс, інваріант, утиліта, фабрика, адаптер (це не тільки шаблони), реєстр компонентів, і купу інших розумних слів. А також як можна застосовувати штуки що ці слова позначають щоб побудувати систему яку можна розширювати новими компонентами, і реалізувати “статичну типізацію” та “слабку типізацію”.

Одним словом те що ви могли почитати в трактаті про ZCA, тільки тепер в авторській озвучці, + можна буде задавати питання, і побачити мене в 3D. Головне руками не чіпати! :)

Тому якщо в кого виникне таке бажання – заходьте в Офіс Lohika Systems, Львів, вул. Лемківська 15а, 2-й поверх, 30-го травня. Реєстрація: http://www.meetup.com/uapycon/events/222342688/

І не переживайте, там доповідаю не тільки я.

Робота і життя після відпустки починається шалено (правда якщо врахувати що під час відпустки я більшість часу лише спав, їв і дихав), я навіть 10% всього цікавого зараз не розповів, але те що розповів – одне з найголовнішого. :)


Filed under: Кодерство, Нещоденник Tagged: Python

Випадковий ідентифікатор в Python

Можна отримати так:

import random
def random_id(length=6):
   return ''.join(
        random.choice(string.lowercase)
        for i in range(length)
    )

###############
>>> random_id()
'kqxmua'

Якщо треба особливо оформлений, як от IP, чи MAC-адреса, то можна зробити перетворення:

def asmac(val):
    """Convert a byte string to a MAC address string.  """
    return ':'.join('%02X' % ord(c) for c in val)

def random_mac():
    return asmac(random_id())

###################
>>> random_mac()
'78:71:6A:72:6E:63'

Але такі ідентифікатори як “kqxmua” нормальній людині важко запам’ятати, бо вони не асоціюються з жодними поняттями. Ну окрім частинки “ua”, але й то вона туди випадково потрапила. Проте, в Linux можна легко отримати випадкове слово, бо в ньому є словник:

def random_word():
    return random.choice(
        open('/usr/share/dict/words').readlines() # жертиме пам’ять! 
    ).strip()

#################
'.join(random_word() for i in range(5))
'hermitical, Canter, Paryavi, mergences, Mind'

Хоча я знайомий лише з “hermitical” та “mind”, але асоціації вже легше побудувати, правда?


Filed under: Всяке, Кодерство Tagged: linux, Python

Випадковий ідентифікатор в Python

Можна отримати так:

import random
def random_id(length=6):
   return ''.join(
        random.choice(string.lowercase)
        for i in range(length)
    )

###############
>>> random_id()
'kqxmua'

Якщо треба особливо оформлений, як от IP, чи MAC-адреса, то можна зробити перетворення:

def asmac(val):
    """Convert a byte string to a MAC address string.  """
    return ':'.join('%02X' % ord(c) for c in val)

def random_mac():
    return asmac(random_id())

###################
>>> random_mac()
'78:71:6A:72:6E:63'

Але такі ідентифікатори як “kqxmua” нормальній людині важко запам’ятати, бо вони не асоціюються з жодними поняттями. Ну окрім частинки “ua”, але й то вона туди випадково потрапила. Проте, в Linux можна легко отримати випадкове слово, бо в ньому є словник:

def random_word():
    return random.choice(
        open('/usr/share/dict/words').readlines() # жертиме пам’ять! 
    ).strip()

#################
'.join(random_word() for i in range(5))
'hermitical, Canter, Paryavi, mergences, Mind'

Хоча я знайомий лише з “hermitical” та “mind”, але асоціації вже легше побудувати, правда?


Filed under: Всяке, Кодерство Tagged: linux, Python

Зберегти всю стіну групи VK в таблицю

Якось під час чергового наближення економічної кризи захотілось проаналізувати ціну нерухомості в Львові. А ще, в той же час я наткнувся на документацію API Вконтакті. А так як своє житло я шукав в тому числі і в тій соціальній мережі, то вирішив проаналізувати наприклад спільноту vk.com/nomakler.

Ну, проаналізувати це легко сказати – важче зробити. Як витягти з повідомлення ціну, і як відрізнити попит від пропозиції? Га?

Але є половинний результат – ФСБ мусило поділитись частиною своєї бази даних розміром в 40450 оголошень. Тут можна її завантажити як tsv, xls чи інший зручний для вас формат. Може комусь, хто захоче збільшити конкуренцію серед львівськи маклерів/ріелторів знадобиться.

А якщо кому потрібна інформація з інших груп – ось скрипт. Викликаєте функцію save2tsv з назвою групи і назвою файлу в який писати – і чекаєте поки завантажиться.

import requests
import json
from pprint import pprint
from itertools import islice
from datetime import datetime

from butils.csv_wrapper import UnicodeWriter

class APIError(Exception):
    pass

def vk(method, **kwargs):
    '''
        https://vk.com/dev/methods
    '''
    r = requests.get(
        'https://api.vk.com/method/%s' % method,
        params=kwargs
    )
    js = json.loads(r.text)
    if js.get('error'):
        raise APIError(js['error']['error_msg'])

    return js['response']


def get_users(ids, known_users={}):
    request_ids = [i for i in ids if i not in known_users]
    if request_ids:
        user_ids=','.join(str(i) for i in request_ids if i > 0)
        if user_ids:
            users = vk('users.get', user_ids=user_ids)
        else:
            users = []
        group_ids=','.join(str(-i) for i in request_ids if i < 0)
        if group_ids:
            groups = vk('groups.getById', group_ids=group_ids)
        else:
            groups = []
        for user in users:
            known_users[int(user['uid'])] = dict(
                first_name = user['first_name'],
                last_name = user['last_name'],
            )
        for group in groups:
            known_users[-int(group['gid'])] = dict(
                first_name = group['name'],
                last_name = group['gid']
            )
    return known_users


def get_wall(domain):
    count = 50
    offset = 0
    def get_with_offset(offset):
        nonlocal count
        print('get_with_offset(%s)' % offset)
        total = vk('wall.get',
            domain=domain,
            count=1,
        )[0]
        off = total - offset - count
        if off < 0:
            count = count + off
            off = 0
        return vk('wall.get',
            domain=domain,
            count=count,
            offset=off,
        )[1:][::-1] # remove first and reverse

    while True:
        posts = get_with_offset(offset)
        offset += count
        users = get_users(p['from_id'] for p in posts)
        for p in posts:
            yield p, users[p['from_id']]
        if count < 50:
            return


def save2tsv(domain, dst):
    with UnicodeWriter(dst, encoding='utf-8', delimiter='t') as writer:
        writer.writerow((
            'ID',
            'Datetime',
            'First name',
            'Last name',
            'Text',
            'Type',
            'Comments',
            'Reposts',
            'Likes',
        ))
        for p, user in get_wall(domain):
            writer.writerow(list(map(str, (
                p['id'], # ID
                datetime.fromtimestamp(p['date']), # Datetime
                user['first_name'], # First name
                user['last_name'], # Last name
                p['text'], # Text
                p['post_type'], # Type
                p['comments']['count'], # Comments
                p['reposts']['count'], # Reposts
                p['likes']['count'], # Likes
            ))))

# save2tsv('nomakler', 'nomakler.tsv')

Filed under: Кодерство, Павутина Tagged: Python

Зберегти всю стіну групи VK в таблицю

Якось під час чергового наближення економічної кризи захотілось проаналізувати ціну нерухомості в Львові. А ще, в той же час я наткнувся на документацію API Вконтакті. А так як своє житло я шукав в тому числі і в тій соціальній мережі, то вирішив проаналізувати наприклад спільноту vk.com/nomakler.

Ну, проаналізувати це легко сказати – важче зробити. Як витягти з повідомлення ціну, і як відрізнити попит від пропозиції? Га?

Але є половинний результат – ФСБ мусило поділитись частиною своєї бази даних розміром в 40450 оголошень. Тут можна її завантажити як tsv, xls чи інший зручний для вас формат. Може комусь, хто захоче збільшити конкуренцію серед львівськи маклерів/ріелторів знадобиться.

А якщо кому потрібна інформація з інших груп – ось скрипт. Викликаєте функцію save2tsv з назвою групи і назвою файлу в який писати – і чекаєте поки завантажиться.

import requests
import json
from pprint import pprint
from itertools import islice
from datetime import datetime

from butils.csv_wrapper import UnicodeWriter

class APIError(Exception):
    pass

def vk(method, **kwargs):
    '''
        https://vk.com/dev/methods
    '''
    r = requests.get(
        'https://api.vk.com/method/%s' % method,
        params=kwargs
    )
    js = json.loads(r.text)
    if js.get('error'):
        raise APIError(js['error']['error_msg'])

    return js['response']


def get_users(ids, known_users={}):
    request_ids = [i for i in ids if i not in known_users]
    if request_ids:
        user_ids=','.join(str(i) for i in request_ids if i > 0)
        if user_ids:
            users = vk('users.get', user_ids=user_ids)
        else:
            users = []
        group_ids=','.join(str(-i) for i in request_ids if i < 0)
        if group_ids:
            groups = vk('groups.getById', group_ids=group_ids)
        else:
            groups = []
        for user in users:
            known_users[int(user['uid'])] = dict(
                first_name = user['first_name'],
                last_name = user['last_name'],
            )
        for group in groups:
            known_users[-int(group['gid'])] = dict(
                first_name = group['name'],
                last_name = group['gid']
            )
    return known_users


def get_wall(domain):
    count = 50
    offset = 0
    def get_with_offset(offset):
        nonlocal count
        print('get_with_offset(%s)' % offset)
        total = vk('wall.get',
            domain=domain,
            count=1,
        )[0]
        off = total - offset - count
        if off < 0:
            count = count + off
            off = 0
        return vk('wall.get',
            domain=domain,
            count=count,
            offset=off,
        )[1:][::-1] # remove first and reverse

    while True:
        posts = get_with_offset(offset)
        offset += count
        users = get_users(p['from_id'] for p in posts)
        for p in posts:
            yield p, users[p['from_id']]
        if count < 50:
            return


def save2tsv(domain, dst):
    with UnicodeWriter(dst, encoding='utf-8', delimiter='\t') as writer:
        writer.writerow((
            'ID',
            'Datetime',
            'First name',
            'Last name',
            'Text',
            'Type',
            'Comments',
            'Reposts',
            'Likes',
        ))
        for p, user in get_wall(domain):
            writer.writerow(list(map(str, (
                p['id'], # ID
                datetime.fromtimestamp(p['date']), # Datetime
                user['first_name'], # First name
                user['last_name'], # Last name
                p['text'], # Text
                p['post_type'], # Type
                p['comments']['count'], # Comments
                p['reposts']['count'], # Reposts
                p['likes']['count'], # Likes
            ))))

# save2tsv('nomakler', 'nomakler.tsv')

Filed under: Кодерство, Павутина Tagged: Python