示例#1
0
文件: views.py 项目: neithere/orgtool
def page_index(request):
    db = app.get_feature('document_storage').default_db
    pages = Page.objects(db).order_by('date_time')
    for p in pages:
        print p.pk, p, p.summary, p.image
    return {'pages': pages,
            'thumbnail': _get_thumbnail}
示例#2
0
def ocr(args):
    """Performs optical character recognition (OCR) on given image. The image
    can be defined either as path to a file or as the primary key of a Page
    object. The latter case also enables saving the resulting text to the
    object. By default the result is simply printed.
    """
    try:
        assert args.pk or args.path, '--path or --pk must be provided'
        assert not (args.pk and args.path), 'specify either --path or --pk'
        assert not (args.path and args.save), 'use --save only with --pk'
    except AssertionError as e:
        raise CommandError(e)

    if args.pk:
        db = app.get_feature('document_storage').default_db
        page = Page.object(db, args.pk)
        path = page.image.full_path
    else:
        path = args.path

    text = image_to_text(path, language=args.language)

    if args.save:
        page.details = text
        page.save()
        yield 'Page saved with the new text.'
    else:
        yield text
示例#3
0
def add_need(args):
    """Creates a need with given text. Project name can be incomplete but
    unambiguous.
    """
    for func in flatten_nargs_plus, fix_unicode:
        func(args, 'project', 'summary')
    db = app.get_feature('document_storage').default_db
    # FIXME Docu seems to fail at filling default values such as "is_satisfied"
    need = Need(summary=args.summary, is_satisfied=False)
    project = None
    if args.project:
        qs = SystemUnderDevelopment.objects(db)
        qs = qs.where(summary__matches_caseless=args.project)
        if not qs:
            yield('No projects matching "{0}"'.format(args.project))
            return
        if 1 < len(qs):
            yield('Found {0} projects matching "{1}". Which to use?'.format(
                    len(qs), args.project))
            for candidate in qs:
                yield(u'- {summary}'.format(**candidate))
            return
        project = qs[0]

    # TODO: check for duplicates; require --force to add with dupes
    pk = need.save(db)    # redundant?
    yield(u'Added need: {0}'.format(need.summary))
    if project:
        project.needs.append(need.pk)
        project.save()
        yield(u'  to project: {0}.'.format(project.summary))
    yield('  primary key: {0}.'.format(pk))
示例#4
0
    def __call__(self, environ, start_response):
        plugin = app.get_feature(Routing.features)

        # create request object
        request = Request(environ)
        logger.debug('Got new request object')

        local.request = request

        request_ready.send(sender=request, request=request)

        # bind URLs
        plugin.env['urls'] = plugin.env['url_map'].bind_to_environ(environ)
        #urls_bound.send(sender=self, map_adapter=self.urls)

        # determine current URL, find and call corresponding view function
        # another approach: http://stackoverflow.com/questions/1796063/werkzeug-mapping-urls-to-views-via-endpoint
        logger.debug('Dispatching the request')


        result = plugin.env['urls'].dispatch(plugin._find_and_call_view,
                                             catch_http_exceptions=True)
        #except NotFound:
        #    return self.app()(environ, start_response)
        return result(environ, start_response)
示例#5
0
文件: plugins.py 项目: neithere/tool
def get_feature(name):
    """ Returns the extension class instance """
    try:
        return app.get_feature(name)
    except KeyError:
        raise KeyError('Plugin with identity "{0}" does not exist. Configured '
                       'plugins are: {1}'.format(identity, app._features.keys()))
示例#6
0
def import_dump(path):
    "Imports an XML dump of all your bookmarks on delicious.com"
    assert os.path.exists(path)
    db = app.get_feature("document_storage").default_db
    x = xml.dom.minidom.parse(path)
    posts = x.documentElement.getElementsByTagName("post")
    seen_cnt = saved_cnt = 0
    for post in posts:
        seen_cnt += 1
        data = dict(post.attributes.items())
        if Bookmark.objects(db).where(url=data["href"]).count():
            continue
        bookmark = Bookmark(
            url=data["href"],
            summary=data["description"],
            details=data["extended"],
            tags=data["tag"].split(),
            date_time=datetime.datetime.strptime(data["time"], "%Y-%m-%dT%H:%M:%SZ"),
            shared=False if data.get("shared") == "no" else True,
        )
        # FIXME: check uniqueness
        print u"Saving {date_time} {url}...".format(**bookmark)
        pk = bookmark.save(db)
        saved_cnt += 1
    print "Saved {saved_cnt} bookmarks of {seen_cnt}.".format(**locals())
示例#7
0
def _import_one_sms(data, current_number=None, dry_run=False):
    # define who's the actor and who's the receiver
    other_number = unicode(data['Number'])
    if data['State'] == 'Sent':
        sent_by, sent_to = current_number, other_number
    else:
        sent_by, sent_to = other_number, current_number

    fields = dict(
        sent_by = sent_by,
        sent_to = sent_to,
        date_time = data['DateTime'],
        #is_confirmed = True,  # it is sent, yup
        summary = data['Text'] or u'[text missing]',  # u'' is invalid, None not accepted
    )
    search_fields = dict(fields)
    search_fields.pop('summary')

    # workaround: we don't know which number was "current" when the message was
    # last imported, so we look for any "our" number.
    my_numbers = _get_my_numbers()
    k = 'sent_by' if data['State']=='Sent' else 'sent_to'
    search_fields.pop(k)
    search_fields.update({'{0}__in'.format(k): my_numbers.values()})

    db = app.get_feature('document_storage').default_db
    if not GammuSMS.objects(db).where(**search_fields).count():
        #print 'NOT FOUND:', search_fields
        print u'SAVING {0} {1} → {2} {3}{4}'.format(
            fields['date_time'], sent_by, sent_to, fields['summary'][:20],
            u'…' if 20 < len(fields['summary']) else '')
        if dry_run:
            return '(stub: dry run)'
        else:
            return GammuSMS(**fields).save(db)
示例#8
0
文件: routing.py 项目: neithere/tool
def redirect_to(endpoint, **kwargs):
    """
    A wrapper for :func:`redirect`. The difference is that the endpoint is
    first resolved via :func:`url_for` and then passed to :func:`redirect`.
    """
    plugin = app.get_feature(BaseRoutingPlugin.features)
    return plugin.redirect_to(endpoint, **kwargs)
示例#9
0
def list_events(query=None, format=u'{date_time} {summary}', date=None):
    "Displays a list of matching events."
    db = app.get_feature('document_storage').default_db
    events = Event.objects(db)
    if query:
        events = events.where(summary__matches_caseless=query)
    for event in events:
        yield format.format(**event)
示例#10
0
文件: __init__.py 项目: neithere/tool
 def make_env(self):
     # XXX it is assumed that Jinja2 is the templating engine. It should be
     # possible to populate other engines' globals.
     logger.debug('Contributing functions to the template environment...')
     templating = app.get_feature('templating')
     templating.update_template_context(dict(
         get_breadcrumb_title = get_title,
         get_breadcrumb_trail = get_trail_titles,
     ))
示例#11
0
def render_template(template_path, **extra_context):
    """
    Renders given template file with given context.

    :template_path:
        path to the template; must belong to one of directories listed in
        `searchpaths` (see configuration).
    """
    plugin = app.get_feature(FEATURE)
    return plugin.render_template(template_path, extra_context)
示例#12
0
def _get_my_numbers():
    bundle_name = '.'.join(__name__.split('.')[:-1])
    my_numbers = app.get_feature('mobile').get('my_numbers')
    assert my_numbers, ('Gammu integration requires "my_numbers" setting. '
                        'It must be a dictionary like {"work": "123"}.')
    assert isinstance(my_numbers, dict)
    def _normalize(value):
        if isinstance(value, int):
            return u'+{0}'.format(value)
        return unicode(value)
    return dict((k,_normalize(v)) for k,v in my_numbers.iteritems())
示例#13
0
文件: routing.py 项目: neithere/tool
def url_for(endpoint, **kwargs):
    """
    Given the endpoint and keyword arguments, builds and returns the URL.

    :param endpoint:
        either a callable object, or a string representing the dotted path to
        such object (e.g. ``myapp.views.list_entries``)

    The keywords are passed to :meth:`MapAdapter.build`.
    """
    plugin = app.get_feature(BaseRoutingPlugin.features)
    return plugin.url_for(endpoint, **kwargs)
示例#14
0
def add_pages(args):
    _args_to_unicode(args, ['language', 'summary', 'summary_prefix'])
    db = app.get_feature('document_storage').default_db

    # check if the files exist
    for path in args.paths:
        assert os.path.exists(path)

    # import
    for path in args.paths:
        yield '* importing {0} (language {1})'.format(path, args.language)

        fingerprint = get_file_hash(open(path, 'rb'))

        # check file hash uniqueness
        if Page.objects(db).where(source_fingerprint=fingerprint):
            yield '...already in the database.'
            continue

        p = Page()

        p.summary = args.summary or get_summary_from_path(path)
        if args.summary_prefix:
            p.summary = u'{0} {1}'.format(args.summary_prefix, p.summary)
        p.language = args.language or None
        p.source_fingerprint = fingerprint
        if not args.no_ocr:
            try:
                p.details = image_to_text(path=path, language=p.language)
            except RuntimeError as e:
                if not args.skip_ocr_errors:
                    raise CommandError(e)
                yield '(OCR failed, saving only image itself)'

        # usually we don't need heavy formats like ppm or tiff even for OCR
        img = Image.open(path)
        if args.format:
            fmt = args.format
        elif img.format not in IMAGE_FORMATS:
            fmt = IMAGE_FORMATS[0]
        else:
            fmt = img.format
        img.save(TMP_FILENAME, fmt)
        p['image'] = open(TMP_FILENAME, 'rb')
        # provide original path so that the resulting filename is inherited
        p['image'].path = path

        p.save(db)
示例#15
0
def import_contacts():
    db = app.get_feature('document_storage').default_db
    sm = _get_state_machine()
    memory = 'ME'    # TODO: allow importing from SIM memory, too

    # NOTE: GetNextMemory is not implemented as of python-gammu 1.28.0, so we
    # cannot reuse the iteration code from _iterate_results.
    #
    # Also, we have to iterate the whole lot of slots despite there can be
    # actually a very few records at the very beginning of the list. The import
    # process may seem too long because of this.
    seen_cnt = saved_cnt = 0
    location = 0
    while True:
        location += 1
        try:
            item = sm.GetMemory(Type=memory, Location=location)
        except gammu.ERR_EMPTY:
            # empty records are not always at the end
            continue
        except gammu.ERR_INVALIDLOCATION:
            # aha, looks like there are no more slots
            break
        else:
            seen_cnt += 1
            elems = [(x['Type'], x['Value']) for x in item['Entries']]
            person, contacts = GammuContact.from_raw_elems(elems)

            def _contact_exists(contact):
                conditions = {'kind': contact.kind, 'value': contact.value}
                duplicates = GammuContact.objects(db).where(**conditions)
                return bool(duplicates.count())

            contacts = [c for c in contacts if not _contact_exists(c)]

            if not contacts:
                # even the Person instance is not saved if there's no new
                # contact information (the contacts could be moved btw)
                continue

            # this could be improved so that details don't matter, etc.
            person, created = db.get_or_create(type(person), **person)
            for contact in contacts:
                contact.person = person
                contact.save(db)

    print 'Imported {saved_cnt} of {seen_cnt}.'.format(**locals())
示例#16
0
def mark_need(args):
    "Marks matching needs as satisfied."
    for func in flatten_nargs_plus, fix_unicode:
        func(args, 'query', 'project')
    assert not (args.important and args.unimportant)
    assert not (args.satisfied and args.unsatisfied)
    assert any([args.satisfied, args.unsatisfied,
                args.important, args.unimportant]), 'A flag must be chosen'
    if args.primary_key:
        assert not (args.query or args.project), (
            '--primary-key cannot be combined with --query/--project')
        db = app.get_feature('document_storage').default_db
        needs = [db.get(Need, args.primary_key)]
    else:
        needs = ensure_results(find_needs, project=args.project, query=args.query)
    for need in needs:
        satisfied = 'satisfied' if need.is_satisfied else 'unsatisfied'
        important = 'important' if not need.is_discarded else 'unimportant'
        yield(u'- {summary} ({satisfied}, {important})'.format(
            satisfied=satisfied, important=important, **need))
    yield('')
    if confirm('Apply changes to these items', default=True, skip=args.yes):
        if args.dry_run:
            yield('Simulation: nothing was actually changed in the database.')
        else:
            for need in needs:
                yield(u'Marking "{summary}"...'.format(**need))
                if args.satisfied and not need.is_satisfied:
                    yield(u'  + unsatisfied → satisfied')
                    need.is_satisfied = True
                if args.unsatisfied and need.is_satisfied:
                    yield(u'  + satisfied → unsatisfied')
                    need.is_satisfied = False
                if args.important and need.is_discarded:
                    need.is_discarded = False
                    yield(u'  + discarded → important')
                if args.unimportant and not need.is_discarded:
                    yield(u'  + important → discarded')
                    need.is_discarded = True
                need.save()
            yield('Changes have been applied.')
    else:
        yield('Operation cancelled.')
示例#17
0
def rename_need(args):
    """Renames matching item. Requires exactly one match.
    If `summary` is in the form "/foo/bar/", it is interpreted as a regular
    expression.

    Usage::

        $ needs rename -q "teh stuff" the stuff
        $ needs rename -q "teh stuff" /teh/the/

    """
    for func in flatten_nargs_plus, fix_unicode:
        func(args, 'query', 'project', 'to')
    summary = args.to.strip() if args.to else None
    assert summary
    if args.primary_key:
        db = app.get_feature('document_storage').default_db
        need = db.get(Need, args.primary_key)
    else:
        try:
            # TODO: support batch renames (with regex only?)
            need = get_single(find_needs, query=args.query)
        except (NotFound, MultipleMatches) as e:
            yield(u'Bad query "{0}": {1}'.format(args.query, e))

    # if new value looks like a regex, then use it that way
    match = re.match('^/([^/]+?)/([^/]+?)/$', summary)
    if match:
        old, new = match.groups()
        yield(u'Using regex: replacing "{old}" with "{new}"'.format(**locals()))
        summary = re.sub(old, new, need.summary)

    if need.summary == summary:
        yield(u'Nothing changed.')
    else:
        yield(u'Renaming "{0}" to "{1}"'.format(need.summary, summary))
        if args.dry_run:
            yield('Simulation: nothing was actually changed in the database.')
        else:
            need.summary = summary
            need.save()
示例#18
0
def view_need(args):
    fix_unicode(args, 'project', 'query')
    if (not (args.primary_key or args.query) or
        (args.primary_key and args.query)):
        raise CommandError('Please specify either --query or --primary-key')
    need = None
    try:
        project = get_single(find_projects, query=args.project)
    except MultipleMatches:
        project = None
    if args.query:
        try:
            need = get_single(find_needs, query=args.query, project=project)
        except MultipleMatches as e:
            yield(str(e))
            raise NotImplementedError # TODO
    if args.primary_key:
        db = app.get_feature('document_storage').default_db
        need = db.get(Need, args.primary_key)
    if need:
        yield(need.dump())
示例#19
0
文件: __init__.py 项目: neithere/tool
def get_title(path=None):
    """
    Resolves the URL to a view function and returns the relevant title. The
    title is either taken from the view function's attribute ``breadcrumb`` (can
    be set with :func:`entitled` or manually) or from the view function's
    ``__name__``.

    :param path:
        The "path" part of a URL (see werkzeug.Request.path). If `None`,
        current URL is used (from context locals).

    """
    routing = app.get_feature('routing')
    assert routing.env['urls'], 'Application must be initialized'
    if path is None or path == '':
        path = request.path
    assert isinstance(path, basestring)
    try:
        func, kwargs = routing.env['urls'].match(path)
    except werkzeug.exceptions.NotFound:
        return u'(ENDPOINT NOT FOUND)'
    except werkzeug.routing.RequestRedirect as e:
        return u'({0})'.format(e.new_url)
    except werkzeug.exceptions.MethodNotAllowed as e:
        # if GET is inadequate for this URL, no need to give it a title
        # XXX ...or not? what if it the title is going to be used for the
        # heading of a page that accepts a non-GET method but then normally
        # displays a form or some confirmation messages?
        return u'(Cannot access this URL with GET)'
    else:
        if hasattr(func, BREADCRUMB_ATTR_NAME):
            name_source = getattr(func, BREADCRUMB_ATTR_NAME)
            if hasattr(name_source, '__call__'):
                return name_source(**kwargs)
            else:
                return unicode(name_source)
        return unicode(func.__name__.replace('_', ' '))
示例#20
0
def delete_need(args):
    """Deletes needs with given primary key or with summary matching given
    query. If the query matches more than one item, the operation is cancelled.
    """
    for func in flatten_nargs_plus, fix_unicode:
        func(args, 'query', 'project')
    if args.primary_key:
        yield('Deleting need with primary key {0}'.format(args.primary_key))
        db = app.get_feature('document_storage').default_db
        need = db.get(Need, args.primary_key)
        if confirm(u'Delete need {summary}'.format(**need)):
            if not args.dry_run:
                remove_need(need, dry_run=args.dry_run)
        else:
            yield('Operation cancelled.')
    elif args.query or args.project:
        try:
            needs = ensure_results(find_needs, project=args.project,
                                   query=args.query)
        except NotFound as e:
            raise CommandError('Cannot delete items: {0}'.format(e))
        yield('Matching needs:')
        for need in needs:
            yield(u'- {summary}'.format(**need))
            yield('  primary key: {0}'.format(need.pk))
        if confirm('Delete these items'):
            if not args.dry_run:
                for need in needs:
                    yield('Dropping {summary}…'.format(**need))
                    remove_need(need, dry_run=args.dry_run)
        else:
            yield('Operation cancelled.')
    else:
        yield('Please specify either --primary-key or --query/--project.')

    if args.dry_run:
        yield('Simulation: nothing was actually changed in the database.')
示例#21
0
 def __getitem__(self, label):
     plugin = app.get_feature(FEATURE)
     if label in plugin.env:
         return plugin.env[label]
     raise KeyError('Unknown document storage "{0}". These storages are '
                    'configured: {1}'.format(label, plugin.env.keys()))
示例#22
0
def import_sms(args):
    """Imports SMS from mobile phone.

    :param current_phone:
        Expects "current phone" so that incoming and outgoing messages can be
        correctly populated with both sender and receiver numbers (the phone only
        stores the "other" number) and you have to manually specify yours). Can be
        omitted if there's only one "my number" in the settings.

        Note that you should specify the label (e.g. "personal", "work" or
        "primary") instead of the number itself. The labels are defined in the
        "my_numbers" setting for the bundle::

            extensions:
                orgtool.ext.mobile.MobileETL:
                    my_numbers:
                        home: +1234567890
                        work: +0987654321

    :param full_archive:
        If True, attempts to import all messages in the phone. Despite this
        implies checking for duplicates, the process takes longer and issues
        with dates and phone numbers may arise. By default this option is off
        and only "new" messages are imported. Message is considered "new" if
        its date is greater than the last known message's date. Time is
        ignored in this check.

    :param dry_run:
        If True, newly imported messages are not saved. Use this for testing.
        Default is False.

    """
    if args.dry_run:
        yield 'Dry run, no data will be changed.'

    db = app.get_feature('document_storage').default_db
    my_numbers = _get_my_numbers()
    current_number = None
    if args.current_phone:
        assert args.current_phone in my_numbers, (
            'unknown number label "{0}"'.format(args.current_phone))
        current_number = my_numbers[args.current_phone]
    else:
        if len(my_numbers) != 1:
            raise CommandError('Which phone (SIM card) is that? Choices: '
                               '{0}'.format(', '.join(my_numbers)))
        the_only_label = my_numbers.keys()[0]
        current_number = my_numbers[the_only_label]
    assert current_number

    # find latest known message date
    if args.full_archive:
        last_imported_date = None
        yield 'Importing all messages from the phone...'
    else:
        msgs = GammuSMS.objects(db).order_by('date_time', reverse=True)
        last_imported_date = msgs[0].date_time.date() if msgs.count() else None
        yield 'Importing messages since {0}...'.format(last_imported_date)

    sm = _get_state_machine()

    seen_cnt = saved_cnt = 0
    for data in _iterate_results(sm.GetNextSMS, Folder=0):
        if last_imported_date and not args.full_archive:
            # skip message without full check if a later message had been
            # already imported
            if data['DateTime'].date() < last_imported_date:
                continue
        saved = _import_one_sms(data, current_number, dry_run=args.dry_run)
        if saved:
            saved_cnt += 1
        seen_cnt += 1

    yield 'Imported {saved_cnt} of {seen_cnt}.'.format(**locals())
    if args.dry_run:
        yield '(Dry run, nothing really changed.)'
示例#23
0
文件: utils.py 项目: neithere/orgtool
def get_default_currency():
    ext = app.get_feature('money')
    return unicode(ext.env['default_currency'])
示例#24
0
文件: views.py 项目: neithere/orgtool
def page_detail(request, pk):
    db = app.get_feature('document_storage').default_db
    page = db.get(Page, pk)
    return {'page': page,
            'thumbnail': _get_thumbnail}
示例#25
0
def register_templates(module_path, dir_name=DEFAULT_PATH, prefix=None):
    """See :meth:`JinjaPlugin.register_templates` and
    :meth:`MakoPlugin.register_templates`.
    """
    app.get_feature(FEATURE).register_templates(module_path, dir_name, prefix)
示例#26
0
文件: views.py 项目: neithere/orgtool
BASE_IMAGE_URL = u'/media/scanned_papers/'

@url('/')
@entitled(u'Pages')
@as_html('papers/page_index.html')
def page_index(request):
    db = app.get_feature('document_storage').default_db
    pages = Page.objects(db).order_by('date_time')
    for p in pages:
        print p.pk, p, p.summary, p.image
    return {'pages': pages,
            'thumbnail': _get_thumbnail}

@url('/<string:pk>')
@entitled(lambda pk: u'{0}'.format(
    app.get_feature('document_storage').default_db.get(Page,pk)))
@as_html('papers/page_detail.html')
def page_detail(request, pk):
    db = app.get_feature('document_storage').default_db
    page = db.get(Page, pk)
    return {'page': page,
            'thumbnail': _get_thumbnail}


def _get_thumbnail(image, w=100, h=100):
    import Image
    import os
    appendix = '.thumbnail_{0}x{1}.{2}'.format(w, h, 'JPEG')# image.file.format)
    th_full_path = image.full_path + appendix
    if not os.path.exists(th_full_path):
        print 'NOT CACHED'
示例#27
0
def get_image_base_path():
    from tool import app
    ext = app.get_feature('papers')
    return ext.env['image_base_path']
示例#28
0
文件: shell.py 项目: neithere/tool
 def init_func():
     ns = {'app': app}
     # FIXME this should be conditional
     db_ext = app.get_feature('document_storage')
     ns.update(db=db_ext.default_db)
     return ns
示例#29
0
文件: views.py 项目: neithere/tool
def env(name):
    "Returns plugin environment variable of given name."
    plugin = app.get_feature('admin')
    return plugin.env[name]