From 6bd9dcdb814b2f03f053ff5f5bf9a20ffe092582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Mon, 27 Feb 2017 01:18:09 +0100 Subject: Inserting Documents --- archivist/cli.py | 77 ++++++++++++++++++++++++++++++++++++++++++------- archivist/model.py | 19 ++++++++---- archivist/peewee_ext.py | 10 +++++++ archivist/prefixes.py | 1 + 4 files changed, 91 insertions(+), 16 deletions(-) diff --git a/archivist/cli.py b/archivist/cli.py index 0b8abe1..8238a30 100644 --- a/archivist/cli.py +++ b/archivist/cli.py @@ -61,6 +61,12 @@ def list_prefixes(): for p in Prefix.select(): print(" * %s (builtin: %s; pseudo: %s)" % (p.name, p.builtin, p.pseudo)) +def prefix_tag_name(name, prefix=None): + if prefix: + return "%s:%s" % (prefix, name) + else: + return name + class PrefixTag: def __init__(self, tag, prefix = None): self.tag = tag @@ -72,6 +78,16 @@ class PrefixTag: else: return self.tag + def __iter__(self): + yield self.prefix + yield self.tag + + def prefixed_name(self): + return prefix_tag_name(self.tag, self.prefix) + + def __str__(self): + return self.prefixed_name() + class PrefixTagType(click.ParamType): name = 'prefixed tag' @@ -87,12 +103,8 @@ class PrefixTagType(click.ParamType): TAG = PrefixTagType() -@tag.command('add') -@click.argument('name', type = TAG) -@click.argument('description', required = False) -def add_tag(name, description): +def create_tag(name, description=None): from .model import Tag, Prefix, db - with db.atomic(): if name.prefix: prefix, created = Prefix.get_or_create(name = name.prefix) @@ -100,14 +112,20 @@ def add_tag(name, description): if not created and prefix.pseudo: raise click.UsageError("Prefix '%s' is not allowed to carry additional tags." % name.prefix) - tag, created = Tag.create_or_get(name = name.tag, prefix = name.prefix, description = description) - if not created: - print("Tag already existed:", tag) + return Tag.create_or_get(name = name.tag, prefix = name.prefix, description = description) + +@tag.command('add') +@click.argument('name', type = TAG) +@click.argument('description', required = False) +def add_tag(name, description): + created, tag = create_tag(name, description) + if not created: + print("Tag already existed:", tag) @tag.command('edit') -@click.option('--description') @click.argument('name', type = TAG) -def edit_tag(description, name): +@click.option('--description') +def edit_tag(name, description): from .model import Tag, Prefix, db try: @@ -118,3 +136,42 @@ def edit_tag(description, name): if description: tag.description = description tag.save() + +@cli.group() +def doc(): + """Document handling""" + pass + +@doc.command('add') +@click.argument('file', type=click.File(mode = 'rb')) +@click.argument('tags', type = TAG, nargs = -1) +@click.option('--create-tags', '-c', is_flag = True) +@click.option('--ignore-missing-tags', '-i', is_flag = True) +def add_doc(file, tags, create_tags, ignore_missing_tags): + """Add a new document together with the given tags.""" + + from .model import Document, Tag, db, DocumentTag + from .peewee_ext import sqlite_tuple_in + import magic + + with db.atomic(): + if tags: + if create_tags: + tags = [create_tag(tag)[0] for tag in tags] + else: + fetched_tags = Tag.select().where(sqlite_tuple_in((Tag.prefix, Tag.name), tags)).execute() + if len(fetched_tags) < len(tags): + print("Some tags were not present:", ', '.join(set(map(str, tags)).difference(map(str, fetched_tags)))) + if not ignore_missing_tags: + raise click.ClickException("Not all tags present") + tags = fetched_tags + + mimetype = magic.from_file(file.name, mime=True) + + doc = Document.create(content = file.read(), + file_type = mimetype, + original_path = file.name, + direction = Document.Direction.IN) + + for t in tags: + DocumentTag.create(document = doc, tag = t) diff --git a/archivist/model.py b/archivist/model.py index fd1bec3..bee6200 100644 --- a/archivist/model.py +++ b/archivist/model.py @@ -1,5 +1,5 @@ from peewee import * -from playhouse.fields import CompressedField +from playhouse.fields import CompressedField as _CompressedField from playhouse.hybrid import * from playhouse.sqlite_ext import SqliteExtDatabase @@ -31,6 +31,13 @@ class BaseModel(Model): class Meta: database = db +class CompressedField(_CompressedField): + def db_value(self, value): + return value if value is None else self.compress(value) + + def python_value(self, value): + return value if value is None else self.decompress(value) + @table class Document(BaseModel): @unique @@ -39,10 +46,11 @@ class Document(BaseModel): OUT = 1 content = CompressedField() - created = DateField(default=datetime.datetime.now) + created = DateField(default=datetime.date.today) inserted = DateTimeField(default=datetime.datetime.now) description = CharField(null=True) original_path = CharField(null=True) + file_type = CharField(null=True) direction = EnumField(Direction, null=True) @classmethod @@ -87,6 +95,7 @@ class Tag(BaseModel): (('name', 'prefix'), True) ] + @property def prefixed_name(self): if self.prefix_id: return "%s:%s" % (self.prefix_id, self.name) @@ -94,12 +103,10 @@ class Tag(BaseModel): return self.name def __str__(self): - description = ' -- ' + self.description if self.description else '' - - return self.prefixed_name() + description + return self.prefixed_name def __repr__(self): - return "<%s (#%d) %s>" % (self.__class__.__name__, self.id, self.prefixed_name()) + return "<%s (#%d) %s>" % (self.__class__.__name__, self.id, self.prefixed_name) @table class DocumentTag(BaseModel): diff --git a/archivist/peewee_ext.py b/archivist/peewee_ext.py index 9fda66e..80bb6f4 100644 --- a/archivist/peewee_ext.py +++ b/archivist/peewee_ext.py @@ -1,6 +1,16 @@ from playhouse.sqlite_ext import VirtualModel, VirtualIntegerField, VirtualCharField from peewee import Field +from itertools import starmap +from functools import reduce +import operator as op + +def sqlite_tuple_in(fields, values): + """SQLite does not support (foo, bar) IN ((1,2),(3,4)). + So we construct a '(foo = 1 AND bar = 2) OR (foo = 3 AND bar = 4)' monstrum.""" + subqueries = (reduce(op.and_, starmap(op.eq, zip(fields, value_tuple))) for value_tuple in values) + return reduce(op.or_, subqueries) + class EnumField(Field): db_field = 'enum' diff --git a/archivist/prefixes.py b/archivist/prefixes.py index 4e73ca7..a039485 100644 --- a/archivist/prefixes.py +++ b/archivist/prefixes.py @@ -20,6 +20,7 @@ def _create_prefixes(): FieldPseudoPrefix('direction', 'In- or outgoing', Document.direction) FieldPseudoPrefix('date', 'Date of creation', Document.created) + FieldPseudoPrefix('mime', 'Mime Type', Document.file_type) for part in ('day', 'month', 'year'): FieldPartPseudoPrefix(part, part.capitalize() + ' of creation', Document.created, part) -- cgit v1.2.3