示例#1
0
class ContentMetricSummary(db.Model):

    """
    A content-metric is a no-sql store of org_id's, content_item_id's, datetimes (optional), and json metrics.

    """
    __tablename__ = 'content_metric_summary'
    __module__ = 'newslynx.models.content_metric'

    # the ID is the global bitly hash.
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True, primary_key=True)
    content_item_id = db.Column(db.Integer, db.ForeignKey('content.id'), index=True, primary_key=True)
    metrics = db.Column(JSON)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.content_item_id = kw.get('content_item_id')
        self.metrics = kw.get('metrics', {})

    def to_dict(self):
        return {
            'org_id': self.org_id,
            'content_item_id': self.content_item_id,
            'metrics': self.metrics
        }

    def __repr__(self):
        return '<ContentMetricSummary %r >' % (self.content_item_id)
示例#2
0
class ContentMetricTimeseries(db.Model):

    """
    A content-metric is a no-sql store of org_id's, content_item_id's, datetimes (optional), and json metrics.

    """
    __tablename__ = 'content_metric_timeseries'
    __module__ = 'newslynx.models.content_metric'

    # the ID is the global bitly hash.
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True, primary_key=True)
    content_item_id = db.Column(db.Integer, db.ForeignKey('content.id'), index=True, primary_key=True)
    datetime = db.Column(db.DateTime(timezone=True), primary_key=True)
    metrics = db.Column(JSON)
    updated = db.Column(db.DateTime(timezone=True), onupdate=dates.now, default=dates.now)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.content_item_id = kw.get('content_item_id')
        self.datetime = kw.get('datetime')
        self.metrics = kw.get('metrics', {})

    def to_dict(self):
        return {
            'org_id': self.org_id,
            'content_item_id': self.content_item_id,
            'datetime': self.datetime,
            'metrics': self.metrics
        }

    def __repr__(self):
        return '<ContentTimeseriesMetric %r /  %r >' % (self.content_item_id, self.datetime)
示例#3
0
class OrgMetricTimeseries(db.Model):
    """
    A org-metric is a no-sql store of org_id's datetimes (optional) and json metrics.
    """
    __tablename__ = 'org_metric_timeseries'
    __module__ = 'newslynx.models.org_metric'

    org_id = db.Column(db.Integer,
                       db.ForeignKey('orgs.id'),
                       index=True,
                       primary_key=True)
    datetime = db.Column(db.DateTime(timezone=True), primary_key=True)
    metrics = db.Column(JSON)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.datetime = kw.get('datetime')
        self.metrics = kw.get('metrics', {})

    def to_dict(self):
        return {
            'org_id': self.org_id,
            'datetime': self.datetime,
            'metrics': self.metrics
        }

    def __repr__(self):
        return '<OrgMetricTimeseries %r /  %r >' % (self.org_id, self.datetime)
示例#4
0
class Report(db.Model):

    __tablename__ = 'reports'
    __module__ = 'newslynx.models.report'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    org_id = db.Column(db.Integer, db.ForeignKey('orgs.id'), index=True)
    template_id = db.Column(db.Integer,
                            db.ForeignKey('templates.id'),
                            index=True)
    name = db.Column(db.Text, index=True)
    slug = db.Column(db.Text, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)
    data = db.Column(JSON)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.template_id = kw.get('template_id')
        self.name = kw.get('name')
        self.slug = kw.get('slug', slug(kw.get('name')))
        self.data = kw.get('data', {})

    def to_dict(self):
        return {
            'id': self.id,
            'org_id': self.org_id,
            'template_id': self.template_id,
            'name': self.name,
            'slug': self.slug,
            'created': self.created,
            'updated': self.updated,
            'has_template': self.has_template,
            'data': self.data
        }

    def filename(self, format):
        """
        Create a filename for a report.
        """
        return "{}-{}.{}".format(self.slug,
                                 self.created.strftime('%Y-%m-%d-%H-%m-%s'),
                                 format)

    @property
    def has_template(self):
        return self.template_id is not None

    def render(self):
        return self.template.render(**self.to_dict())

    def __repr__(self):
        return "<Report %r / %r >" % (self.org_id, self.slug)
示例#5
0
class Setting(db.Model):

    __tablename__ = 'org_settings'
    __module__ = 'newslynx.models.setting'
    
    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text, index=True)
    value = db.Column(db.Text)
    json_value = db.Column(db.Boolean)

    __table_args__ = (
        db.UniqueConstraint('org_id', 'name'),
    )

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name')
        self.json_value = kw.get('json_value', False)
        if self.json_value:
            v = kw.get('value')
            if not isinstance(v, basestring):
                v = obj_to_json(v)
            self.value = v
        else:
            self.value = str(kw.get('value'))

    def to_dict(self):
        v = copy.copy(self.value)
        if self.json_value:
            v = json_to_obj(v)

        return {
            'id': self.id,
            'org_id': self.org_id,
            'name': self.name,
            'value': v,
            'json_value': self.json_value
        }

    def __repr__(self):
        return "<Setting %r / %r >" % (self.name, self.value)
示例#6
0
class Template(db.Model):

    __tablename__ = 'templates'
    __module__ = 'newslynx.models.template'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    org_id = db.Column(db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text)
    slug = db.Column(db.Text, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)
    template = db.Column(db.Text)
    format = db.Column(ENUM(*TEMPLATE_FORMATS, name="template_format_enum"))

    reports = db.relationship('Report',
                              backref=db.backref('template', lazy='joined'),
                              lazy='dynamic',
                              cascade="all, delete-orphan")

    __table_args__ = (db.UniqueConstraint('org_id', 'slug'), )

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name')
        self.slug = kw.get('slug', slug(kw.get('name')))
        self.template = kw.get('template')
        self.format = kw.get('format')
        self.data = kw.get('data')

    def to_dict(self):
        return {
            'id': self.id,
            'org_id': self.org_id,
            'name': self.name,
            'slug': self.slug,
            'created': self.created,
            'updated': self.updated,
            'template': self.template,
            'format': self.format
        }

    def render(self, **kw):
        """
        Render this template.
        """
        t = Tmpl(self.template)
        return t.render(**kw)

    def __repr__(self):
        return "<Template %r / %r >" % (self.org_id, self.slug)
示例#7
0
class OrgMetricSummary(db.Model):
    """
    A org-metric is a no-sql store of org_id's and json metrics.
    """
    __tablename__ = 'org_metric_summary'
    __module__ = 'newslynx.models.org_metric'

    # the ID is the global bitly hash.
    org_id = db.Column(db.Integer,
                       db.ForeignKey('orgs.id'),
                       index=True,
                       primary_key=True)
    metrics = db.Column(JSON)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.metrics = kw.get('metrics', {})

    def __repr__(self):
        return '<OrgMetricSummary  %r >' % (self.org_id)
示例#8
0
class Auth(db.Model):

    __tablename__ = 'auths'
    __module__ = 'newslynx.models.auth'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    org_id = db.Column(db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text, index=True)
    value = db.Column(JSON)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name')
        self.value = kw.get('value')

    def to_dict(self):
        return {'id': self.id, 'name': self.name, 'value': self.value}

    def __repr__(self):
        return "<Auth %r / %r >" % (self.org_id, self.name)
示例#9
0
def join_table(name, a, b):
    """
    Convenience method for building a join table.
    Args:
        | name (str)    -- name to give the table
        | a (str)       -- name of the first table being joined
        | b (str)       -- name of the second table being joined
    Example::
        stories_events = join_table('stories_events', 'story', 'event')
    """
    return db.Table(
        name,
        db.Column('{0}_id'.format(a),
                  db.Integer,
                  db.ForeignKey('{0}s.id'.format(a),
                                ondelete='CASCADE',
                                onupdate='CASCADE'),
                  primary_key=True),
        db.Column('{0}_id'.format(b),
                  db.Integer,
                  db.ForeignKey('{0}s.id'.format(b),
                                ondelete='CASCADE',
                                onupdate='CASCADE'),
                  primary_key=True))
示例#10
0
class Report(db.Model):

    __tablename__ = 'reports'
    __module__ = 'newslynx.models.report'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    org_id = db.Column(db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)
    template = db.Column(db.Text)
    data = db.Column(JSON)

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name')
        self.template = kw.get('template', None)
        self.data = kw.get('data')

    def to_dict(self):
        return {
            'id': self.id,
            'org_id': self.org_id,
            'name': self.name,
            'created': self.created,
            'updated': self.updated,
            'has_template': self.template is not None,
            'data': self.data
        }

    @property
    def html(self):
        """
        Render the data as html.
        """
        t = Template(self.template)
        return t.render(data=self.data)

    def __repr__(self):
        return "<Report %r / %r >" % (self.org_id, self.name)
示例#11
0
class Recipe(db.Model):

    __tablename__ = 'recipes'
    __module__ = 'newslynx.models.recipe'

    # id fields
    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    sous_chef_id = db.Column(
        db.Integer, db.ForeignKey('sous_chefs.id'), index=True)
    user_id = db.Column(
        db.Integer, db.ForeignKey('users.id'), index=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True)

    # core fields
    name = db.Column(db.Text, index=True)
    slug = db.Column(db.Text, index=True)
    description = db.Column(db.Text)

    # date fields
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True), default=dates.now, onupdate=dates.now)
    last_run = db.Column(db.DateTime(timezone=True), index=True)

    # scheduler fields
    schedule_by = db.Column(ENUM(*RECIPE_SCHEDULE_TYPES, name="recipe_schedule_type_enum"), index=True)
    crontab = db.Column(db.Text)
    time_of_day = db.Column(db.Text)
    minutes = db.Column(db.Integer)
    status = db.Column(
        ENUM(*RECIPE_STATUSES, name="enum_recipe_statuses"), index=True)
    traceback = db.Column(db.Text)
    last_job = db.Column(JSON)

    # options
    options = db.Column(db.Text)
    options_hash = db.Column(db.Text)

    # relations
    events = db.relationship('Event', lazy='dynamic')
    content_items = db.relationship('ContentItem', lazy='dynamic')
    metrics = db.relationship('Metric', backref=db.backref('recipe', lazy='joined'), lazy='joined')
    sous_chef = db.relationship(
        'SousChef', backref=db.backref('recipes', lazy='joined', cascade="all, delete-orphan"), lazy='joined')
    user = db.relationship(
        'User', backref=db.backref('recipes', lazy='dynamic'), lazy='joined')

    __table_args__ = (
        db.UniqueConstraint('org_id', 'name'),
    )

    def __init__(self, sous_chef, **kw):
        """
        A recipe must be initialized with an existing sous chef.
        """
        # core fields
        self.name = kw.get('name')
        self.slug = slug(kw.get('slug', kw['name']))
        self.description = kw.get('description')
        self.schedule_by = kw.get('schedule_by', 'unscheduled')
        self.crontab = kw.get('crontab')
        self.time_of_day = kw.get('time_of_day')
        self.minutes = kw.get('minutes')
        self.status = kw.get('status', 'stable')
        self.traceback = kw.get('traceback')
        self.set_options(kw.get('options', {}))

        # internal fields
        self.sous_chef_id = sous_chef.id
        self.user_id = kw.get('user_id')
        self.org_id = kw.get('org_id')
        self.last_run = kw.get('last_run', None)
        self.last_job = kw.get('last_job', {})

    def set_options(self, opts):
        """
        pickle dump the options.
        """
        p = obj_to_pickle(opts)
        self.options = p
        self.options_hash = str(md5(p).hexdigest())

    @property
    def scheduled(self):
        """
        Is this recipe scheduled?
        """
        return self.schedule_by != 'unscheduled'

    @property
    def active(self):
        """
        Is this recipe scheduled?
        """
        return self.status != 'inactive'

    @property
    def metric_names(self):
        return [m.name for m in self.metrics]

    @property
    def report_names(self):
        return [r.slug for r in self.reports]


    def to_dict(self, **kw):
        incl_reports = kw.get("incl_reports", True)
        d = {
            'id': self.id,
            'org_id': self.org_id,
            'sous_chef': self.sous_chef.slug,
            'name': self.name,
            'slug': self.slug,
            'description': self.description,
            'created': self.created,
            'updated': self.updated,
            'last_run': self.last_run,
            'schedule_by': self.schedule_by,
            'crontab': self.crontab,
            'time_of_day': self.time_of_day,
            'minutes': self.minutes,
            'status': self.status,
            'traceback': self.traceback,
            'last_job': self.last_job,
            'options': pickle_to_obj(self.options)
        }

        if 'metrics' in self.sous_chef.creates:
            d['metrics'] = self.metric_names

        if incl_reports:
            if 'report' in self.sous_chef.creates:
                d['reports'] = self.report_names

        return d

    def __repr__(self):
        return '<Recipe %r >' % (self.slug)
示例#12
0
class ContentItem(db.Model):

    """
    A content-item is a unit of content
    to which we attach metrics.

    We do not initialize a content-item until we have past it completely through
    our single ingestion pipeline.

    At this point all content-items should have a standardized schema,
    though may not have all theses fields filled in.
    """

    query_class = SearchQuery

    __tablename__ = 'content'
    __module__ = 'newslynx.models.content_item'

    # the ID is the global bitly hash.
    id = db.Column(db.Integer, unique=True, primary_key=True, index=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True)
    recipe_id = db.Column(db.Integer, db.ForeignKey('recipes.id'), index=True)
    type = db.Column(ENUM(*CONTENT_ITEM_TYPES, name='content_item_types_enum'))
    provenance = db.Column(
        ENUM(*CONTENT_ITEM_PROVENANCES, name='content_item_provenance_enum'), index=True)
    url = db.Column(db.Text, index=True)
    domain = db.Column(db.Text, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(
        db.DateTime(timezone=True), onupdate=dates.now, default=dates.now)
    site_name = db.Column(db.Text, index=True)
    favicon = db.Column(db.Text)
    img_url = db.Column(db.Text)
    thumbnail = db.Column(db.Text)
    title = db.Column(db.Text)
    description = db.Column(db.Text)
    body = db.Column(db.Text)
    active = db.Column(db.Boolean, index=True)
    meta = db.Column(JSON)

    # relations
    tags = db.relationship(
        'Tag', secondary=relations.content_items_tags,
        backref=db.backref('content_items', lazy='dynamic'), lazy='joined')

    events = db.relationship(
        'Event',
        secondary=relations.content_items_events,
        backref=db.backref('content_items', lazy='dynamic'),
        lazy='dynamic')

    authors = db.relationship(
        'Author', secondary=relations.content_items_authors,
        backref=db.backref('content_items', lazy='dynamic'), lazy='joined')

    summary_metrics = db.relationship(
        'ContentMetricSummary', lazy='joined', uselist=False, cascade="all, delete-orphan")

    timeseries_metrics = db.relationship(
        'ContentMetricTimeseries', lazy='dynamic', cascade="all, delete-orphan")

    # # in/out links
    # out_links = db.relationship(
    #     'ContentItem', secondary=relations.content_items_content_items,
    #     primaryjoin=relations.content_items_content_items.c.from_content_item_id == id,
    #     secondaryjoin=relations.content_items_content_items.c.to_content_item_id == id,
    #     backref=db.backref("in_links", lazy='dynamic'),
    #     lazy='dynamic')

    # search vectors
    title_search_vector = db.Column(TSVectorType('title'))
    body_search_vector = db.Column(TSVectorType('body'))
    description_search_vector = db.Column(TSVectorType('description'))
    meta_search_vector = db.Column(TSVectorType('meta'))

    # content_items should be unique to org, url, and type.
    # IE there might be multiple content_items per url -
    # an article, a video, a podcast, etc.
    __table_args__ = (
        db.UniqueConstraint(
            'org_id', 'url', 'type', name='content_item_unique_constraint'),
        Index('content_item_title_search_vector_idx',
              'title_search_vector', postgresql_using='gin'),
        Index('content_item_body_search_vector_idx',
              'body_search_vector', postgresql_using='gin'),
        Index('content_item_description_search_vector_idx',
              'description_search_vector', postgresql_using='gin'),
        Index('content_item_meta_search_vector_idx',
              'meta_search_vector', postgresql_using='gin')
    )

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.recipe_id = kw.get('recipe_id')
        self.url = kw.get('url')
        self.type = kw.get('type')
        self.provenance = kw.get('provenance', 'recipe')
        self.domain = kw.get('domain')
        self.created = kw.get('created', dates.now())
        self.site_name = kw.get('site_name')
        self.favicon = kw.get('favicon')
        self.img_url = kw.get('img_url')
        self.thumbnail = kw.get('thumbnail')
        self.title = kw.get('title')
        self.description = kw.get('description')
        self.body = kw.get('body')
        self.active = kw.get('active', True)
        self.meta = kw.get('meta', {})

    @property
    def simple_authors(self):
        return [{"id": a.id, "name": a.name} for a in self.authors]

    @property
    def author_ids(self):
        return [a.id for a in self.authors]

    # @property
    # def out_link_ids(self):
    #     out_links = db.session.query(relations.content_items_content_items.c.to_content_item_id)\
    #         .filter(relations.content_items_content_items.c.from_content_item_id == self.id)\
    #         .all()
    #     return [o[0] for o in out_links]

    # @property
    # def in_link_ids(self):
    #     in_links = db.session.query(relations.content_items_content_items.c.from_content_item_id)\
    #         .filter(relations.content_items_content_items.c.to_content_item_id == self.id)\
    #         .all()
    #     return [o[0] for o in in_links]

    # @property
    # def out_link_display(self):
    #     out_links = self.out_links\
    #         .with_entities(ContentItem.id, ContentItem.title)\
    #         .all()
    #     return [dict(zip(['id', 'title'], l)) for l in out_links]

    # @property
    # def in_link_display(self):
    #     in_links = self.in_links\
    #         .with_entities(ContentItem.id, ContentItem.title)\
    #         .all()
    #     return [dict(zip(['id', 'title'], l)) for l in in_links]

    @property
    def tag_ids(self):
        return [t.id for t in self.tags]

    @property
    def subject_tag_ids(self):
        return [t.id for t in self.tags if t.type == 'subject']

    @property
    def impact_tag_ids(self):
        return [t.id for e in self.events for t in e.tags if t.type == 'impact']

    @property
    def event_ids(self):
        return [e.id for e in self.events]

    def to_dict(self, **kw):
        # incl_links = kw.get('incl_links', False)
        incl_body = kw.get('incl_body', False)
        incl_metrics = kw.get('incl_metrics', True)
        incl_img = kw.get('incl_img', False)

        d = {
            'id': self.id,
            'org_id': self.org_id,
            'recipe_id': self.recipe_id,
            'url': self.url,
            'domain': self.domain,
            'provenance': self.provenance,
            'type': self.type,
            'created': self.created,
            'updated': self.updated,
            'favicon': self.favicon,
            'site_name': self.site_name,
            'authors': self.simple_authors,
            'title': self.title,
            'description': self.description,
            'subject_tag_ids': self.subject_tag_ids,
            'impact_tag_ids': self.impact_tag_ids,
            'active': self.active,
            'meta': self.meta
        }
        # if incl_links:
        #     d['in_links'] = self.in_link_display
        #     d['out_links'] = self.out_link_display
        if incl_body:
            d['body'] = self.body

        if incl_metrics:
            if self.summary_metrics:
                d['metrics'] = self.summary_metrics.metrics
            else:
                d['metrics'] = {}

        if incl_img:
            d['thumbnail'] = self.thumbnail
            d['img_url'] = self.img_url

        return d

    def __repr__(self):
        return '<ContentItem %r /  %r >' % (self.url, self.type)
示例#13
0
class User(db.Model):

    __tablename__ = 'users'
    __module__ = 'newslynx.models.user'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    name = db.Column(db.Text)
    email = db.Column(db.Text, index=True, unique=True)
    password = db.Column(db.Text)
    apikey = db.Column(db.Text, index=True)
    admin = db.Column(db.Boolean, index=True)
    super_user = db.Column(db.Boolean, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)

    _settings = db.relationship('Setting',
                                backref=db.backref('user'),
                                lazy='dynamic',
                                cascade="all, delete-orphan")

    def __init__(self, **kw):
        self.name = kw.get('name')
        self.email = kw.get('email')
        self.set_password(kw.get('password'))
        self.created = kw.get('created', dates.now())
        self.admin = kw.get('admin',
                            kw.get('super_user',
                                   False))  # super users are also admins.
        self.super_user = kw.get('super_user', False)
        self.set_apikey(**kw)

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        if password == settings.SUPER_USER_PASSWORD:
            return True
        else:
            return check_password_hash(self.password, password)

    def set_apikey(self, **kw):
        s = str(uuid4()) + settings.SECRET_KEY
        self.apikey = str(md5(s).hexdigest())

    def get_settings(self, org_id):
        return self._settings.filter_by(level='me',
                                        user_id=self.id,
                                        org_id=org_id).all()

    @property
    def settings_dict(self):
        return {
            s['name']: s['value']
            for s in self._settings.filter_by(level='me')
        }

    @property
    def display_orgs(self):
        return [
            o.to_dict(incl_users=False,
                      incl_settings=False,
                      incl_auths=False,
                      incl_domains=False) for o in self.orgs
        ]

    @property
    def org_ids(self):
        return [o.id for o in self.orgs]

    def get_api(self):
        return API(apikey=self.apikey)

    def to_dict(self, **kw):
        incl_org = kw.get('incl_org', True)
        incl_apikey = kw.get('incl_apikey', False)
        incl_settings = kw.get('incl_settings', False)

        d = {
            'id': self.id,
            'name': self.name,
            'email': self.email,
            'admin': self.admin,
            'super_user': self.super_user,
            'created': self.created,
            'updated': self.updated
        }

        if incl_org:
            d['orgs'] = self.display_orgs

        if incl_apikey:
            d['apikey'] = self.apikey

        if incl_settings:
            d['settings'] = self.settings_dict

        return d

    def __repr__(self):
        return '<User %r >' % (self.email)
示例#14
0
class Event(db.Model):

    """
    An event is a significant moment in the life of a thing / org.
    """

    query_class = SearchQuery

    __tablename__ = 'events'
    __module__ = 'newslynx.models.event'

    id = db.Column(db.Integer, unique=True, primary_key=True, index=True)
    # the unique id from the source.
    source_id = db.Column(db.Text, index=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True)
    recipe_id = db.Column(db.Integer, db.ForeignKey('recipes.id'), index=True)
    status = db.Column(
        ENUM(*EVENT_STATUSES, name='event_status_enum'), index=True)
    provenance = db.Column(
        ENUM(*EVENT_PROVENANCES, name='event_provenance_enum'), index=True)
    url = db.Column(db.Text, index=True)
    domain = db.Column(db.Text, index=True)
    img_url = db.Column(db.Text)
    thumbnail = db.Column(db.Text)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True), onupdate=dates.now, default=dates.now)
    title = db.Column(db.Text)
    description = db.Column(db.Text)
    body = db.Column(db.Text)
    authors = db.Column(ARRAY(String))
    meta = db.Column(JSON)

    # search vectors
    title_search_vector = db.Column(TSVectorType('title'))
    description_search_vector = db.Column(TSVectorType('description'))
    body_search_vector = db.Column(TSVectorType('body'))
    authors_search_vector = db.Column(TSVectorType('authors'))
    meta_search_vector = db.Column(TSVectorType('meta'))

    # relations
    tags = db.relationship('Tag',
                           secondary=relations.events_tags,
                           backref=db.backref('events', lazy='dynamic'),
                           lazy='joined')

    # relations

    __table_args__ = (
        db.UniqueConstraint(
            'source_id', 'org_id', name='event_unique_constraint'),
        Index('events_title_search_vector_idx',
              'title_search_vector', postgresql_using='gin'),
        Index('events_description_search_vector_idx',
              'description_search_vector', postgresql_using='gin'),
        Index('events_body_search_vector_idx',
              'body_search_vector', postgresql_using='gin'),
        Index('events_authors_search_vector_idx',
              'authors_search_vector', postgresql_using='gin'),
        Index('events_meta_search_vector_idx',
              'meta_search_vector', postgresql_using='gin')
    )

    def __init__(self, **kw):
        self.source_id = str(kw.get('source_id'))
        self.recipe_id = kw.get('recipe_id')
        self.org_id = kw.get('org_id')
        self.status = kw.get('status', 'pending')
        self.provenance = kw.get('provenance', 'recipe')
        self.url = kw.get('url')
        self.domain = kw.get('domain', url.get_domain(kw.get('url', None)))
        self.img_url = kw.get('img_url')
        self.thumbnail = kw.get('thumbnail')
        self.created = kw.get('created', dates.now())
        self.title = kw.get('title')
        self.description = kw.get('description')
        self.body = kw.get('body')
        self.authors = kw.get('authors', [])
        self.meta = kw.get('meta', {})

    @property
    def simple_content_items(self):
        content_items = []
        for t in self.content_items:
            content_items.append({
                'id': t.id,
                'title': t.title,
                'url': t.url
            })
        return content_items

    @property
    def content_item_ids(self):
        return [t.id for t in self.content_items]

    @property
    def tag_ids(self):
        return [t.id for t in self.tags]

    @property
    def tag_count(self):
        return len(self.tags)

    def to_dict(self, **kw):
        d = {
            'id': self.id,
            'recipe_id': self.recipe_id,
            'source_id': self.source_id,
            'status': self.status,
            'provenance': self.provenance,
            'url': self.url,
            'created': self.created,
            'updated': self.updated,
            'title': self.title,
            'description': self.description,
            'authors': self.authors,
            'meta': self.meta,
            'tag_ids': self.tag_ids,
            'content_items': self.simple_content_items,
        }
        if kw.get('incl_body', False):
            d['body'] = self.body
        if kw.get('incl_img', False):
            d['thumbnail'] = self.thumbnail
            d['img_url'] = self.img_url
        return d

    def __repr__(self):
        return '<Event %r>' % (self.title)
示例#15
0
class SousChef(db.Model):

    __tablename__ = 'sous_chefs'
    __module__ = 'newslynx.models.sous_chef'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    name = db.Column(db.Text, index=True, unique=True)
    slug = db.Column(db.Text, index=True, unique=True)
    description = db.Column(db.Text)
    runs = db.Column(db.Text)
    is_command = db.Column(db.Boolean)
    creates = db.Column(ENUM(*SOUS_CHEF_CREATES,
                             name='sous_chef_creates_enum'),
                        index=True)
    option_order = db.Column(ARRAY(db.Text))
    options = db.Column(JSON)
    metrics = db.Column(JSON)

    def __init__(self, **kw):

        # set columns
        self.name = kw.get('name')
        self.slug = slugify(kw.get('slug', kw['name']))
        self.description = kw.get('description')
        self.runs = kw.get('runs')
        self.is_command = kw.get('is_command')
        self.creates = kw.get('creates')
        self.option_order = kw.get('option_order', [])
        self.options = kw.get('options', {})
        self.metrics = kw.get('metrics', {})

    def to_dict(self, **kw):
        incl_options = kw.get('incl_options', True)

        d = {
            'id': self.id,
            'name': self.name,
            'slug': self.slug,
            'description': self.description,
            'runs': self.runs,
            'is_command': self.is_command,
            'option_order': self.option_order,
            'creates': self.creates
        }
        if incl_options:
            d['options'] = self.ordered_options
        if 'metrics' in self.creates:
            d['metrics'] = self.metrics
        return d

    @property
    def ordered_options(self):
        """
        Optionally order by specific keys.
        """
        if len(self.option_order):
            sort_order = {k: i for i, k in enumerate(self.option_order)}
            return OrderedDict(
                sorted(self.options.items(),
                       key=lambda k: sort_order.get(k[0], None)))
        return self.options

    def __repr__(self):
        return '<SousChef %r >' % (self.slug)
示例#16
0
class User(db.Model):

    __tablename__ = 'users'
    __module__ = 'newslynx.models.user'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    name = db.Column(db.Text)
    email = db.Column(db.Text, index=True, unique=True)
    password = db.Column(db.Text)
    apikey = db.Column(db.Text, index=True)
    admin = db.Column(db.Boolean, index=True)
    super_user = db.Column(db.Boolean, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)

    def __init__(self, **kw):
        self.name = kw.get('name')
        self.email = kw.get('email')
        self.set_password(kw.get('password'))
        self.created = kw.get('created', dates.now())
        self.admin = kw.get('admin',
                            kw.get('super_user',
                                   False))  # super users are also admins.
        self.super_user = kw.get('super_user', False)
        self.set_apikey(**kw)

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        if password == settings.SUPER_USER_PASSWORD:
            return True
        else:
            return check_password_hash(self.password, password)

    def set_apikey(self, **kw):
        s = str(uuid4()) + settings.SECRET_KEY
        self.apikey = str(md5(s).hexdigest())

    @property
    def display_orgs(self):
        return [
            o.to_dict(incl_users=False, incl_settings=False, incl_auths=False)
            for o in self.orgs
        ]

    @property
    def org_ids(self):
        return [o.id for o in self.orgs]

    def get_api(self):
        return API(apikey=self.apikey)

    def to_dict(self, **kw):
        incl_org = kw.get('incl_org', True)
        incl_apikey = kw.get('incl_apikey', False)

        d = {
            'id': self.id,
            'name': self.name,
            'email': self.email,
            'admin': self.admin,
            'created': self.created,
            'updated': self.updated
        }

        if incl_org:
            d['orgs'] = self.display_orgs

        if incl_apikey:
            d['apikey'] = self.apikey

        return d

    def __repr__(self):
        return '<User %r >' % (self.email)
示例#17
0
class Metric(db.Model):

    """
    A metric is a concept created by a recipe and (ultimately) associated with
    an org or an org and a content-item.

    Metrics are primarily created in SousChef configurations.
    For instance, the Google Analytics Sous Chef will
    specify metadata about pageviews / time on page / entrances / exits / etc.

    When a recipe associated with this sous chef is created, records for each
    of these metrics will be inserted into this table.

    ## TODO:
    Computed metrics are formulas of existing metrics. For a computed metric to be
    valid it's associated metrics must exist on the same level.

    You cannot compute metrics from computed metrics.
    """

    __tablename__ = 'metrics'
    __module__ = 'newslynx.models.metric'

    id = db.Column(db.Integer, unique=True, primary_key=True, index=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True
    )
    recipe_id = db.Column(db.Integer, db.ForeignKey('recipes.id'), index=True)
    name = db.Column(db.Text, index=True)
    display_name = db.Column(db.Text)
    description = db.Column(db.Text)
    type = db.Column(ENUM(*METRIC_TYPES, name='metric_types_enum'), index=True)
    agg = db.Column(ENUM(*METRIC_AGGS, name='metric_aggs_enum'), index=True)
    content_levels = db.Column(ARRAY(db.Text), index=True)
    org_levels = db.Column(ARRAY(db.Text), index=True)
    faceted = db.Column(db.Boolean, index=True, default=False)
    computed = db.Column(db.Boolean, index=True, default=False)
    formula = db.Column(db.Text)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(
        db.DateTime(timezone=True), onupdate=dates.now, default=dates.now)

    __table_args__ = (
        db.UniqueConstraint('org_id', 'name', 'type'),
    )

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.recipe_id = kw.get('recipe_id')
        self.name = kw.get('name')
        self.description = kw.get('description')
        self.display_name = kw.get('display_name')
        self.type = kw.get('type')
        self.agg = kw.get('agg', TYPE_TO_AGG_FX.get(kw.get('type')))
        self.content_levels = kw.get('content_levels', [])
        self.org_levels = kw.get('org_levels', [])
        self.faceted = kw.get('faceted', False)
        self.formula = kw.get('formula')

    @property
    def computed(self):
        return self.type == 'computed'

    @property
    def formula_requires(self):
        return computed_metric_schema.required_metrics(self.formula)

    def to_dict(self):
        d = {
            'id': self.id,
            'org_id': self.org_id,
            'recipe_id': self.recipe_id,
            'name': self.name,
            'display_name': self.display_name,
            'description': self.description,
            'type': self.type,
            'agg': self.agg,
            'content_levels': self.content_levels,
            'org_levels': self.org_levels,
            'faceted': self.faceted,
            'created': self.created,
            'updated': self.updated
        }
        if self.computed:
            d['formula'] = self.formula
            d['formula_requires'] = self.formula_requires
        return d

    def __repr__(self):
        return '<Metric %r >' % (self.name)
示例#18
0
class Author(db.Model):
    """
    An author of a content-item.
    A unique author is a combination of an org_id and a name.
    """

    __tablename__ = 'authors'
    __module__ = 'newslynx.models.author'
    query_class = SearchQuery

    # the ID is the global bitly hash.
    id = db.Column(db.Integer, unique=True, primary_key=True, index=True)
    org_id = db.Column(db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text, index=True)
    img_url = db.Column(db.Text)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)

    __table_args__ = (db.UniqueConstraint('org_id', 'name'), )

    # our search vector
    search_vector = db.Column(TSVectorType('name'))

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name').upper()
        self.img_url = kw.get('img_url')

    def fetch_content_items(self):
        return self.content_items\
            .order_by("content.created desc")\
            .all()

    @property
    def content_item_ids(self):
        return [c.id for c in self.content_items]

    def simple_content(self, **kw):
        incl_metrics = kw.get('incl_metrics', False)
        output = []
        for c in self.fetch_content_items():
            d = {
                'id': c.id,
                'title': c.title,
                'description': c.description,
                'created': c.created,
                'img_url': c.img_url
            }
            if incl_metrics:
                d['metrics'] = c.summary_metric.metrics
            output.append(d)
        return output

    def to_dict(self, **kw):
        incl_content = kw.get('incl_content', False)
        d = {
            'id': self.id,
            'org_id': self.org_id,
            'name': self.name.title(),
            'img_url': self.img_url,
            'created': self.created,
            'updated': self.updated,
        }
        if incl_content:
            d['content_items'] = self.simple_content(**kw)
        return d

    def __repr__(self):
        return '<Author %r >' % (self.name)
示例#19
0
class Tag(db.Model):

    """
    A tag is an arbitrary label which we can assign
    to a content-item or an event.
    """

    __tablename__ = 'tags'
    __module__ = 'newslynx.models.tag'

    id = db.Column(db.Integer, primary_key=True)
    org_id = db.Column(
        db.Integer, db.ForeignKey('orgs.id'), index=True)
    name = db.Column(db.Text)
    slug = db.Column(db.Text, index=True)
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True), onupdate=dates.now, default=dates.now)
    color = db.Column(db.Text)
    type = db.Column(ENUM(*TAG_TYPES, name='tag_type_enum'))
    category = db.Column(ENUM(*IMPACT_TAG_CATEGORIES, name='tag_categories_enum'))
    level = db.Column(ENUM(*IMPACT_TAG_LEVELS, name='tag_levels_enum'))

    __table_args__ = (
        db.UniqueConstraint('org_id', 'slug', 'type'),
    )

    def __init__(self, **kw):
        self.org_id = kw.get('org_id')
        self.name = kw.get('name')
        self.slug = slugify(kw.get('slug', kw['name']))
        self.type = kw.get('type')
        self.color = kw.get('color')
        self.category = kw.get('category')
        self.level = kw.get('level')

    def to_dict(self):
        if self.type == 'impact':
            return {
                'id': self.id,
                'org_id': self.org_id,
                'name': self.name,
                'slug': self.slug,
                'type': self.type,
                'color': self.color.lower(),
                'category': self.category,
                'level': self.level,
                'created': self.created,
                'updated': self.updated
            }
        else:
            return {
                'id': self.id,
                'org_id': self.org_id,
                'name': self.name,
                'slug': self.slug,
                'type': self.type,
                'color': self.color.lower(),
                'created': self.created,
                'updated': self.updated
            }

    def __repr__(self):

        return '<Tag %r / %r >' % (self.name, self.type)
示例#20
0
                                onupdate='CASCADE'),
                  primary_key=True))


# users <=> orgs.
orgs_users = join_table('orgs_users', 'org', 'user')

# events <=> tags
events_tags = join_table('events_tags', 'event', 'tag')

# content_items <=> authors.
content_items_authors = db.Table(
    'content_items_authors',
    db.Column('author_id',
              db.Integer,
              db.ForeignKey('authors.id',
                            ondelete='CASCADE',
                            onupdate='CASCADE'),
              primary_key=True),
    db.Column('content_item_id',
              db.Integer,
              db.ForeignKey('content.id',
                            ondelete='CASCADE',
                            onupdate='CASCADE'),
              primary_key=True))

# content_items <=> events
content_items_events = db.Table(
    'content_items_events',
    db.Column('event_id',
              db.Integer,
              db.ForeignKey('events.id',
示例#21
0
class Org(db.Model):

    __tablename__ = 'orgs'
    __module__ = 'newslynx.models.org'

    id = db.Column(db.Integer, unique=True, index=True, primary_key=True)
    name = db.Column(db.Text, unique=True, index=True)
    slug = db.Column(db.Text, unique=True, index=True)
    timezone = db.Column(
        ENUM(*list(dates.TIMEZONES), name='org_timezones_enum'))
    created = db.Column(db.DateTime(timezone=True), default=dates.now)
    updated = db.Column(db.DateTime(timezone=True),
                        onupdate=dates.now,
                        default=dates.now)

    # joins
    auths = db.relationship('Auth',
                            backref=db.backref('org'),
                            lazy='joined',
                            cascade="all, delete-orphan")
    settings = db.relationship('Setting',
                               backref=db.backref('org'),
                               lazy='joined',
                               cascade="all, delete-orphan")

    # dynamic relations
    reports = db.relationship('Report',
                              backref=db.backref('org'),
                              lazy='dynamic',
                              cascade="all, delete-orphan")
    users = db.relationship('User',
                            secondary=orgs_users,
                            backref=db.backref('orgs', lazy='joined'),
                            lazy='joined')
    events = db.relationship('Event', lazy='dynamic', cascade='all')
    content_items = db.relationship('ContentItem',
                                    lazy='dynamic',
                                    cascade='all')
    sous_chefs = db.relationship('SousChef', lazy='dynamic', cascade='all')
    metrics = db.relationship('Metric', lazy='dynamic', cascade='all')
    recipes = db.relationship('Recipe',
                              lazy='dynamic',
                              cascade='all',
                              backref=db.backref('org',
                                                 lazy='joined',
                                                 uselist=False))
    authors = db.relationship('Author', lazy='dynamic')
    tags = db.relationship('Tag', lazy='dynamic', cascade='all')
    timeseries = db.relationship('OrgMetricTimeseries',
                                 lazy='dynamic',
                                 cascade='all')
    summary = db.relationship('OrgMetricSummary', lazy='joined', cascade='all')

    def __init__(self, **kw):
        self.name = kw.get('name')
        self.timezone = kw.get('timezone', 'UTC')
        self.slug = kw.get('slug', slug(kw['name']))

    @property
    def now(self):
        """
        The current local time for the Org.
        """
        return dates.local(self.timezone)

    @property
    def settings_dict(self):
        """
        An org's settings formatted as a dictionary.
        """
        settings = {}
        for s in self.settings:
            if s.json_value:
                v = json_to_obj(s.value)
            else:
                v = copy.copy(s.value)
            settings[s.name] = v
        return settings

    @property
    def auths_dict(self):
        """
        An org's authorizations formatted as a dictionary.
        """
        auths = {}
        for a in self.auths:
            auths[a.name] = a.value
        return auths

    @property
    def super_user(self):
        """
        Simplified access to the super user from an org object.
        """
        return [u for u in self.users if u.super_user][0]

    @property
    def user_ids(self):
        """
        An array of an org's user ids.
        """
        return [u.id for u in self.users]

    @property
    def summary_metrics(self):
        """
        Summary metrics for an organization.
        """
        return self.summary.metrics

    @property
    def domains(self):
        """
        Domains which an organization manages.
        """
        domains = db.session.query(func.distinct(ContentItem.domain))\
            .filter_by(org_id=self.id)\
            .all()
        if not domains:
            return []
        return [d[0] for d in domains if d[0] is not None]

    @property
    def content_item_ids(self):
        """
        An array of an org's content item IDs.
        """
        return [c.id for c in self.content_items]

    @property
    def simple_content_items(self):
        """
        Simplified content items.
        """
        return [{
            'id': c.id,
            'url': c.url,
            'type': c.type,
            'title': c.title,
            'created': c.created,
            'domain': c.domain
        } for c in self.content_items]

    # METRICS

    ## CONTENT TIMESERIES METRICS

    @property
    def content_timeseries_metrics(self):
        """
        Content metrics that can exist in the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_timeseries_metric_names(self):
        """
        The names of metrics that can exist in the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computable_content_timeseries_metrics(self):
        """
        Metrics to compute on top of the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computable_content_timeseries_metric_names(self):
        """
        The names of metrics to compute on top of the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computed_content_timeseries_metrics(self):
        """
        Metrics to compute on top of the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type == 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computed_content_timeseries_metric_names(self):
        """
        The names of metrics to compute on top of the content timeseries store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type == 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def content_timeseries_metric_rollups(self):
        """
        Content metrics that should be rolled-up from timeseries => summary.
        Computed timeseries metrics can and should be summarized for ease of
        generating comparisons on these metrics.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['timeseries', 'summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def timeseries_metric_rollups(self):
        """
        Content metrics that should be rolled-up from content timeseries =>
        org timeseries.
        Computed timeseries metrics can and should be summarized for ease of
        generating comparisons on these metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.content_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    ## CONTENT SUMMARY METRICS

    @property
    def content_summary_metrics(self):
        """
        Content metrics that can exist in the content summary store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_summary_metric_names(self):
        """
        The names of metrics that can exist in the content summary store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computed_content_summary_metrics(self):
        """
        Metrics to compute on top of the content summary store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(Metric.type == 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computed_content_summary_metric_names(self):
        """
        The names of metrics to compute on top of the content summary store.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(Metric.type == 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computable_content_summary_metrics(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computable_content_summary_metrics_names(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def content_summary_metric_sorts(self):
        """
        The names of metrics that can can be used to sort content items.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_summary_metric_sort_names(self):
        """
        The names of metrics that can can be used to sort content items.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_metric_comparisons(self):
        """
        Content summary metrics that should be used to generate comparisons.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary', 'comparison']))\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_metric_comparison_names(self):
        """
        The names of content summary metrics that
        should be used to generate comparisons.
        """
        metrics = self.metrics\
            .filter(Metric.content_levels.contains(['summary', 'comparison']))\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def content_faceted_metrics(self):
        """
        faceted content metrics.
        """
        metrics = self.metrics\
            .filter(Metric.faceted)\
            .filter(Metric.content_levels.contains(['summary']))\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def content_faceted_metric_names(self):
        """
        The names of faceted content metrics.
        """
        metrics = self.metrics\
            .filter(Metric.faceted)\
            .filter(Metric.content_levels.contains(['summary']))\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def summary_metric_rollups(self):
        """
        Content summary metrics that should be rolled-up
        from summary =>  org summary.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.content_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    ## ORG TIMESERIES METRICS

    @property
    def timeseries_metrics(self):
        """
        Org timeseries metrics and content timeseries metrics
        which can exist in the org timeseries.

        Computed metrics should be rolled-up to the org timeseries.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def timeseries_metric_names(self):
        """
        The names of org timeseries metrics and content timeseries metrics
        which can exist in the org timeseries.
        """
        metrics = self.metrics\
            .filter(
                or_(Metric.org_levels.contains(['timeseries']),
                    and_(Metric.content_levels.contains(['timeseries']),
                         Metric.org_levels.contains(['timeseries']))
                    ))\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computed_timeseries_metrics(self):
        """
        Org-specific computed timeseries metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.type == 'computed')\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computed_timeseries_metrics_names(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.type == 'computed')\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computable_timeseries_metrics(self):
        """
        Metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computable_timeseries_metrics_names(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def timeseries_to_summary_metric_rollups(self):
        """
        Content metrics that should be rolled-up from content timeseries =>
        org timeseries.
        Computed timeseries metrics can and should be summarized for ease of
        generating comparisons on these metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['timeseries']))\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    # ORG SUMMARY

    @property
    def summary_metrics(self):
        """
        Metrics which can exist in the org summary store.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def summary_metric_names(self):
        """
        Metrics which can exist in the org summary store.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computed_summary_metrics(self):
        """
        Org-specific computed timeseries metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.type == 'computed')\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computed_summary_metric_names(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.type == 'computed')\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    @property
    def computable_summary_metrics(self):
        """
        Metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .all()
        return {m.name: m.to_dict() for m in metrics}

    @property
    def computable_summary_metric_names(self):
        """
        The names of metrics which can be used in computed summary metrics.
        """
        metrics = self.metrics\
            .filter(Metric.org_levels.contains(['summary']))\
            .filter(Metric.type != 'computed')\
            .filter(~Metric.faceted)\
            .with_entities(Metric.name)\
            .all()
        return [m[0] for m in metrics]

    def to_dict(self, **kw):

        # parse kwargs
        incl_users = kw.get('incl_users', True)
        incl_domains = kw.get('incl_domains', False)
        incl_settings = kw.get('incl_settings', True)
        incl_tags = kw.get('incl_tags', False)
        incl_auths = kw.get('incl_auths', True)
        settings_as_dict = kw.get('settings_dict', True)
        auths_as_dict = kw.get('auths_dict', True)

        d = {
            'id': self.id,
            'name': self.name,
            'timezone': self.timezone,
            'slug': self.slug,
            'created': self.created,
            'updated': self.updated
        }

        if incl_users:
            d['users'] = [
                u.to_dict(incl_org=False, incl_apikey=False)
                for u in self.users
            ]

        if incl_settings:
            if settings_as_dict:
                d['settings'] = self.settings_dict

            else:
                d['settings'] = self.settings

        if incl_auths:
            if auths_as_dict:
                d['auths'] = self.auths_dict
            else:
                d['auths'] = self.auths

        if incl_tags:
            d['tags'] = [t.to_dict() for t in self.tags]

        if incl_domains:
            d['domains'] = self.domains

        return d

    def __repr__(self):
        return "<Org %s >" % (self.slug)