<commit_before>import virtool.utils
import virtool.virus_index
import virtool.virus_history
from virtool.handlers.utils import json_response, bad_request, not_found, protected, compose_regex_query, paginate, \
    conflict

async def find(req):
    """
    Return a list of indexes.

    """
    db = req.app["db"]

    documents = await db.indexes.find({}, virtool.virus_index.PROJECTION, sort=[("version", -1)]).to_list(None)

    modified_virus_count = await db.viruses.count({"modified": True})
    total_virus_count = await db.viruses.count()

    return json_response({
        "documents": [virtool.utils.base_processor(d) for d in documents],
        "modified_virus_count": modified_virus_count,
        "total_virus_count": total_virus_count
    })

async def get(req):
    """
    Get the complete document for a given index.

    """
    db = req.app["db"]

    index_id_or_version = req.match_info["index_id_or_version"]

    try:
        document = await db.indexes.find_one({"version": int(index_id_or_version)})
    except ValueError:
        document = await db.indexes.find_one(index_id_or_version)

    if not document:
        return not_found()

    document = virtool.utils.base_processor(document)

    contributors = await db.history.aggregate([
        {"$match": {
            "index.id": document["id"]
        }},
        {"$group": {
            "_id": "$user.id",
            "count": {"$sum": 1}
        }}
    ]).to_list(None)

    document["contributors"] = [{"id": c["_id"], "count": c["count"]} for c in contributors]

    viruses = await db.history.aggregate([
        {"$match": {
            "index.id": document["id"]
        }},
        {"$sort": {
            "virus.id": 1,
            "virus.version": -1
        }},
        {"$group": {
            "_id": "$virus.id",
            "name": {"$first": "$virus.name"},
            "count": {"$sum": 1}
        }},
        {"$match": {
            "name": {"$ne": None}
        }},
        {"$sort": {
            "name": 1
        }}
    ]).to_list(None)

    document["viruses"] = [{"id": v["_id"], "name": v["name"], "change_count": v["count"]} for v in viruses]

    document["change_count"] = sum(v["count"] for v in viruses)

    del document["modified_virus_count"]
    del document["modification_count"]

    return json_response(document)

async def get_unbuilt(req):
    db = req.app["db"]

    history = await db.history.find({"index.id": "unbuilt"}, virtool.virus_history.LIST_PROJECTION).to_list(None)

    return json_response({
        "history": [virtool.utils.base_processor(c) for c in history]
    })

@protected("rebuild_index")
async def create(req):
    """
    Starts a job to rebuild the viruses Bowtie2 index on disk. Does a check to make sure there are no unverified
    viruses in the collection and updates virus history to show the version and id of the new index.

    """
    db = req.app["db"]

    if await db.indexes.count({"ready": False}):
        return conflict("Index build already in progress")

    if await db.viruses.count({"verified": False}):
        return bad_request("There are unverified viruses")

    if not await db.history.count({"index.id": "unbuilt"}):
        return bad_request("There are no unbuilt changes")

    index_id = await virtool.utils.get_new_id(db.indexes)
    index_version = await virtool.virus_index.get_current_index_version(db) + 1

    user_id = req["session"].user_id

    job_id = await virtool.utils.get_new_id(db.jobs)

    await db.indexes.insert_one({
        "_id": index_id,
        "version": index_version,
        "created_at": virtool.utils.timestamp(),
        "virus_count": None,
        "ready": False,
        "has_files": True,
        "user": {
            "id": user_id
        },
        "job": {
            "id": job_id
        }
    })

    # Update all history entries with no index_version to the new index version.
    await db.history.update_many({"index.id": "unbuilt"}, {
        "$set": {
            "index": {
                "id": index_id,
                "version": index_version
            }
        }
    })

    # Generate a dict of virus document version numbers keyed by the document id. We use this to make sure only changes
    # made at the time the index rebuild was started are
    virus_manifest = dict()

    async for document in db.viruses.find({}, ["version"]):
        virus_manifest[document["_id"]] = document["version"]

    # A dict of task_args for the rebuild job.
    task_args = {
        "user_id": user_id,
        "index_id": index_id,
        "index_version": index_version,
        "virus_manifest": virus_manifest
    }

    # Start the job.
    await req.app["job_manager"].new("rebuild_index", task_args, 2, 2, user_id, job_id=job_id)

    document = await db.indexes.find_one(index_id)

    return json_response(virtool.utils.base_processor(document), status=201)

async def find_history(req):
    db = req.app["db"]

    index_id_or_version = req.match_info["index_id_or_version"]

    print(index_id_or_version)

    try:
        document = await db.indexes.find_one({"version": int(index_id_or_version)}, ["_id"])
    except ValueError:
        document = await db.indexes.find_one(index_id_or_version, ["_id"])

    if not document:
        return not_found()

    term = req.query.get("term", None)

    db_query = {
        "index.id": document["_id"]
    }

    if term:
        db_query.update(compose_regex_query(term, ["virus.name", "user.id"]))

    data = await paginate(
        db.history,
        db_query,
        req.query,
        "created_at",
        projection=virtool.virus_history.LIST_PROJECTION,
        reverse=True
    )

    return json_response(data)
<commit_msg>Refactor indexes API handler for new RebuildIndex class<commit_after>import virtool.utils
import virtool.virus_index
import virtool.virus_history
from virtool.handlers.utils import json_response, bad_request, not_found, protected, compose_regex_query, paginate, \
    conflict

async def find(req):
    """
    Return a list of indexes.

    """
    db = req.app["db"]

    documents = await db.indexes.find({}, virtool.virus_index.PROJECTION, sort=[("version", -1)]).to_list(None)

    modified_virus_count = await db.viruses.count({"modified": True})
    total_virus_count = await db.viruses.count()

    return json_response({
        "documents": [virtool.utils.base_processor(d) for d in documents],
        "modified_virus_count": modified_virus_count,
        "total_virus_count": total_virus_count
    })

async def get(req):
    """
    Get the complete document for a given index.

    """
    db = req.app["db"]

    index_id_or_version = req.match_info["index_id_or_version"]

    try:
        document = await db.indexes.find_one({"version": int(index_id_or_version)})
    except ValueError:
        document = await db.indexes.find_one(index_id_or_version)

    if not document:
        return not_found()

    document = virtool.utils.base_processor(document)

    contributors = await db.history.aggregate([
        {"$match": {
            "index.id": document["id"]
        }},
        {"$group": {
            "_id": "$user.id",
            "count": {"$sum": 1}
        }}
    ]).to_list(None)

    document["contributors"] = [{"id": c["_id"], "count": c["count"]} for c in contributors]

    viruses = await db.history.aggregate([
        {"$match": {
            "index.id": document["id"]
        }},
        {"$sort": {
            "virus.id": 1,
            "virus.version": -1
        }},
        {"$group": {
            "_id": "$virus.id",
            "name": {"$first": "$virus.name"},
            "count": {"$sum": 1}
        }},
        {"$match": {
            "name": {"$ne": None}
        }},
        {"$sort": {
            "name": 1
        }}
    ]).to_list(None)

    document["viruses"] = [{"id": v["_id"], "name": v["name"], "change_count": v["count"]} for v in viruses]

    document["change_count"] = sum(v["count"] for v in viruses)

    del document["modified_virus_count"]
    del document["modification_count"]

    return json_response(document)

async def get_unbuilt(req):
    db = req.app["db"]

    history = await db.history.find({"index.id": "unbuilt"}, virtool.virus_history.LIST_PROJECTION).to_list(None)

    return json_response({
        "history": [virtool.utils.base_processor(c) for c in history]
    })

@protected("rebuild_index")
async def create(req):
    """
    Starts a job to rebuild the viruses Bowtie2 index on disk. Does a check to make sure there are no unverified
    viruses in the collection and updates virus history to show the version and id of the new index.

    """
    db = req.app["db"]

    if await db.indexes.count({"ready": False}):
        return conflict("Index build already in progress")

    if await db.viruses.count({"verified": False}):
        return bad_request("There are unverified viruses")

    if not await db.history.count({"index.id": "unbuilt"}):
        return bad_request("There are no unbuilt changes")

    index_id = await virtool.utils.get_new_id(db.indexes)
    index_version = await virtool.virus_index.get_current_index_version(db) + 1

    user_id = req["session"].user_id

    job_id = await virtool.utils.get_new_id(db.jobs)

    # Generate a dict of virus document version numbers keyed by the document id. We use this to make sure only changes
    # made at the time the index rebuild was started are
    virus_manifest = dict()

    async for document in db.viruses.find({}, ["version"]):
        virus_manifest[document["_id"]] = document["version"]

    await db.indexes.insert_one({
        "_id": index_id,
        "version": index_version,
        "created_at": virtool.utils.timestamp(),
        "virus_count": None,
        "manifest": virus_manifest,
        "ready": False,
        "has_files": True,
        "user": {
            "id": user_id
        },
        "job": {
            "id": job_id
        }
    })

    # Update all history entries with no index_version to the new index version.
    await db.history.update_many({"index.id": "unbuilt"}, {
        "$set": {
            "index": {
                "id": index_id,
                "version": index_version
            }
        }
    })

    # A dict of task_args for the rebuild job.
    task_args = {
        "user_id": user_id,
        "index_id": index_id,
        "index_version": index_version,
        "virus_manifest": virus_manifest
    }

    # Start the job.
    await req.app["job_manager"].new("rebuild_index", task_args, user_id, job_id=job_id)

    document = await db.indexes.find_one(index_id)

    return json_response(virtool.utils.base_processor(document), status=201)

async def find_history(req):
    db = req.app["db"]

    index_id_or_version = req.match_info["index_id_or_version"]

    print(index_id_or_version)

    try:
        document = await db.indexes.find_one({"version": int(index_id_or_version)}, ["_id"])
    except ValueError:
        document = await db.indexes.find_one(index_id_or_version, ["_id"])

    if not document:
        return not_found()

    term = req.query.get("term", None)

    db_query = {
        "index.id": document["_id"]
    }

    if term:
        db_query.update(compose_regex_query(term, ["virus.name", "user.id"]))

    data = await paginate(
        db.history,
        db_query,
        req.query,
        "created_at",
        projection=virtool.virus_history.LIST_PROJECTION,
        reverse=True
    )

    return json_response(data)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2014, Vispy Development Team.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

"""
vispy backend for glut.
"""

from __future__ import division

import OpenGL.error
import OpenGL.GLUT as glut

from ..base import BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend
from ...util import ptime, keys

# glut.GLUT_ACTIVE_SHIFT: keys.SHIFT,
# glut.GLUT_ACTIVE_CTRL: keys.CONTROL,
# glut.GLUT_ACTIVE_ALT: keys.ALT,
# -1: keys.META,

# Map native keys to vispy keys
KEYMAP = {
    -1: keys.SHIFT,
    -2: keys.CONTROL,
    -3: keys.ALT,
    -4: keys.META,

    glut.GLUT_KEY_LEFT: keys.LEFT,
    glut.GLUT_KEY_UP: keys.UP,
    glut.GLUT_KEY_RIGHT: keys.RIGHT,
    glut.GLUT_KEY_DOWN: keys.DOWN,
    glut.GLUT_KEY_PAGE_UP: keys.PAGEUP,
    glut.GLUT_KEY_PAGE_DOWN: keys.PAGEDOWN,

    glut.GLUT_KEY_INSERT: keys.INSERT,
    chr(127): keys.DELETE,
    glut.GLUT_KEY_HOME: keys.HOME,
    glut.GLUT_KEY_END: keys.END,

    chr(27): keys.ESCAPE,
    chr(8): keys.BACKSPACE,

    glut.GLUT_KEY_F1: keys.F1,
    glut.GLUT_KEY_F2: keys.F2,
    glut.GLUT_KEY_F3: keys.F3,
    glut.GLUT_KEY_F4: keys.F4,
    glut.GLUT_KEY_F5: keys.F5,
    glut.GLUT_KEY_F6: keys.F6,
    glut.GLUT_KEY_F7: keys.F7,
    glut.GLUT_KEY_F8: keys.F8,
    glut.GLUT_KEY_F9: keys.F9,
    glut.GLUT_KEY_F10: keys.F10,
    glut.GLUT_KEY_F11: keys.F11,
    glut.GLUT_KEY_F12: keys.F12,

    ' ': keys.SPACE,
    '\r': keys.ENTER,
    '\n': keys.ENTER,
    '\t': keys.TAB,
}

BUTTONMAP = {glut.GLUT_LEFT_BUTTON: 1,
             glut.GLUT_RIGHT_BUTTON: 2,
             glut.GLUT_MIDDLE_BUTTON: 3
             }

ALL_WINDOWS = []

class ApplicationBackend(BaseApplicationBackend):

    def __init__(self):
        BaseApplicationBackend.__init__(self)
        self._initialized = False
        self._windows = []

    def _vispy_get_backend_name(self):
        return 'Glut'

    def _vispy_process_events(self):
        pass  # not possible?

    def _vispy_run(self):
        self._vispy_get_native_app()  # Force exist
        return glut.glutMainLoop()

    def _vispy_quit(self):
        global ALL_WINDOWS
        for win in ALL_WINDOWS:
            win._vispy_close()

    def _vispy_get_native_app(self):
        import sys
        import ctypes
        from OpenGL import platform

        # HiDPI support for retina display
        # This requires glut from
        # http://iihm.imag.fr/blanch/software/glut-macosx/
        if sys.platform == 'darwin':
            try:
                glutInitDisplayString = platform.createBaseFunction(
                    'glutInitDisplayString',
                    dll=platform.GLUT,
                    resultType=None,
                    argTypes=[
                        ctypes.c_char_p],
                    doc='glutInitDisplayString(  ) -> None',
                    argNames=())
                text = ctypes.c_char_p("rgba stencil double samples=8 hidpi")
                glutInitDisplayString(text)
            except:
                pass
        if not self._initialized:
            glut.glutInit()  # todo: maybe allow user to give args?
            glut.glutInitDisplayMode(glut.GLUT_RGBA |
                                     glut.GLUT_DOUBLE |
                                     glut.GLUT_STENCIL |
                                     glut.GLUT_DEPTH)
            self._initialized = True
        return glut

class CanvasBackend(BaseCanvasBackend):

    """ GLUT backend for Canvas abstract class."""

    def __init__(self, name='glut window', *args, **kwargs):
        BaseCanvasBackend.__init__(self)
        self._id = glut.glutCreateWindow(name)
        global ALL_WINDOWS
        ALL_WINDOWS.append(self)

        # Cache of modifiers so we can send modifiers along with mouse motion
        self._modifiers_cache = ()

        # Note: this seems to cause the canvas to ignore calls to show()
        # about half of the time.
        # glut.glutHideWindow()  # Start hidden, like the other backends

        # Register callbacks
        glut.glutDisplayFunc(self.on_draw)
        glut.glutReshapeFunc(self.on_resize)
        # glut.glutVisibilityFunc(self.on_show)
        glut.glutKeyboardFunc(self.on_key_press)
        glut.glutSpecialFunc(self.on_key_press)
        glut.glutKeyboardUpFunc(self.on_key_release)
        glut.glutMouseFunc(self.on_mouse_action)
        glut.glutMotionFunc(self.on_mouse_motion)
        glut.glutPassiveMotionFunc(self.on_mouse_motion)

        # Set close function. See issue #10. For some reason, the function
        # can still not exist even if we checked its boolean status.
        closeFuncSet = False
        if bool(glut.glutWMCloseFunc):  # OSX specific test
            try:
                glut.glutWMCloseFunc(self.on_close)
                closeFuncSet = True
            except OpenGL.error.NullFunctionError:
                pass
        if not closeFuncSet:
            try:
                glut.glutCloseFunc(self.on_close)
                closeFuncSet = True
            except OpenGL.error.NullFunctionError:
                pass

        # glut.glutFunc(self.on_)

        self._initialized = False

        # LC: I think initializing here makes it more consistent with other
        # backends
        glut.glutTimerFunc(0, self._emit_initialize, None)

    def _emit_initialize(self, _=None):
        if not self._initialized:
            self._initialized = True
            self._vispy_canvas.events.initialize()

    def _vispy_set_current(self):
        # Make this the current context
        glut.glutSetWindow(self._id)

    def _vispy_swap_buffers(self):
        # Swap front and back buffer
        glut.glutSetWindow(self._id)
        glut.glutSwapBuffers()

    def _vispy_set_title(self, title):
        # Set the window title. Has no effect for widgets
        glut.glutSetWindow(self._id)
        glut.glutSetWindowTitle(title)

    def _vispy_set_size(self, w, h):
        # Set size of the widget or window
        glut.glutSetWindow(self._id)
        glut.glutReshapeWindow(w, h)

    def _vispy_set_position(self, x, y):
        # Set position of the widget or window. May have no effect for widgets
        glut.glutSetWindow(self._id)
        glut.glutPositionWindow(x, y)

    def _vispy_set_visible(self, visible):
        # Show or hide the window or widget
        glut.glutSetWindow(self._id)
        if visible:
            glut.glutShowWindow()
        else:
            glut.glutHideWindow()

    def _vispy_update(self):
        # Invoke a redraw
        glut.glutSetWindow(self._id)
        glut.glutPostRedisplay()

    def _vispy_close(self):
        # Force the window or widget to shut down
        glut.glutDestroyWindow(self._id)

    def _vispy_get_geometry(self):
        # Should return widget (x, y, w, h)
        glut.glutSetWindow(self._id)
        x = glut.glutGet(glut.GLUT_WINDOW_X)
        y = glut.glutGet(glut.GLUT_WINDOW_Y)
        w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        return x, y, w, h

    def _vispy_get_size(self):
        glut.glutSetWindow(self._id)
        w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        return w, h

    def _vispy_get_position(self):
        glut.glutSetWindow(self._id)
        x = glut.glutGet(glut.GLUT_WINDOW_X)
        y = glut.glutGet(glut.GLUT_WINDOW_Y)
        return x, y

    def on_resize(self, w, h):
        if self._vispy_canvas is None:
            return
        self._vispy_canvas.events.resize(size=(w, h))

    def on_close(self):
        if self._vispy_canvas is None:
            return
        self._vispy_canvas.events.close()

    def on_draw(self, dummy=None):
        if self._vispy_canvas is None:
            return
        if not self._initialized:
            # The timer that we set may not have fired just yet
            self._emit_initialize()

        #w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        #h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        self._vispy_canvas.events.paint(region=None)  # (0, 0, w, h))

    def on_mouse_action(self, button, state, x, y):
        if self._vispy_canvas is None:
            return
        action = {glut.GLUT_UP: 'release', glut.GLUT_DOWN: 'press'}[state]
        mod = self._modifiers(False)

        if button < 3:
            # Mouse click event
            button = BUTTONMAP.get(button, 0)
            if action == 'press':
                self._vispy_mouse_press(
                    pos=(
                        x,
                        y),
                    button=button,
                    modifiers=mod)
            else:
                self._vispy_mouse_release(
                    pos=(
                        x,
                        y),
                    button=button,
                    modifiers=mod)

        elif button in (3, 4):
            # Wheel event
            deltay = 1.0 if button == 3 else -1.0
            self._vispy_canvas.events.mouse_wheel(
                pos=(x, y),
                delta=(0.0, deltay),
                modifiers=mod,
            )

    def on_mouse_motion(self, x, y):
        if self._vispy_canvas is None:
            return
        self._vispy_mouse_move(
            pos=(x, y),
            modifiers=self._modifiers(False),
        )

    def on_key_press(self, key, x, y):
        key, text = self._process_key(key)
        self._vispy_canvas.events.key_press(
            key=key,
            text=text,
            modifiers=self._modifiers(),
        )

    def on_key_release(self, key, x, y):
        key, text = self._process_key(key)
        self._vispy_canvas.events.key_release(
            key=key,
            text=text,
            modifiers=self._modifiers()
        )

    def _process_key(self, key):
        if key in KEYMAP:
            if isinstance(key, int):
                return KEYMAP[key], ''
            else:
                return KEYMAP[key], key
        elif isinstance(key, int):
            return None, ''  # unsupported special char
        else:
            return keys.Key(key.upper()), key

    def _modifiers(self, query_now=True):
        if query_now:
            glutmod = glut.glutGetModifiers()
            mod = ()
            if glut.GLUT_ACTIVE_SHIFT & glutmod:
                mod += keys.SHIFT,
            if glut.GLUT_ACTIVE_CTRL & glutmod:
                mod += keys.CONTROL,
            if glut.GLUT_ACTIVE_ALT & glutmod:
                mod += keys.ALT,
            self._modifiers_cache = mod
        return self._modifiers_cache

#import weakref

def _glut_callback(id):
    # Get weakref wrapper for timer
    timer = TimerBackend._timers.get(id, None)
    if timer is None:
        return
    # Get timer object
    timer = timer()
    if timer is None:
        return
    # Kick it!
    if timer._vispy_timer._running:
        timer._vispy_timer._timeout()
        ms = int(timer._vispy_timer._interval * 1000)
        glut.glutTimerFunc(ms, _glut_callback, timer._id)

# class TimerBackend(BaseTimerBackend):
    #_counter = 0
    #_timers = {}

    # def __init__(self, vispy_timer):
        #BaseTimerBackend.__init__(self, vispy_timer)
        # Give this timer a unique id
        #TimerBackend._counter += 1
        #self._id = TimerBackend._counter
        # Store this timer (using a weak ref)
        #self._timers[self._id] = weakref.ref(self)

    #@classmethod
    # def _glut_callback(cls, id):
        # Get weakref wrapper for timer
        #timer = cls._timers.get(id, None)
        # if timer is None:
            # return
        # Get timer object
        #timer = timer()
        # if timer is None:
            # return
        # Kick it!
        # if timer._vispy_timer._running:
            # timer._vispy_timer._timeout()
            #ms = int(timer._vispy_timer._interval*1000)
            #glut.glutTimerFunc(ms, TimerBackend._glut_callback, timer._id)

    # def _vispy_start(self, interval):
        ##glut.glutTimerFunc(int(interval*1000), TimerBackend._glut_callback,
        ##                   self._id)
        #glut.glutTimerFunc(int(interval*1000), _glut_callback, self._id)

    # def _vispy_stop(self):
        # pass

    # def _vispy_get_native_timer(self):
        # return glut # or self?

# Note: we could also build a timer using glutTimerFunc, but this causes
# trouble because timer callbacks appear to take precedence over all others.
# Thus, a fast timer can block new display events.
class TimerBackend(BaseTimerBackend):
    _initialized = False
    _schedule = []

    def __init__(self, vispy_timer):
        BaseTimerBackend.__init__(self, vispy_timer)
        self._init_class()

    @classmethod
    def _init_class(cls):
        if cls._initialized:
            return
        glut.glutIdleFunc(cls._idle_callback)
        cls._initialized = True

    @classmethod
    def _idle_callback(cls):
        now = ptime.time()
        new_schedule = []

        # see whether there are any timers ready
        while len(cls._schedule) > 0 and cls._schedule[0][0] <= now:
            timer = cls._schedule.pop(0)[1]
            timer._vispy_timer._timeout()
            if timer._vispy_timer.running:
                new_schedule.append((now + timer._vispy_timer.interval, timer))

        # schedule next round of timeouts
        if len(new_schedule) > 0:
            cls._schedule.extend(new_schedule)
            cls._schedule.sort()

    def _vispy_start(self, interval):
        now = ptime.time()
        self._schedule.append((now + interval, self))

    def _vispy_stop(self):
        pass

    def _vispy_get_native_timer(self):
        return None  # glut has no native timer objects.
<commit_msg>FIX: Fix GLUT nested import<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2014, Vispy Development Team.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.

"""
vispy backend for glut.
"""

from __future__ import division

import sys
import ctypes
from OpenGL import platform

import OpenGL.error
import OpenGL.GLUT as glut

from ..base import BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend
from ...util import ptime, keys

# glut.GLUT_ACTIVE_SHIFT: keys.SHIFT,
# glut.GLUT_ACTIVE_CTRL: keys.CONTROL,
# glut.GLUT_ACTIVE_ALT: keys.ALT,
# -1: keys.META,

# Map native keys to vispy keys
KEYMAP = {
    -1: keys.SHIFT,
    -2: keys.CONTROL,
    -3: keys.ALT,
    -4: keys.META,

    glut.GLUT_KEY_LEFT: keys.LEFT,
    glut.GLUT_KEY_UP: keys.UP,
    glut.GLUT_KEY_RIGHT: keys.RIGHT,
    glut.GLUT_KEY_DOWN: keys.DOWN,
    glut.GLUT_KEY_PAGE_UP: keys.PAGEUP,
    glut.GLUT_KEY_PAGE_DOWN: keys.PAGEDOWN,

    glut.GLUT_KEY_INSERT: keys.INSERT,
    chr(127): keys.DELETE,
    glut.GLUT_KEY_HOME: keys.HOME,
    glut.GLUT_KEY_END: keys.END,

    chr(27): keys.ESCAPE,
    chr(8): keys.BACKSPACE,

    glut.GLUT_KEY_F1: keys.F1,
    glut.GLUT_KEY_F2: keys.F2,
    glut.GLUT_KEY_F3: keys.F3,
    glut.GLUT_KEY_F4: keys.F4,
    glut.GLUT_KEY_F5: keys.F5,
    glut.GLUT_KEY_F6: keys.F6,
    glut.GLUT_KEY_F7: keys.F7,
    glut.GLUT_KEY_F8: keys.F8,
    glut.GLUT_KEY_F9: keys.F9,
    glut.GLUT_KEY_F10: keys.F10,
    glut.GLUT_KEY_F11: keys.F11,
    glut.GLUT_KEY_F12: keys.F12,

    ' ': keys.SPACE,
    '\r': keys.ENTER,
    '\n': keys.ENTER,
    '\t': keys.TAB,
}

BUTTONMAP = {glut.GLUT_LEFT_BUTTON: 1,
             glut.GLUT_RIGHT_BUTTON: 2,
             glut.GLUT_MIDDLE_BUTTON: 3
             }

ALL_WINDOWS = []

class ApplicationBackend(BaseApplicationBackend):

    def __init__(self):
        BaseApplicationBackend.__init__(self)
        self._initialized = False
        self._windows = []

    def _vispy_get_backend_name(self):
        return 'Glut'

    def _vispy_process_events(self):
        pass  # not possible?

    def _vispy_run(self):
        self._vispy_get_native_app()  # Force exist
        return glut.glutMainLoop()

    def _vispy_quit(self):
        global ALL_WINDOWS
        for win in ALL_WINDOWS:
            win._vispy_close()

    def _vispy_get_native_app(self):
        # HiDPI support for retina display
        # This requires glut from
        # http://iihm.imag.fr/blanch/software/glut-macosx/
        if sys.platform == 'darwin':
            try:
                glutInitDisplayString = platform.createBaseFunction(
                    'glutInitDisplayString',
                    dll=platform.GLUT,
                    resultType=None,
                    argTypes=[
                        ctypes.c_char_p],
                    doc='glutInitDisplayString(  ) -> None',
                    argNames=())
                text = ctypes.c_char_p("rgba stencil double samples=8 hidpi")
                glutInitDisplayString(text)
            except:
                pass
        if not self._initialized:
            glut.glutInit()  # todo: maybe allow user to give args?
            glut.glutInitDisplayMode(glut.GLUT_RGBA |
                                     glut.GLUT_DOUBLE |
                                     glut.GLUT_STENCIL |
                                     glut.GLUT_DEPTH)
            self._initialized = True
        return glut

class CanvasBackend(BaseCanvasBackend):

    """ GLUT backend for Canvas abstract class."""

    def __init__(self, name='glut window', *args, **kwargs):
        BaseCanvasBackend.__init__(self)
        self._id = glut.glutCreateWindow(name)
        global ALL_WINDOWS
        ALL_WINDOWS.append(self)

        # Cache of modifiers so we can send modifiers along with mouse motion
        self._modifiers_cache = ()

        # Note: this seems to cause the canvas to ignore calls to show()
        # about half of the time.
        # glut.glutHideWindow()  # Start hidden, like the other backends

        # Register callbacks
        glut.glutDisplayFunc(self.on_draw)
        glut.glutReshapeFunc(self.on_resize)
        # glut.glutVisibilityFunc(self.on_show)
        glut.glutKeyboardFunc(self.on_key_press)
        glut.glutSpecialFunc(self.on_key_press)
        glut.glutKeyboardUpFunc(self.on_key_release)
        glut.glutMouseFunc(self.on_mouse_action)
        glut.glutMotionFunc(self.on_mouse_motion)
        glut.glutPassiveMotionFunc(self.on_mouse_motion)

        # Set close function. See issue #10. For some reason, the function
        # can still not exist even if we checked its boolean status.
        closeFuncSet = False
        if bool(glut.glutWMCloseFunc):  # OSX specific test
            try:
                glut.glutWMCloseFunc(self.on_close)
                closeFuncSet = True
            except OpenGL.error.NullFunctionError:
                pass
        if not closeFuncSet:
            try:
                glut.glutCloseFunc(self.on_close)
                closeFuncSet = True
            except OpenGL.error.NullFunctionError:
                pass

        # glut.glutFunc(self.on_)

        self._initialized = False

        # LC: I think initializing here makes it more consistent with other
        # backends
        glut.glutTimerFunc(0, self._emit_initialize, None)

    def _emit_initialize(self, _=None):
        if not self._initialized:
            self._initialized = True
            self._vispy_canvas.events.initialize()

    def _vispy_set_current(self):
        # Make this the current context
        glut.glutSetWindow(self._id)

    def _vispy_swap_buffers(self):
        # Swap front and back buffer
        glut.glutSetWindow(self._id)
        glut.glutSwapBuffers()

    def _vispy_set_title(self, title):
        # Set the window title. Has no effect for widgets
        glut.glutSetWindow(self._id)
        glut.glutSetWindowTitle(title)

    def _vispy_set_size(self, w, h):
        # Set size of the widget or window
        glut.glutSetWindow(self._id)
        glut.glutReshapeWindow(w, h)

    def _vispy_set_position(self, x, y):
        # Set position of the widget or window. May have no effect for widgets
        glut.glutSetWindow(self._id)
        glut.glutPositionWindow(x, y)

    def _vispy_set_visible(self, visible):
        # Show or hide the window or widget
        glut.glutSetWindow(self._id)
        if visible:
            glut.glutShowWindow()
        else:
            glut.glutHideWindow()

    def _vispy_update(self):
        # Invoke a redraw
        glut.glutSetWindow(self._id)
        glut.glutPostRedisplay()

    def _vispy_close(self):
        # Force the window or widget to shut down
        glut.glutDestroyWindow(self._id)

    def _vispy_get_geometry(self):
        # Should return widget (x, y, w, h)
        glut.glutSetWindow(self._id)
        x = glut.glutGet(glut.GLUT_WINDOW_X)
        y = glut.glutGet(glut.GLUT_WINDOW_Y)
        w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        return x, y, w, h

    def _vispy_get_size(self):
        glut.glutSetWindow(self._id)
        w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        return w, h

    def _vispy_get_position(self):
        glut.glutSetWindow(self._id)
        x = glut.glutGet(glut.GLUT_WINDOW_X)
        y = glut.glutGet(glut.GLUT_WINDOW_Y)
        return x, y

    def on_resize(self, w, h):
        if self._vispy_canvas is None:
            return
        self._vispy_canvas.events.resize(size=(w, h))

    def on_close(self):
        if self._vispy_canvas is None:
            return
        self._vispy_canvas.events.close()

    def on_draw(self, dummy=None):
        if self._vispy_canvas is None:
            return
        if not self._initialized:
            # The timer that we set may not have fired just yet
            self._emit_initialize()

        #w = glut.glutGet(glut.GLUT_WINDOW_WIDTH)
        #h = glut.glutGet(glut.GLUT_WINDOW_HEIGHT)
        self._vispy_canvas.events.paint(region=None)  # (0, 0, w, h))

    def on_mouse_action(self, button, state, x, y):
        if self._vispy_canvas is None:
            return
        action = {glut.GLUT_UP: 'release', glut.GLUT_DOWN: 'press'}[state]
        mod = self._modifiers(False)

        if button < 3:
            # Mouse click event
            button = BUTTONMAP.get(button, 0)
            if action == 'press':
                self._vispy_mouse_press(
                    pos=(
                        x,
                        y),
                    button=button,
                    modifiers=mod)
            else:
                self._vispy_mouse_release(
                    pos=(
                        x,
                        y),
                    button=button,
                    modifiers=mod)

        elif button in (3, 4):
            # Wheel event
            deltay = 1.0 if button == 3 else -1.0
            self._vispy_canvas.events.mouse_wheel(
                pos=(x, y),
                delta=(0.0, deltay),
                modifiers=mod,
            )

    def on_mouse_motion(self, x, y):
        if self._vispy_canvas is None:
            return
        self._vispy_mouse_move(
            pos=(x, y),
            modifiers=self._modifiers(False),
        )

    def on_key_press(self, key, x, y):
        key, text = self._process_key(key)
        self._vispy_canvas.events.key_press(
            key=key,
            text=text,
            modifiers=self._modifiers(),
        )

    def on_key_release(self, key, x, y):
        key, text = self._process_key(key)
        self._vispy_canvas.events.key_release(
            key=key,
            text=text,
            modifiers=self._modifiers()
        )

    def _process_key(self, key):
        if key in KEYMAP:
            if isinstance(key, int):
                return KEYMAP[key], ''
            else:
                return KEYMAP[key], key
        elif isinstance(key, int):
            return None, ''  # unsupported special char
        else:
            return keys.Key(key.upper()), key

    def _modifiers(self, query_now=True):
        if query_now:
            glutmod = glut.glutGetModifiers()
            mod = ()
            if glut.GLUT_ACTIVE_SHIFT & glutmod:
                mod += keys.SHIFT,
            if glut.GLUT_ACTIVE_CTRL & glutmod:
                mod += keys.CONTROL,
            if glut.GLUT_ACTIVE_ALT & glutmod:
                mod += keys.ALT,
            self._modifiers_cache = mod
        return self._modifiers_cache

#import weakref

def _glut_callback(id):
    # Get weakref wrapper for timer
    timer = TimerBackend._timers.get(id, None)
    if timer is None:
        return
    # Get timer object
    timer = timer()
    if timer is None:
        return
    # Kick it!
    if timer._vispy_timer._running:
        timer._vispy_timer._timeout()
        ms = int(timer._vispy_timer._interval * 1000)
        glut.glutTimerFunc(ms, _glut_callback, timer._id)

# class TimerBackend(BaseTimerBackend):
    #_counter = 0
    #_timers = {}

    # def __init__(self, vispy_timer):
        #BaseTimerBackend.__init__(self, vispy_timer)
        # Give this timer a unique id
        #TimerBackend._counter += 1
        #self._id = TimerBackend._counter
        # Store this timer (using a weak ref)
        #self._timers[self._id] = weakref.ref(self)

    #@classmethod
    # def _glut_callback(cls, id):
        # Get weakref wrapper for timer
        #timer = cls._timers.get(id, None)
        # if timer is None:
            # return
        # Get timer object
        #timer = timer()
        # if timer is None:
            # return
        # Kick it!
        # if timer._vispy_timer._running:
            # timer._vispy_timer._timeout()
            #ms = int(timer._vispy_timer._interval*1000)
            #glut.glutTimerFunc(ms, TimerBackend._glut_callback, timer._id)

    # def _vispy_start(self, interval):
        ##glut.glutTimerFunc(int(interval*1000), TimerBackend._glut_callback,
        ##                   self._id)
        #glut.glutTimerFunc(int(interval*1000), _glut_callback, self._id)

    # def _vispy_stop(self):
        # pass

    # def _vispy_get_native_timer(self):
        # return glut # or self?

# Note: we could also build a timer using glutTimerFunc, but this causes
# trouble because timer callbacks appear to take precedence over all others.
# Thus, a fast timer can block new display events.
class TimerBackend(BaseTimerBackend):
    _initialized = False
    _schedule = []

    def __init__(self, vispy_timer):
        BaseTimerBackend.__init__(self, vispy_timer)
        self._init_class()

    @classmethod
    def _init_class(cls):
        if cls._initialized:
            return
        glut.glutIdleFunc(cls._idle_callback)
        cls._initialized = True

    @classmethod
    def _idle_callback(cls):
        now = ptime.time()
        new_schedule = []

        # see whether there are any timers ready
        while len(cls._schedule) > 0 and cls._schedule[0][0] <= now:
            timer = cls._schedule.pop(0)[1]
            timer._vispy_timer._timeout()
            if timer._vispy_timer.running:
                new_schedule.append((now + timer._vispy_timer.interval, timer))

        # schedule next round of timeouts
        if len(new_schedule) > 0:
            cls._schedule.extend(new_schedule)
            cls._schedule.sort()

    def _vispy_start(self, interval):
        now = ptime.time()
        self._schedule.append((now + interval, self))

    def _vispy_stop(self):
        pass

    def _vispy_get_native_timer(self):
        return None  # glut has no native timer objects.
<|endoftext|>


<commit_before># Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from cliff import show
from vitrageclient.common import utils

# noinspection PyAbstractClass
class RcaShow(show.ShowOne):
    """Show an RCA"""

    def get_parser(self, prog_name):
        parser = super(RcaShow, self).get_parser(prog_name)
        parser.add_argument('alarm_id',
                            help='ID of an alarm')

        parser.add_argument('--all-tenants',
                            default=False,
                            dest='all_tenants',
                            action='store_true',
                            help='Shows alarms of all the tenants for the RCA')

        return parser

    def formatter_default(self):
        return 'json'

    def take_action(self, parsed_args):
        alarm_id = parsed_args.alarm_id
        all_tenants = parsed_args.all_tenants

        alarm = utils.get_client(self).rca.get(alarm_id=alarm_id,
                                               all_tenants=all_tenants)

        return self.dict2columns(alarm)
<commit_msg>change alarm_id in the rca api to alarm_vitrage_id<commit_after># Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from cliff import show
from vitrageclient.common import utils

# noinspection PyAbstractClass
class RcaShow(show.ShowOne):
    """Show an RCA"""

    def get_parser(self, prog_name):
        parser = super(RcaShow, self).get_parser(prog_name)
        parser.add_argument('alarm_vitrage_id',
                            help='ID of an alarm')

        parser.add_argument('--all-tenants',
                            default=False,
                            dest='all_tenants',
                            action='store_true',
                            help='Shows alarms of all the tenants for the RCA')

        return parser

    def formatter_default(self):
        return 'json'

    def take_action(self, parsed_args):
        alarm_id = parsed_args.alarm_vitrage_id
        all_tenants = parsed_args.all_tenants

        alarm = utils.get_client(self).rca.get(alarm_id=alarm_id,
                                               all_tenants=all_tenants)

        return self.dict2columns(alarm)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

'''Reads a wall of the specified user using VKontakte API.'''

import json
import datetime
import logging
import re
import urllib

import vkfeed.util
from vkfeed import constants
from vkfeed.core import Error

LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)

_TEXT_URL_RE = re.compile(r'(^|\s|>)(https?://[^"]+?)(\.?(?:<|\s|$))')
'''Matches a URL in a plain text.'''

_DOMAIN_ONLY_TEXT_URL_RE = re.compile(r'(^|\s|>)((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+[a-z0-9](?:[-a-z0-9]*[a-z0-9])/[^"]+?)(\.?(?:<|\s|$))')
'''Matches a URL without protocol specification in a plain text.'''

_USER_LINK_RE = re.compile(r'\[((?:id|club)\d+)\|([^\]]+)\]')
'''Matches a user link in a post text.'''

_GROUP_ALIAS_RE = re.compile(r'^(?:event|public)(\d+)$')
'''Matches group ID aliases.'''

_HASH_TAG_RE = re.compile(ur'#[a-zA-Zа-яА-Я0-9\-_]+')
'''Matches a hash tag.'''

class ConnectionError(Error):
    '''Raised when we fail to get a data from the server.'''

    def __init__(self, *args, **kwargs):
        Error.__init__(self, *args, **kwargs)

class ServerError(Error):
    '''Raised when the server reports an error.'''

    def __init__(self, code, *args, **kwargs):
        Error.__init__(self, *args, **kwargs)
        self.code = code

def read(profile_name, foreign_posts, show_photo, hash_tag_title, text_title):
    '''Reads a wall of the specified user.'''

    user = _get_user(profile_name)
    reply = _api('wall.get', owner_id = user['id'], extended = 1)

    users = {}

    for profile in reply.get('profiles', []):
        users[profile['uid']] = {
            'name':  profile['first_name'] + ' ' + profile['last_name'],
            'photo': profile['photo'],
        }

    for profile in reply.get('groups', []):
        users[-profile['gid']] = {
            'name':  profile['name'],
            'photo': profile['photo'],
        }

    img_style = 'style="border-style: none; display: block;"'

    posts = []
    for post in reply['wall'][1:]:
        if not foreign_posts and post['from_id'] != user['id']:
            LOG.debug(u'Ignore post %s from user %s.', post['id'], post['from_id'])
            continue

        supported = []
        unsupported = []

        title = _get_post_title(users, post, text_title, hash_tag_title)

        if 'attachment' in post and post['text'] == post['attachment'][post['attachment']['type']].get('title'):
            post['text'] = ''

        for attachment in post.get('attachments', []):
            info = attachment[attachment['type']]

            if attachment['type'] == 'app':
                supported.append(
                    u''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style))
            elif attachment['type'] == 'graffiti':
                supported.append(
                    u''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style))
            elif attachment['type'] == 'link':
                info['description'] = _parse_text(info['description']) or info['title']

                html = u'Ссылка: {info[title]}
'.format(info = info)

                if info.get('image_src') and info['description']:
                    html += (
                        u'

'
                            '	'
                            '{info[description]}	'
                        '

'.format(info = info, img_style = img_style))
                elif info.get('image_src'):
                    html += u''.format(
                        info = info, img_style = img_style)
                elif info['description']:
                    html += info['description']

                html += '
'

                supported.append(html)
            elif attachment['type'] in ('photo', 'posted_photo'):
                photo_id = info.get('pid', info.get('id', 0))
                photo_count = reduce(
                    lambda count, attachment:
                        count + ( attachment['type'] in ('photo', 'posted_photo') ),
                    post['attachments'], 0)
                photo_src = info['src_big'] if photo_count == 1 else info['src']

                # Photo may have id = 0 and owner_id = 0 if it for example
                # generated by an application.
                if photo_id == 0 or info['owner_id'] == 0:
                    supported.append(
                        u''.format(
                            vk_url = constants.VK_URL, profile_id = user['id'], post_id = post['id'],
                            img_style = img_style, photo_src = photo_src))
                else:
                    supported.append(
                        u''
                        ''.format(
                            vk_url = constants.VK_URL, profile_id = user['id'], photo_id = photo_id,
                            info = info, post_id = post['id'], img_style = img_style, photo_src = photo_src))
            elif attachment['type'] == 'video':
                supported.append(
                    u''
                        ''
                        '{info[title]} ({duration})'
                    ''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style,
                        duration = _get_duration(info['duration'])))

            elif attachment['type'] == 'audio':
                unsupported.append(u'Аудиозапись: {title}'.format(
                    vk_url = constants.VK_URL, query = urllib.urlencode({
                        'c[q]': (info['performer'] + ' - ' + info['title']).encode('utf-8'),
                        'c[section]': 'audio'
                    }), title = u'{} - {} ({})'.format(info['performer'], info['title'], _get_duration(info['duration']))))
            elif attachment['type'] == 'doc':
                unsupported.append(u'Документ: {}'.format(info['title']))
            elif attachment['type'] == 'note':
                unsupported.append(u'Заметка: {}'.format(info['title']))
            elif attachment['type'] == 'page':
                unsupported.append(u'Страница: {}'.format(info['title']))
            elif attachment['type'] == 'poll':
                unsupported.append(u'Опрос: {}'.format(info['question']))

        text = ''

        if supported:
            text += '
' + '

'.join(supported) + '
'

        text += _parse_text(post['text'])

        if unsupported:
            text += '
' + '

'.join(unsupported) + '
'

        if 'copy_owner_id' in post and 'copy_post_id' in post:
            text = u'
{user_name} пишет:
'.format(
                profile_url = _get_profile_url(post['copy_owner_id']), user_name = users[post['copy_owner_id']]['name']) + text

            if 'copy_text' in post:
                text = u'
{}

{}
'.format(post['copy_text'], text)

        if 'reply_owner_id' in post and 'reply_post_id' in post:
            text += (
                u'
'
                    u'В ответ на запись '
                    u'пользователя {user_name}.'
                '
'.format(vk_url = constants.VK_URL, post = post,
                    profile_url = _get_profile_url(post['reply_owner_id']), user_name = users[post['reply_owner_id']]['name']))

        if show_photo:
            text = (
                u'

'
                    '	'
                    '{text}	'
                '

'.format(
                    url = _get_profile_url(post['from_id']), img_style = img_style,
                    photo = users[post['from_id']]['photo'], text = text))

        date = (
            datetime.datetime.fromtimestamp(post['date'])
            # Take MSK timezone into account
            + datetime.timedelta(hours = 4))

        posts.append({
            'title': title,
            'url':   u'{0}wall{1}_{2}'.format(constants.VK_URL, user['id'], post['id']),
            'text':  text,
            'date':  date,
        })

    return {
        'url':        constants.VK_URL + profile_name,
        'user_name':  user['name'],
        'user_photo': user['photo'],
        'posts':      posts,
    }

def _api(method, **kwargs):
    '''Calls the specified VKontakte API method.'''

    url = u'{0}method/{1}?language=0&'.format(constants.API_URL, method) + urllib.urlencode(kwargs)

    try:
        data = vkfeed.util.fetch_url(url, content_type = 'application/json')

        try:
            data = json.loads(data)
        except Exception as e:
            raise Error(u'Failed to parse JSON data: {0}.', e)
    except Exception as e:
        raise ConnectionError(u'API call {0} failed: {1}', url, e)

    if 'error' in data or 'response' not in data:
        error = data.get('error', {}).get('error_msg', '').strip()

        if not error:
            error = u'Ошибка вызова API.'
        elif error == 'Access denied: group is blocked':
            error = (
                u'Страница временно заблокирована и проверяется администраторами, '
                u'так как некоторые пользователи считают, что она не соответствует правилам сайта.')
        elif error == 'Access denied: this wall available only for community members':
            error = u'Это частное сообщество. Доступ только по приглашениям администраторов.'
        elif error == 'User was deleted or banned':
            error = u'Пользователь удален или забанен.'
        elif not error.endswith('.'):
            error += '.'

        raise ServerError(data.get('error', {}).get('error_code'), error)

    return data['response']

def _get_duration(seconds):
    '''Returns audio/video duration string.'''

    hours = seconds / 60 / 60
    minutes = seconds / 60 % 60
    seconds = seconds % 60

    if hours:
        return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
    else:
        return '{:02d}:{:02d}'.format(minutes, seconds)

def _get_post_title(users, post, text_title, hash_tag_title):
    '''Formats title for a post.'''

    title = users[post['from_id']]['name']

    if text_title:
        text = post['text'].lstrip('.?!').strip()
        if text:
            title = _USER_LINK_RE.sub(r'\2', text)
            limit_pos = len(title)

            pos = title.find('.')
            if pos != -1:
                limit_pos = min(limit_pos, pos)
                if title[limit_pos : limit_pos + 3] == '...':
                    limit_pos += 3

            pos = title.find('?')
            if pos != -1:
                limit_pos = min(limit_pos, pos + 1)

            pos = title.find('!')
            if pos != -1:
                limit_pos = min(limit_pos, pos + 1)

            title = title[:limit_pos]
    elif hash_tag_title:
        hash_tags = _HASH_TAG_RE.findall(post['text'])
        if hash_tags:
            title = ', '.join(
                tag[1:].lower() if id else tag[1:].title()
                for id, tag in enumerate(hash_tags))

    return title

def _get_profile_url(profile_id):
    '''Returns URL to profile with the specified ID.'''

    return constants.VK_URL + ( 'club' if profile_id < 0 else 'id' ) + str(abs(profile_id))

def _get_user(profile_name):
    '''Returns user info by profile name.'''

    try:
        profile = _api('users.get', uid = profile_name, fields = 'photo_big,photo_medium,photo')[0]
        user = {
            'id':   profile['uid'],
            'name': profile['first_name'] + ' ' + profile['last_name'],
        }
    except ServerError as e:
        # Invalid user ID
        if e.code == 113:
            try:
                # VKontakte API doesn't understand group ID aliases
                match = _GROUP_ALIAS_RE.match(profile_name)
                if match is not None:
                    profile_name = 'club' + match.group(1)

                profile = _api('groups.getById', gid = profile_name, fields = 'photo_big,photo_medium,photo')[0]
                user = {
                    'id':    -profile['gid'],
                    'name':  profile['name'],
                }
            except ServerError as e:
                # Invalid group ID
                if e.code == 125:
                    raise ServerError(113, u'Пользователя не существует.')
                else:
                    raise e
        else:
            raise e

    if 'photo_big' in profile:
        user['photo'] = profile['photo_big']
    elif 'photo_medium' in profile:
        user['photo'] = profile['photo_medium']
    else:
        user['photo'] = profile['photo']

    return user

def _parse_text(text):
    '''Parses a post text.'''

    text = _TEXT_URL_RE.sub(r'\1\2\3', text)
    text = _DOMAIN_ONLY_TEXT_URL_RE.sub(r'\1\2\3', text)
    text = _USER_LINK_RE.sub(r'\2'.format(constants.VK_URL), text)

    return text

<commit_msg>Added a limit on post number to save bandwidth<commit_after># -*- coding: utf-8 -*-

'''Reads a wall of the specified user using VKontakte API.'''

import json
import datetime
import logging
import re
import urllib

import vkfeed.util
from vkfeed import constants
from vkfeed.core import Error

LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)

_TEXT_URL_RE = re.compile(r'(^|\s|>)(https?://[^"]+?)(\.?(?:<|\s|$))')
'''Matches a URL in a plain text.'''

_DOMAIN_ONLY_TEXT_URL_RE = re.compile(r'(^|\s|>)((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+[a-z0-9](?:[-a-z0-9]*[a-z0-9])/[^"]+?)(\.?(?:<|\s|$))')
'''Matches a URL without protocol specification in a plain text.'''

_USER_LINK_RE = re.compile(r'\[((?:id|club)\d+)\|([^\]]+)\]')
'''Matches a user link in a post text.'''

_GROUP_ALIAS_RE = re.compile(r'^(?:event|public)(\d+)$')
'''Matches group ID aliases.'''

_HASH_TAG_RE = re.compile(ur'#[a-zA-Zа-яА-Я0-9\-_]+')
'''Matches a hash tag.'''

_MAX_POSTS_NUM = 10
'''Maximum number of posts in feed.'''

class ConnectionError(Error):
    '''Raised when we fail to get a data from the server.'''

    def __init__(self, *args, **kwargs):
        Error.__init__(self, *args, **kwargs)

class ServerError(Error):
    '''Raised when the server reports an error.'''

    def __init__(self, code, *args, **kwargs):
        Error.__init__(self, *args, **kwargs)
        self.code = code

def read(profile_name, foreign_posts, show_photo, hash_tag_title, text_title):
    '''Reads a wall of the specified user.'''

    user = _get_user(profile_name)
    reply = _api('wall.get', owner_id = user['id'], extended = 1)

    users = {}

    for profile in reply.get('profiles', []):
        users[profile['uid']] = {
            'name':  profile['first_name'] + ' ' + profile['last_name'],
            'photo': profile['photo'],
        }

    for profile in reply.get('groups', []):
        users[-profile['gid']] = {
            'name':  profile['name'],
            'photo': profile['photo'],
        }

    img_style = 'style="border-style: none; display: block;"'

    posts = []
    for post in reply['wall'][1:]:
        if len(posts) >= _MAX_POSTS_NUM:
            break

        if not foreign_posts and post['from_id'] != user['id']:
            LOG.debug(u'Ignore post %s from user %s.', post['id'], post['from_id'])
            continue

        supported = []
        unsupported = []

        title = _get_post_title(users, post, text_title, hash_tag_title)

        if 'attachment' in post and post['text'] == post['attachment'][post['attachment']['type']].get('title'):
            post['text'] = ''

        for attachment in post.get('attachments', []):
            info = attachment[attachment['type']]

            if attachment['type'] == 'app':
                supported.append(
                    u''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style))
            elif attachment['type'] == 'graffiti':
                supported.append(
                    u''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style))
            elif attachment['type'] == 'link':
                info['description'] = _parse_text(info['description']) or info['title']

                html = u'Ссылка: {info[title]}
'.format(info = info)

                if info.get('image_src') and info['description']:
                    html += (
                        u'

'
                            '	'
                            '{info[description]}	'
                        '

'.format(info = info, img_style = img_style))
                elif info.get('image_src'):
                    html += u''.format(
                        info = info, img_style = img_style)
                elif info['description']:
                    html += info['description']

                html += '
'

                supported.append(html)
            elif attachment['type'] in ('photo', 'posted_photo'):
                photo_id = info.get('pid', info.get('id', 0))
                photo_count = reduce(
                    lambda count, attachment:
                        count + ( attachment['type'] in ('photo', 'posted_photo') ),
                    post['attachments'], 0)
                photo_src = info['src_big'] if photo_count == 1 else info['src']

                # Photo may have id = 0 and owner_id = 0 if it for example
                # generated by an application.
                if photo_id == 0 or info['owner_id'] == 0:
                    supported.append(
                        u''.format(
                            vk_url = constants.VK_URL, profile_id = user['id'], post_id = post['id'],
                            img_style = img_style, photo_src = photo_src))
                else:
                    supported.append(
                        u''
                        ''.format(
                            vk_url = constants.VK_URL, profile_id = user['id'], photo_id = photo_id,
                            info = info, post_id = post['id'], img_style = img_style, photo_src = photo_src))
            elif attachment['type'] == 'video':
                supported.append(
                    u''
                        ''
                        '{info[title]} ({duration})'
                    ''.format(
                        vk_url = constants.VK_URL, info = info, img_style = img_style,
                        duration = _get_duration(info['duration'])))

            elif attachment['type'] == 'audio':
                unsupported.append(u'Аудиозапись: {title}'.format(
                    vk_url = constants.VK_URL, query = urllib.urlencode({
                        'c[q]': (info['performer'] + ' - ' + info['title']).encode('utf-8'),
                        'c[section]': 'audio'
                    }), title = u'{} - {} ({})'.format(info['performer'], info['title'], _get_duration(info['duration']))))
            elif attachment['type'] == 'doc':
                unsupported.append(u'Документ: {}'.format(info['title']))
            elif attachment['type'] == 'note':
                unsupported.append(u'Заметка: {}'.format(info['title']))
            elif attachment['type'] == 'page':
                unsupported.append(u'Страница: {}'.format(info['title']))
            elif attachment['type'] == 'poll':
                unsupported.append(u'Опрос: {}'.format(info['question']))

        text = ''

        if supported:
            text += '
' + '

'.join(supported) + '
'

        text += _parse_text(post['text'])

        if unsupported:
            text += '
' + '

'.join(unsupported) + '
'

        if 'copy_owner_id' in post and 'copy_post_id' in post:
            text = u'
{user_name} пишет:
'.format(
                profile_url = _get_profile_url(post['copy_owner_id']), user_name = users[post['copy_owner_id']]['name']) + text

            if 'copy_text' in post:
                text = u'
{}

{}
'.format(post['copy_text'], text)

        if 'reply_owner_id' in post and 'reply_post_id' in post:
            text += (
                u'
'
                    u'В ответ на запись '
                    u'пользователя {user_name}.'
                '
'.format(vk_url = constants.VK_URL, post = post,
                    profile_url = _get_profile_url(post['reply_owner_id']), user_name = users[post['reply_owner_id']]['name']))

        if show_photo:
            text = (
                u'

'
                    '	'
                    '{text}	'
                '

'.format(
                    url = _get_profile_url(post['from_id']), img_style = img_style,
                    photo = users[post['from_id']]['photo'], text = text))

        date = (
            datetime.datetime.fromtimestamp(post['date'])
            # Take MSK timezone into account
            + datetime.timedelta(hours = 4))

        posts.append({
            'title': title,
            'url':   u'{0}wall{1}_{2}'.format(constants.VK_URL, user['id'], post['id']),
            'text':  text,
            'date':  date,
        })

    return {
        'url':        constants.VK_URL + profile_name,
        'user_name':  user['name'],
        'user_photo': user['photo'],
        'posts':      posts,
    }

def _api(method, **kwargs):
    '''Calls the specified VKontakte API method.'''

    url = u'{0}method/{1}?language=0&'.format(constants.API_URL, method) + urllib.urlencode(kwargs)

    try:
        data = vkfeed.util.fetch_url(url, content_type = 'application/json')

        try:
            data = json.loads(data)
        except Exception as e:
            raise Error(u'Failed to parse JSON data: {0}.', e)
    except Exception as e:
        raise ConnectionError(u'API call {0} failed: {1}', url, e)

    if 'error' in data or 'response' not in data:
        error = data.get('error', {}).get('error_msg', '').strip()

        if not error:
            error = u'Ошибка вызова API.'
        elif error == 'Access denied: group is blocked':
            error = (
                u'Страница временно заблокирована и проверяется администраторами, '
                u'так как некоторые пользователи считают, что она не соответствует правилам сайта.')
        elif error == 'Access denied: this wall available only for community members':
            error = u'Это частное сообщество. Доступ только по приглашениям администраторов.'
        elif error == 'User was deleted or banned':
            error = u'Пользователь удален или забанен.'
        elif not error.endswith('.'):
            error += '.'

        raise ServerError(data.get('error', {}).get('error_code'), error)

    return data['response']

def _get_duration(seconds):
    '''Returns audio/video duration string.'''

    hours = seconds / 60 / 60
    minutes = seconds / 60 % 60
    seconds = seconds % 60

    if hours:
        return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
    else:
        return '{:02d}:{:02d}'.format(minutes, seconds)

def _get_post_title(users, post, text_title, hash_tag_title):
    '''Formats title for a post.'''

    title = users[post['from_id']]['name']

    if text_title:
        text = post['text'].lstrip('.?!').strip()
        if text:
            title = _USER_LINK_RE.sub(r'\2', text)
            limit_pos = len(title)

            pos = title.find('.')
            if pos != -1:
                limit_pos = min(limit_pos, pos)
                if title[limit_pos : limit_pos + 3] == '...':
                    limit_pos += 3

            pos = title.find('?')
            if pos != -1:
                limit_pos = min(limit_pos, pos + 1)

            pos = title.find('!')
            if pos != -1:
                limit_pos = min(limit_pos, pos + 1)

            title = title[:limit_pos]
    elif hash_tag_title:
        hash_tags = _HASH_TAG_RE.findall(post['text'])
        if hash_tags:
            title = ', '.join(
                tag[1:].lower() if id else tag[1:].title()
                for id, tag in enumerate(hash_tags))

    return title

def _get_profile_url(profile_id):
    '''Returns URL to profile with the specified ID.'''

    return constants.VK_URL + ( 'club' if profile_id < 0 else 'id' ) + str(abs(profile_id))

def _get_user(profile_name):
    '''Returns user info by profile name.'''

    try:
        profile = _api('users.get', uid = profile_name, fields = 'photo_big,photo_medium,photo')[0]
        user = {
            'id':   profile['uid'],
            'name': profile['first_name'] + ' ' + profile['last_name'],
        }
    except ServerError as e:
        # Invalid user ID
        if e.code == 113:
            try:
                # VKontakte API doesn't understand group ID aliases
                match = _GROUP_ALIAS_RE.match(profile_name)
                if match is not None:
                    profile_name = 'club' + match.group(1)

                profile = _api('groups.getById', gid = profile_name, fields = 'photo_big,photo_medium,photo')[0]
                user = {
                    'id':    -profile['gid'],
                    'name':  profile['name'],
                }
            except ServerError as e:
                # Invalid group ID
                if e.code == 125:
                    raise ServerError(113, u'Пользователя не существует.')
                else:
                    raise e
        else:
            raise e

    if 'photo_big' in profile:
        user['photo'] = profile['photo_big']
    elif 'photo_medium' in profile:
        user['photo'] = profile['photo_medium']
    else:
        user['photo'] = profile['photo']

    return user

def _parse_text(text):
    '''Parses a post text.'''

    text = _TEXT_URL_RE.sub(r'\1\2\3', text)
    text = _DOMAIN_ONLY_TEXT_URL_RE.sub(r'\1\2\3', text)
    text = _USER_LINK_RE.sub(r'\2'.format(constants.VK_URL), text)

    return text

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

"""
"""

import os
import datetime

from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

from . import mainwindow_ui
from . import audiorecorder
from . import recordsmanager
from . import helperutils

from . import __app_name__
from . import __version__

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.__ui = mainwindow_ui.Ui_MainWindow()
        self.__ui.setupUi(self)

        self.setWindowTitle(f'{__app_name__} - {__version__}')
        self.ui.labelRecordDuration.setVisible(False)

        self.ui.tableRecords.horizontalHeader().setFixedHeight(37)

        self.ui.tableRecords.horizontalHeader().setSectionResizeMode(
            0, QtWidgets.QHeaderView.Stretch)
        self.ui.tableRecords.horizontalHeader().setSectionResizeMode(
            1, QtWidgets.QHeaderView.ResizeToContents)

        settings_fname = os.path.normpath(
            os.path.join(helperutils.get_app_config_dir(), __app_name__+'.ini'))

        self.__settings = QtCore.QSettings(
            settings_fname, QtCore.QSettings.IniFormat, self)
        self.__settings_group = helperutils.qsettings_group(self.__settings)

        self.__audio_recorder = audiorecorder.AudioRecorder(parent=self)
        self.__records_manager = recordsmanager.RecordsManager(parent=self)

        self.ui.pbRecordingStartAndStop.toggled.connect(self.__on_start_stop)
        self.ui.pbRecordingPause.toggled.connect(self.__on_pause)
        self.ui.pbRemoveRecords.clicked.connect(self.__remove_selected_records)

        self.ui.tableRecords.cellDoubleClicked.connect(self.__on_play_record)
        self.ui.tableRecords.itemSelectionChanged.connect(
            self.__on_change_selected_records)
        self.ui.tableRecords.installEventFilter(self)

        self.__audio_recorder.record_updated.connect(
            self.__on_update_duration_time)

        self.__read_settings()
        self.__update_records_info()

    def closeEvent(self, event):
        self.__write_settings()

    def eventFilter(self, obj, event: QtGui.QKeyEvent):
        if obj is not self.ui.tableRecords:
            return False
        if event.type() != QtCore.QEvent.KeyPress:
            return False
        if event.key() != QtCore.Qt.Key_Delete:
            return False

        self.__remove_selected_records()
        return True

    @property
    def ui(self):
        return self.__ui

    def __on_start_stop(self, is_checked):
        pb_title_text = {
            True: self.tr('Stop'),
            False: self.tr('Record'),
        }

        self.ui.pbRecordingStartAndStop.setText(pb_title_text[is_checked])
        self.ui.labelRecordDuration.setVisible(is_checked)

        if is_checked:
            self.__start_recording()
        else:
            self.__stop_recording()

    def __on_pause(self, is_checked):
        if is_checked:
            self.__pause_recording()
        else:
            self.__start_recording()

    def __on_update_duration_time(self):
        duration_delta = datetime.timedelta(
            seconds=int(self.__audio_recorder.duration))
        self.ui.labelRecordDuration.setText(str(duration_delta))

    def __on_play_record(self, index):
        record_info = self.ui.tableRecords.item(index, 0).data(
            QtCore.Qt.UserRole)

        record_url = QtCore.QUrl(record_info.filename.replace('\\', '/'))
        QtGui.QDesktopServices.openUrl(record_url)

    def __on_change_selected_records(self):
        is_selected = len(self.ui.tableRecords.selectedItems()) > 0
        self.ui.pbRemoveRecords.setEnabled(is_selected)

    def __start_recording(self):
        self.__audio_recorder.record()

    def __pause_recording(self):
        self.__audio_recorder.stop()

    def __stop_recording(self):
        self.__audio_recorder.stop()
        self.__save_record()
        self.__audio_recorder.clear()
        self.__on_update_duration_time()

    def __save_record(self):
        record_info = self.__records_manager.save_record(
            self.__audio_recorder.get_record())

        self.__add_record_info_to_table(0, record_info)

    def __read_settings(self):
        with self.__settings_group('UI'):
            self.restoreGeometry(self.__settings.value(
                'WindowGeometry', self.saveGeometry()))
            self.restoreState(self.__settings.value(
                'WindowState', self.saveState()))

        self.__records_manager.read_settings(self.__settings)

    def __write_settings(self):
        with self.__settings_group('UI'):
            self.__settings.setValue('WindowGeometry', self.saveGeometry())
            self.__settings.setValue('WindowState', self.saveState())

        self.__records_manager.write_settings(self.__settings)

    def __add_record_info_to_table(self, index, record_info):
        self.ui.tableRecords.insertRow(index)

        date_item = QtWidgets.QTableWidgetItem(str(record_info.date))
        date_item.setData(QtCore.Qt.UserRole, record_info)

        dur_item = QtWidgets.QTableWidgetItem(str(record_info.duration))
        dur_item.setTextAlignment(QtCore.Qt.AlignCenter)

        self.ui.tableRecords.setItem(index, 0, date_item)
        self.ui.tableRecords.setItem(index, 1, dur_item)

        self.ui.tableRecords.verticalHeader().setSectionResizeMode(
            index, QtWidgets.QHeaderView.ResizeToContents)

    def __update_records_info(self):
        records_info = self.__records_manager.get_records_info()
        self.ui.tableRecords.clearContents()
        self.ui.tableRecords.setRowCount(0)

        for i, record_info in enumerate(records_info):
            self.__add_record_info_to_table(i, record_info)

    def __remove_selected_records(self):
        selected_items = self.ui.tableRecords.selectedItems()

        if not selected_items:
            return False

        records_for_remove = [
            (self.ui.tableRecords.row(item), item.data(QtCore.Qt.UserRole))
            for item in selected_items if item.data(QtCore.Qt.UserRole)]

        for row, record_info in records_for_remove:
            self.__records_manager.remove_record(record_info)
            self.ui.tableRecords.removeRow(row)
<commit_msg>Refactoring<commit_after># -*- coding: utf-8 -*-

"""
"""

import os
import datetime

from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

from . import mainwindow_ui
from . import audiorecorder
from . import recordsmanager
from . import helperutils

from . import __app_name__
from . import __version__

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.__ui = mainwindow_ui.Ui_MainWindow()
        self.__ui.setupUi(self)

        self.setWindowTitle(f'{__app_name__} - {__version__}')
        self.ui.labelRecordDuration.setVisible(False)

        self.ui.tableRecords.horizontalHeader().setSectionResizeMode(
            0, QtWidgets.QHeaderView.Stretch)
        self.ui.tableRecords.horizontalHeader().setSectionResizeMode(
            1, QtWidgets.QHeaderView.ResizeToContents)
        self.ui.tableRecords.verticalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents)

        settings_fname = os.path.normpath(
            os.path.join(helperutils.get_app_config_dir(), __app_name__+'.ini'))

        self.__settings = QtCore.QSettings(
            settings_fname, QtCore.QSettings.IniFormat, self)
        self.__settings_group = helperutils.qsettings_group(self.__settings)

        self.__audio_recorder = audiorecorder.AudioRecorder(parent=self)
        self.__records_manager = recordsmanager.RecordsManager(parent=self)

        self.ui.pbRecordingStartAndStop.toggled.connect(self.__on_start_stop)
        self.ui.pbRecordingPause.toggled.connect(self.__on_pause)
        self.ui.pbRemoveRecords.clicked.connect(self.__remove_selected_records)

        self.ui.tableRecords.cellDoubleClicked.connect(self.__on_play_record)
        self.ui.tableRecords.itemSelectionChanged.connect(
            self.__on_change_selected_records)
        self.ui.tableRecords.installEventFilter(self)

        self.__audio_recorder.record_updated.connect(
            self.__on_update_duration_time)

        self.__read_settings()
        self.__update_records_info()

    def closeEvent(self, event):
        self.__write_settings()

    def eventFilter(self, obj, event: QtGui.QKeyEvent):
        if obj is not self.ui.tableRecords:
            return False
        if event.type() != QtCore.QEvent.KeyPress:
            return False
        if event.key() != QtCore.Qt.Key_Delete:
            return False

        self.__remove_selected_records()
        return True

    @property
    def ui(self):
        return self.__ui

    def __on_start_stop(self, is_checked):
        pb_title_text = {
            True: self.tr('Stop'),
            False: self.tr('Record'),
        }

        self.ui.pbRecordingStartAndStop.setText(pb_title_text[is_checked])
        self.ui.labelRecordDuration.setVisible(is_checked)

        if is_checked:
            self.__start_recording()
        else:
            self.__stop_recording()

    def __on_pause(self, is_checked):
        if is_checked:
            self.__pause_recording()
        else:
            self.__start_recording()

    def __on_update_duration_time(self):
        duration_delta = datetime.timedelta(
            seconds=int(self.__audio_recorder.duration))
        self.ui.labelRecordDuration.setText(str(duration_delta))

    def __on_play_record(self, index):
        record_info = self.ui.tableRecords.item(index, 0).data(
            QtCore.Qt.UserRole)

        record_url = QtCore.QUrl(record_info.filename.replace('\\', '/'))
        QtGui.QDesktopServices.openUrl(record_url)

    def __on_change_selected_records(self):
        is_selected = len(self.ui.tableRecords.selectedItems()) > 0
        self.ui.pbRemoveRecords.setEnabled(is_selected)

    def __start_recording(self):
        self.__audio_recorder.record()

    def __pause_recording(self):
        self.__audio_recorder.stop()

    def __stop_recording(self):
        self.__audio_recorder.stop()
        self.__save_record()
        self.__audio_recorder.clear()
        self.__on_update_duration_time()

    def __save_record(self):
        record_info = self.__records_manager.save_record(
            self.__audio_recorder.get_record())

        self.__add_record_info_to_table(0, record_info)

    def __read_settings(self):
        with self.__settings_group('UI'):
            self.restoreGeometry(self.__settings.value(
                'WindowGeometry', self.saveGeometry()))
            self.restoreState(self.__settings.value(
                'WindowState', self.saveState()))

        self.__records_manager.read_settings(self.__settings)

    def __write_settings(self):
        with self.__settings_group('UI'):
            self.__settings.setValue('WindowGeometry', self.saveGeometry())
            self.__settings.setValue('WindowState', self.saveState())

        self.__records_manager.write_settings(self.__settings)

    def __add_record_info_to_table(self, index, record_info):
        self.ui.tableRecords.insertRow(index)

        date_item = QtWidgets.QTableWidgetItem(str(record_info.date))
        date_item.setData(QtCore.Qt.UserRole, record_info)

        dur_item = QtWidgets.QTableWidgetItem(str(record_info.duration))
        dur_item.setTextAlignment(QtCore.Qt.AlignCenter)

        self.ui.tableRecords.setItem(index, 0, date_item)
        self.ui.tableRecords.setItem(index, 1, dur_item)

    def __update_records_info(self):
        records_info = self.__records_manager.get_records_info()
        self.ui.tableRecords.clearContents()
        self.ui.tableRecords.setRowCount(0)

        for i, record_info in enumerate(records_info):
            self.__add_record_info_to_table(i, record_info)

    def __remove_selected_records(self):
        selected_items = self.ui.tableRecords.selectedItems()

        if not selected_items:
            return False

        records_for_remove = [
            (self.ui.tableRecords.row(item), item.data(QtCore.Qt.UserRole))
            for item in selected_items if item.data(QtCore.Qt.UserRole)]

        for row, record_info in records_for_remove:
            self.__records_manager.remove_record(record_info)
            self.ui.tableRecords.removeRow(row)
<|endoftext|>


<commit_before>import urllib
import logging

from django.contrib.auth import authenticate, login
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponseForbidden

import requests

log = logging.getLogger(__name__)

def redirect_profile(request):
    '''
    The default destination from logging in, redirect to the actual profile URL
    '''
    if request.user.is_authenticated():
        return HttpResponseRedirect(reverse('wafer_user_profile',
                                            args=(request.user.username,)))
    else:
        return HttpResponseRedirect(reverse('wafer_page', args=('index',)))

def github_login(request):
    if 'code' not in request.GET:
        return HttpResponseRedirect(
            'https://github.com/login/oauth/authorize?' + urllib.urlencode({
                'client_id': settings.WAFER_GITHUB_CLIENT_ID,
                'redirect_uri': request.build_absolute_uri(
                    reverse(github_login)),
                'scope': 'user:email',
                'state': request.META['CSRF_COOKIE'],
            }))

    if request.GET['state'] != request.META['CSRF_COOKIE']:
        return HttpResponseForbidden('Incorrect state',
                                     content_type='text/plain')
    code = request.GET['code']

    r = requests.post('https://github.com/login/oauth/access_token', data={
        'client_id': settings.WAFER_GITHUB_CLIENT_ID,
        'client_secret': settings.WAFER_GITHUB_CLIENT_SECRET,
        'code': code,
    })
    if r.status_code != 200:
        return HttpResponseForbidden('Invalid code', content_type='text/plain')
    token = r.content

    r = requests.get('https://api.github.com/user?%s' % token)
    if r.status_code != 200:
        return HttpResponseForbidden('Unexpected response from github',
                content_type='text/plain')
    gh = r.json()

    email = gh.get('email', None)
    if not email:  # No public e-mail address
        r = requests.get('https://api.github.com/user/emails?%s' % token)
        if r.status_code != 200:
            return HttpResponseForbidden('Failed to obtain email address from'
                    ' github - unexpected response', content_type='text/plain')
        try:
            email = r.json()[0]['email']
        except (KeyError, IndexError) as e:
            log.debug('Error extracting github email address: %s', e)
            return HttpResponseForbidden('Failed to obtain email address from'
                    ' github - unexpected response', content_type='text/plain')

    try:
        user = authenticate(github_login=gh['login'], name=gh['name'], email=email,
                            blog=gh['blog'])
    except KeyError as e:
        log.debug('Error creating account from github information: %s', e)
        return HttpResponseForbidden('Unexpected response from github.'
                ' Authentication failed', content_type='text/plain')

    if not user:
        return HttpResponseForbidden('Authentication with github credentials'
                ' failed', content_type='text/plain')
    login(request, user)
    return HttpResponseRedirect(reverse(redirect_profile))
<commit_msg>Catch MultipleObjectedReturned in github authentication and turn it into a more sensible error for the user<commit_after>import urllib
import logging

from django.contrib.auth import authenticate, login
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponseForbidden

import requests

from wafer.users.models import UserProfile

log = logging.getLogger(__name__)

def redirect_profile(request):
    '''
    The default destination from logging in, redirect to the actual profile URL
    '''
    if request.user.is_authenticated():
        return HttpResponseRedirect(reverse('wafer_user_profile',
                                            args=(request.user.username,)))
    else:
        return HttpResponseRedirect(reverse('wafer_page', args=('index',)))

def github_login(request):
    if 'code' not in request.GET:
        return HttpResponseRedirect(
            'https://github.com/login/oauth/authorize?' + urllib.urlencode({
                'client_id': settings.WAFER_GITHUB_CLIENT_ID,
                'redirect_uri': request.build_absolute_uri(
                    reverse(github_login)),
                'scope': 'user:email',
                'state': request.META['CSRF_COOKIE'],
            }))

    if request.GET['state'] != request.META['CSRF_COOKIE']:
        return HttpResponseForbidden('Incorrect state',
                                     content_type='text/plain')
    code = request.GET['code']

    r = requests.post('https://github.com/login/oauth/access_token', data={
        'client_id': settings.WAFER_GITHUB_CLIENT_ID,
        'client_secret': settings.WAFER_GITHUB_CLIENT_SECRET,
        'code': code,
    })
    if r.status_code != 200:
        return HttpResponseForbidden('Invalid code', content_type='text/plain')
    token = r.content

    r = requests.get('https://api.github.com/user?%s' % token)
    if r.status_code != 200:
        return HttpResponseForbidden('Unexpected response from github',
                content_type='text/plain')
    gh = r.json()

    email = gh.get('email', None)
    if not email:  # No public e-mail address
        r = requests.get('https://api.github.com/user/emails?%s' % token)
        if r.status_code != 200:
            return HttpResponseForbidden('Failed to obtain email address from'
                    ' github - unexpected response', content_type='text/plain')
        try:
            email = r.json()[0]['email']
        except (KeyError, IndexError) as e:
            log.debug('Error extracting github email address: %s', e)
            return HttpResponseForbidden('Failed to obtain email address from'
                    ' github - unexpected response', content_type='text/plain')

    try:
        user = authenticate(github_login=gh['login'], name=gh['name'], email=email,
                            blog=gh['blog'])
    except KeyError as e:
        log.debug('Error creating account from github information: %s', e)
        return HttpResponseForbidden('Unexpected response from github.'
                ' Authentication failed', content_type='text/plain')
    except UserProfile.MultipleObjectsReturned as e:
        # FIXME: This is a short term workaround. Find a better fix
        # later
        log.debug('Duplicate accounts for github login %s: %s',
                  gh['login'], e)
        return HttpResponseForbidden('Multiple accounts associated with'
                ' these github credentials. Authentication failed',
                content_type='text/plain')

    if not user:
        return HttpResponseForbidden('Authentication with github credentials'
                ' failed', content_type='text/plain')
    login(request, user)
    return HttpResponseRedirect(reverse(redirect_profile))
<|endoftext|>


<commit_before>#!/usr/bin/env python

NAME = 'Profense (ArmorLogic)'

def is_waf(self):
    if self.matchheader(('Server', 'profense')):
        return True
    if self.matchcookie(r'^PLBSID='):
        return True
    return False<commit_msg>Added profense plugin<commit_after>#!/usr/bin/env python
'''
Copyright (C) 2019, WAFW00F Developers.
See the LICENSE file for copying permission.
'''

NAME = 'Profense (ArmorLogic)'

def is_waf(self):
    schemes = [
        self.matchHeader(('Server', 'Profense')),
        self.matchCookie(r'^PLBSID=')
    ]
    if any(i for i in schemes):
        return True
    return False<|endoftext|>


<commit_before>import asyncio
import logging

from functools import partial

from warreport import redis_conn, screeps_info, queuing, DATABASE_PREFIX
from warreport.screeps_info import ScreepsError

_LAST_CHECKED_TICK_KEY = DATABASE_PREFIX + "last-checked-tick"
_LAST_CHECKED_EXPIRE_SECONDS = 60 * 60
_SEND_TO_BACK_OF_QUEUE_IF_OLDER_THAN_TICKS = 1000

logger = logging.getLogger("warreport")

@asyncio.coroutine
def grab_new_battles(loop):
    """
    :type loop: asyncio.events.AbstractEventLoop
    """
    last_grabbed_tick = yield from loop.run_in_executor(None, redis_conn.get, _LAST_CHECKED_TICK_KEY)
    if last_grabbed_tick:
        last_grabbed_tick = int(last_grabbed_tick)
    while True:
        logger.debug("Grabbing battles.")
        try:
            if last_grabbed_tick is not None:
                battles = yield from loop.run_in_executor(None, partial(screeps_info.grab_battles,
                                                                        since_tick=last_grabbed_tick))
            else:
                battles = yield from loop.run_in_executor(None, partial(screeps_info.grab_battles,
                                                                        interval=2000))
        except ScreepsError as e:
            logging.warning("Error accessing battles API: {}".format(e))
        else:
            grabbed_from = last_grabbed_tick
            last_grabbed_tick = int(battles['time'])
            if len(battles['rooms']):
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug("Found {} new battles in the last {} ticks.".format(
                        len(battles['rooms']), int(last_grabbed_tick) - int(grabbed_from) if grabbed_from else 2000))
                yield from asyncio.gather(
                    loop.run_in_executor(None, partial(redis_conn.set, _LAST_CHECKED_TICK_KEY, last_grabbed_tick,
                                                       ex=_LAST_CHECKED_EXPIRE_SECONDS)),
                    loop.run_in_executor(None, queuing.push_battles_for_processing,
                                         ((obj['_id'], obj['lastPvpTime']) for obj in battles['rooms'])),
                    loop=loop
                )
            elif logger.isEnabledFor(logging.DEBUG):
                logger.debug("Found no new battles in the last {} ticks.".format(
                    int(last_grabbed_tick) - int(grabbed_from) if grabbed_from else 2000))
        yield from asyncio.sleep(60, loop=loop)

@asyncio.coroutine
def process_battles(loop):
    """
    :type loop: asyncio.events.AbstractEventLoop
    """
    while True:
        room_name, hostilities_tick, database_key = yield from loop.run_in_executor(
            None, queuing.get_next_battle_to_process)

        latest_tick = yield from loop.run_in_executor(None, redis_conn.get, _LAST_CHECKED_TICK_KEY)
        allow_unfinished = latest_tick is not None and int(latest_tick) - int(hostilities_tick) > 10000

        battle_info = yield from loop.run_in_executor(None, partial(screeps_info.get_battle_data,
                                                                    room_name, hostilities_tick,
                                                                    allow_unfinished_results=allow_unfinished))

        if battle_info is None:
            # If we continue without sending queuing a finished battle, the battle will simply be sent to the back of
            # the processing queue. This way, we'll keep checking to see if we have any history parts available every 30
            # seconds, and if we have multiple battles which aren't in order, we'll still get to all of them in good
            # time.
            yield from asyncio.sleep(30, loop=loop)
            continue

        logger.debug("Processed {}:{}: submitting to reporting queue!".format(room_name, hostilities_tick))

        yield from loop.run_in_executor(None, queuing.submit_processed_battle, database_key, battle_info)
<commit_msg>For now, solve the duplicate battles problem by only scanning every 15 minutes.<commit_after>import asyncio
import logging

from functools import partial

from warreport import redis_conn, screeps_info, queuing, DATABASE_PREFIX
from warreport.screeps_info import ScreepsError

_LAST_CHECKED_TICK_KEY = DATABASE_PREFIX + "last-checked-tick"
_LAST_CHECKED_EXPIRE_SECONDS = 60 * 60
_SEND_TO_BACK_OF_QUEUE_IF_OLDER_THAN_TICKS = 1000

logger = logging.getLogger("warreport")

@asyncio.coroutine
def grab_new_battles(loop):
    """
    :type loop: asyncio.events.AbstractEventLoop
    """
    last_grabbed_tick = yield from loop.run_in_executor(None, redis_conn.get, _LAST_CHECKED_TICK_KEY)
    if last_grabbed_tick:
        last_grabbed_tick = int(last_grabbed_tick)
    while True:
        logger.debug("Grabbing battles.")
        try:
            if last_grabbed_tick is not None:
                battles = yield from loop.run_in_executor(None, partial(screeps_info.grab_battles,
                                                                        since_tick=last_grabbed_tick))
            else:
                battles = yield from loop.run_in_executor(None, partial(screeps_info.grab_battles,
                                                                        interval=2000))
        except ScreepsError as e:
            logging.warning("Error accessing battles API: {}".format(e))
        else:
            grabbed_from = last_grabbed_tick
            last_grabbed_tick = int(battles['time'])
            if len(battles['rooms']):
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug("Found {} new battles in the last {} ticks.".format(
                        len(battles['rooms']), int(last_grabbed_tick) - int(grabbed_from) if grabbed_from else 2000))
                yield from asyncio.gather(
                    loop.run_in_executor(None, partial(redis_conn.set, _LAST_CHECKED_TICK_KEY, last_grabbed_tick,
                                                       ex=_LAST_CHECKED_EXPIRE_SECONDS)),
                    loop.run_in_executor(None, queuing.push_battles_for_processing,
                                         ((obj['_id'], obj['lastPvpTime']) for obj in battles['rooms'])),
                    loop=loop
                )
            elif logger.isEnabledFor(logging.DEBUG):
                logger.debug("Found no new battles in the last {} ticks.".format(
                    int(last_grabbed_tick) - int(grabbed_from) if grabbed_from else 2000))
        yield from asyncio.sleep(60 * 15, loop=loop)

@asyncio.coroutine
def process_battles(loop):
    """
    :type loop: asyncio.events.AbstractEventLoop
    """
    while True:
        room_name, hostilities_tick, database_key = yield from loop.run_in_executor(
            None, queuing.get_next_battle_to_process)

        latest_tick = yield from loop.run_in_executor(None, redis_conn.get, _LAST_CHECKED_TICK_KEY)
        allow_unfinished = latest_tick is not None and int(latest_tick) - int(hostilities_tick) > 10000

        battle_info = yield from loop.run_in_executor(None, partial(screeps_info.get_battle_data,
                                                                    room_name, hostilities_tick,
                                                                    allow_unfinished_results=allow_unfinished))

        if battle_info is None:
            # If we continue without sending queuing a finished battle, the battle will simply be sent to the back of
            # the processing queue. This way, we'll keep checking to see if we have any history parts available every 30
            # seconds, and if we have multiple battles which aren't in order, we'll still get to all of them in good
            # time.
            yield from asyncio.sleep(30, loop=loop)
            continue

        logger.debug("Processed {}:{}: submitting to reporting queue!".format(room_name, hostilities_tick))

        yield from loop.run_in_executor(None, queuing.submit_processed_battle, database_key, battle_info)
<|endoftext|>


<commit_before>from raco import RACompiler
from raco.language import MyriaAlgebra
from raco.myrialang import compile_to_json
import json

def json_pretty_print(dictionary):
    """a function to pretty-print a JSON dictionary.
From http://docs.python.org/2/library/json.html"""
    return json.dumps(dictionary, sort_keys=True,
            indent=2, separators=(',', ': '))

# A simple join
join = """
A(x,z) :- Twitter(x,y), Twitter(y,z)
"""
# A multi-join version
multi_join = """
A(x,w) :- R3(x,y,z), S3(y,z,w)
"""

# Triangles
triangles = """
A(x,y,z) :- R(x,y),S(y,z),T(z,x)
"""

# Which one do we use?
query = join

def comment(s):
  print "/*\n%s\n*/" % str(s)

# Create a cmpiler object
dlog = RACompiler()

# parse the query
dlog.fromDatalog(query)
print "************ LOGICAL PLAN *************"
cached_logicalplan = str(dlog.logicalplan)
print dlog.logicalplan
print

# Optimize the query, includes producing a physical plan
print "************ PHYSICAL PLAN *************"
dlog.optimize(target=MyriaAlgebra, eliminate_common_subexpressions=False)
print dlog.physicalplan
print

# generate code in the target language
print "************ CODE *************"
myria_json = compile_to_json(query, cached_logicalplan, dlog.physicalplan)
print json_pretty_print(myria_json)
print

# dump the JSON to output.json
print "************ DUMPING CODE TO output.json *************"
with open('output.json', 'w') as outfile:
    json.dump(myria_json, outfile)

<commit_msg>helloworld: add a ThreeHops query on the TwitterK dataset<commit_after>from raco import RACompiler
from raco.language import MyriaAlgebra
from raco.myrialang import compile_to_json
import json

def json_pretty_print(dictionary):
    """a function to pretty-print a JSON dictionary.
From http://docs.python.org/2/library/json.html"""
    return json.dumps(dictionary, sort_keys=True,
            indent=2, separators=(',', ': '))

# A simple join
join = """
A(x,z) :- Twitter(x,y), Twitter(y,z)
"""
# A multi-join version
multi_join = """
A(x,w) :- R3(x,y,z), S3(y,z,w)
"""

# Triangles
triangles = """
A(x,y,z) :- R(x,y),S(y,z),T(z,x)
"""

# Three hops
three_hops = """
ThreeHops(x,w) :- TwitterK(x,y),TwitterK(y,z),TwitterK(z,w)
"""

# Which one do we use?
query = join

def comment(s):
  print "/*\n%s\n*/" % str(s)

# Create a cmpiler object
dlog = RACompiler()

# parse the query
dlog.fromDatalog(query)
print "************ LOGICAL PLAN *************"
cached_logicalplan = str(dlog.logicalplan)
print dlog.logicalplan
print

# Optimize the query, includes producing a physical plan
print "************ PHYSICAL PLAN *************"
dlog.optimize(target=MyriaAlgebra, eliminate_common_subexpressions=False)
print dlog.physicalplan
print

# generate code in the target language
print "************ CODE *************"
myria_json = compile_to_json(query, cached_logicalplan, dlog.physicalplan)
print json_pretty_print(myria_json)
print

# dump the JSON to output.json
print "************ DUMPING CODE TO output.json *************"
with open('output.json', 'w') as outfile:
    json.dump(myria_json, outfile)

<|endoftext|>