# -*- coding: utf-8 -*-
# situation (c) Ian Dennis Miller
import json
import string
import random
from flask_marshmallow.fields import fields
from flask_diamond import db, ma
from flask_diamond.mixins.crud import CRUDMixin
from flask_diamond.mixins.marshmallow import MarshmallowMixin
from slugify import slugify
# https://stackoverflow.com/questions/2257441/python-random-string-generation-with-upper-case-letters-and-digits
[docs]def id_generator(size=8, chars=None):
"""
Create a random sequence of letters and numbers.
:param int size: the desired length of the sequence
:param str chars: the eligible character set to draw from when picking random characters
:returns: a string with the random sequence
"""
if chars is None:
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
return ''.join(random.choice(chars) for x in range(size))
[docs]def dump():
"""
Build a dictionary containing the entire Situation.
:returns: a Dict with the situation as nested Dictionaries.
"""
"save all the people and everything else"
return({
"persons": [p.dump() for p in Person.query.order_by(Person.id).all()],
"acquaintances": [a.dump() for a in Acquaintance.query.order_by(Acquaintance.person_id,
Acquaintance.acquainted_id).all()],
"groups": [g.dump() for g in Group.query.order_by(Group.id).all()],
"places": [p.dump() for p in Place.query.order_by(Place.id).all()],
"items": [i.dump() for i in Item.query.order_by(Item.id).all()],
"events": [e.dump() for e in Event.query.order_by(Event.id).all()],
# "details": [d.dump() for d in Detail.query.order_by(Detail.id).all()],
"excerpts": [e.dump() for e in Excerpt.query.order_by(Excerpt.id).all()],
"resources": [r.dump() for r in Resource.query.order_by(Resource.id).all()],
})
[docs]def save(filename):
"""
Write the Situation to a JSON file.
:param str filename: the name of the file to output to.
"""
with open(filename, "w") as f:
json.dump(dump(), f, indent=True, sort_keys=True)
class ResourceSchema(ma.Schema):
"Description"
class Meta:
additional = ("id", "unique", "name", "url", "publisher", "author", "description")
[docs]class Resource(db.Model, CRUDMixin, MarshmallowMixin):
"""
A Resource is an authoritative information source from which evidence is drawn.
Usually, a Resource is an artifact like a newspaper article, a report, or another
document. These documents usually have an associated URL.
Any time an Excerpt is used, that Excerpt must be directly quotable from a Resource.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the resource is called
:param str url: the canonical URL for the resource
:param str publisher: the name of the institution reputationally backing this resource
:param str author: the name of the author(s)
:param str description: a short summary of this resource
"""
__schema__ = ResourceSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
name = db.Column(db.String(4096))
url = db.Column(db.String(4096))
publisher = db.Column(db.String(4096))
author = db.Column(db.String(4096))
description = db.Column(db.String(8**7))
def __str__(self):
return(self.url)
class ExcerptSchema(ma.Schema):
"Description"
class Meta:
additional = ("id", "unique", "content", "resource_id", "xpath")
[docs]class Excerpt(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str content: the actual quoted material of the excerpt
:param Resource resource: the Resource from which this excerpt comes
:param str xpath: the xpath leading to this excerpt within the Resource
"""
__schema__ = ExcerptSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
content = db.Column(db.String(8**7))
resource = db.relationship('Resource', backref='excerpts')
resource_id = db.Column(db.Integer, db.ForeignKey('resource.id'), nullable=False)
xpath = db.Column(db.String(4096))
def __str__(self):
return(self.content)
class AcquaintanceExcerpt(db.Model, CRUDMixin):
"Excerpts."
__tablename__ = 'acquaintance_excerpts'
__table_args__ = (
db.ForeignKeyConstraint(
['person_id', 'acquainted_id'],
['acquaintance.person_id', 'acquaintance.acquainted_id']
),
)
id = db.Column(db.Integer, primary_key=True)
excerpt_id = db.Column(db.Integer, db.ForeignKey('excerpt.id'), nullable=False)
person_id = db.Column(db.Integer, db.ForeignKey('person.id'), nullable=False)
acquainted_id = db.Column(db.Integer, db.ForeignKey('person.id'), nullable=False)
"Excerpts."
persons_excerpts = db.Table('persons_excerpts',
db.Column('id', db.Integer, primary_key=True),
db.Column('person_id', db.Integer, db.ForeignKey('person.id')),
db.Column('excerpt_id', db.Integer, db.ForeignKey('excerpt.id'))
)
"Excerpts."
places_excerpts = db.Table('places_excerpts',
db.Column('id', db.Integer, primary_key=True),
db.Column('place_id', db.Integer, db.ForeignKey('place.id')),
db.Column('excerpt_id', db.Integer, db.ForeignKey('excerpt.id'))
)
"Excerpts."
items_excerpts = db.Table('items_excerpts',
db.Column('id', db.Integer, primary_key=True),
db.Column('item_id', db.Integer, db.ForeignKey('item.id')),
db.Column('excerpt_id', db.Integer, db.ForeignKey('excerpt.id'))
)
"Excerpts."
events_excerpts = db.Table('events_excerpts',
db.Column('id', db.Integer, primary_key=True),
db.Column('event_id', db.Integer, db.ForeignKey('event.id')),
db.Column('excerpt_id', db.Integer, db.ForeignKey('excerpt.id'))
)
"Excerpts."
groups_excerpts = db.Table('groups_excerpts',
db.Column('id', db.Integer, primary_key=True),
db.Column('group_id', db.Integer, db.ForeignKey('group.id')),
db.Column('excerpt_id', db.Integer, db.ForeignKey('excerpt.id'))
)
class PersonSchema(ma.Schema):
"Description"
slug = fields.Method("get_slugify")
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
events = fields.Nested('EventSchema', allow_none=True, many=True, only=["id"])
places = fields.Nested('PlaceSchema', allow_none=True, many=True, only=["id"])
possessions = fields.Nested('ItemSchema', allow_none=True, many=True, only=["id"])
properties = fields.Nested('PlaceSchema', allow_none=True, many=True, only=["id"])
groups = fields.Nested('GroupSchema', allow_none=True, many=True, only=["id"])
acquaintances = fields.Nested('PersonSchema', allow_none=True, many=True, only=["id"])
# TODO: this is a nested query
# "encounters": [i.id for e in self.events for i in e.items],
def get_slugify(self, obj):
return(slugify(obj.name))
class Meta:
additional = ("id", "name", "alias", "unique")
[docs]class Person(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the person is called
:param str alias: that *other* thing the person is called
:param str slug: a URL-friendly identifier
:param [Excerpt] excerpts: null
:param [Event] events: null
:param [Place] places: null
:param [Item] possessions: null
:param [Place] properties: null
:param [Group] groups: null
:param [Acquaintance] acquaintances: null
"""
__schema__ = PersonSchema
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
alias = db.Column(db.String(255))
unique = db.Column(db.String(255), unique=True, default=id_generator)
excerpts = db.relationship('Excerpt', secondary="persons_excerpts", lazy='dynamic')
def isa(self, isa_type, of=None):
e = Acquaintance(person=self, isa=isa_type, acquainted=of)
result = e.save()
return result
def __str__(self):
return(self.name)
class AcquaintanceSchema(ma.Schema):
"Description"
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
class Meta:
additional = ("isa", "person", "acquainted")
[docs]class Acquaintance(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param int :
:param int :
:param int :
:param int :
:param int :
"""
__schema__ = AcquaintanceSchema
isa = db.Column(db.String(64))
excerpts = db.relationship('Excerpt', secondary="acquaintance_excerpts", lazy='dynamic')
person_id = db.Column(db.Integer(), db.ForeignKey('person.id'), primary_key=True)
person = db.relationship(Person, primaryjoin=person_id == Person.id, backref='acquaintances')
acquainted_id = db.Column(db.Integer(), db.ForeignKey('person.id'), primary_key=True)
acquainted = db.relationship(Person, primaryjoin=acquainted_id == Person.id)
def add_excerpt(self, excerpt):
annotation = AcquaintanceExcerpt.create(
excerpt_id=excerpt.id,
person_id=self.person_id,
acquainted_id=self.acquainted_id
)
return annotation
def __str__(self):
return("%s isa %s of %s)" % (self.person, self.isa, self.acquainted))
class PlaceSchema(ma.Schema):
"Description"
slug = fields.Method("get_slugify")
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
events = fields.Nested('EventSchema', allow_none=True, many=True, only=["id"])
owners = fields.Nested('PersonSchema', allow_none=True, many=True, only=["id"])
def get_slugify(self, obj):
return(slugify(obj.name))
class Meta:
additional = ("id", "unique", "name", "description", "address", "lat", "lon")
[docs]class Place(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the place is called
:param int :
:param int :
:param int :
:param int :
:param int :
:param int :
"""
__schema__ = PlaceSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
name = db.Column(db.String(255))
description = db.Column(db.String(8**7))
address = db.Column(db.String(4096))
lat = db.Column(db.Float())
lon = db.Column(db.Float())
owners = db.relationship('Person', secondary="places_owners", lazy='dynamic',
backref="properties")
excerpts = db.relationship('Excerpt', secondary="places_excerpts", lazy='dynamic')
def __str__(self):
return(self.name)
places_owners = db.Table('places_owners',
db.Column('id', db.Integer, primary_key=True),
db.Column('place_id', db.Integer, db.ForeignKey('place.id')),
db.Column('owner_id', db.Integer, db.ForeignKey('person.id'))
)
class ItemSchema(ma.Schema):
"Description"
slug = fields.Method("get_slugify")
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
owners = fields.Nested('PersonSchema', allow_none=True, many=True, only=["id"])
def get_slugify(self, obj):
return(slugify(obj.name))
class Meta:
additional = ("id", "unique", "name", "description")
[docs]class Item(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the item is called
:param int :
:param int :
:param int :
"""
__schema__ = ItemSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
name = db.Column(db.String(255))
description = db.Column(db.String(8**7))
owners = db.relationship('Person', secondary="items_owners", lazy='dynamic', backref="items")
excerpts = db.relationship('Excerpt', secondary="items_excerpts", lazy='dynamic')
def __str__(self):
return(self.name)
items_owners = db.Table('items_owners',
db.Column('id', db.Integer, primary_key=True),
db.Column('item_id', db.Integer, db.ForeignKey('item.id')),
db.Column('owner_id', db.Integer, db.ForeignKey('person.id'))
)
class GroupSchema(ma.Schema):
slug = fields.Method("get_slugify")
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
members = fields.Nested('PersonSchema', allow_none=True, many=True, only=["id"])
def get_slugify(self, obj):
return(slugify(obj.name))
class Meta:
additional = ("id", "unique", "name")
[docs]class Group(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the group is called
:param int :
:param int :
"""
__schema__ = GroupSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
name = db.Column(db.String(255))
members = db.relationship('Person', secondary="groups_members", lazy='dynamic',
backref="groups")
excerpts = db.relationship('Excerpt', secondary="groups_excerpts", lazy='dynamic')
def __str__(self):
return self.name
groups_members = db.Table('groups_members',
db.Column('id', db.Integer, primary_key=True),
db.Column('group_id', db.Integer, db.ForeignKey('group.id')),
db.Column('member_id', db.Integer, db.ForeignKey('person.id'))
)
class EventSchema(ma.Schema):
slug = fields.Method("get_slugify")
timestamp = fields.Method("get_timestamp")
excerpts = fields.Nested('ExcerptSchema', allow_none=True, many=True, only=["id"])
items = fields.Nested('ItemSchema', allow_none=True, many=True, only=["id"])
actors = fields.Nested('PersonSchema', allow_none=True, many=True, only=["id"])
def get_slugify(self, obj):
return(slugify(obj.name))
def get_timestamp(self, obj):
return(obj.timestamp.strftime('%Y-%m-%dT%H:%M:%S'))
class Meta:
additional = ("id", "unique", "name", "phone", "description", "place_id")
[docs]class Event(db.Model, CRUDMixin, MarshmallowMixin):
"""
Description.
:param int id: the database object identifier
:param str unique: alpha-numeric code for shorthand identifier
:param str name: what the event is called
:param int :
:param int :
:param int :
:param int :
:param int :
:param int :
:param int :
"""
__schema__ = EventSchema
id = db.Column(db.Integer, primary_key=True)
unique = db.Column(db.String(255), unique=True, default=id_generator)
name = db.Column(db.String(255))
description = db.Column(db.String(8**7))
place_id = db.Column(db.Integer, db.ForeignKey('place.id'))
place = db.relationship("Place", backref="events")
phone = db.Column(db.Boolean(), default=False)
timestamp = db.Column(db.DateTime())
actors = db.relationship('Person', secondary="events_actors", lazy='dynamic', backref="events")
excerpts = db.relationship('Excerpt', secondary="events_excerpts", lazy='dynamic')
items = db.relationship('Item', secondary="events_items", lazy='dynamic')
def __str__(self):
return self.name
events_actors = db.Table('events_actors',
db.Column('id', db.Integer, primary_key=True),
db.Column('event_id', db.Integer, db.ForeignKey('event.id')),
db.Column('actor_id', db.Integer, db.ForeignKey('person.id'))
)
events_items = db.Table('events_items',
db.Column('id', db.Integer, primary_key=True),
db.Column('event_id', db.Integer, db.ForeignKey('event.id')),
db.Column('item_id', db.Integer, db.ForeignKey('item.id'))
)