On Thu, Jun 26, 2014 at 1:45 PM, Mike Bayer <mike...@zzzcomputing.com>
wrote:

>  minimal mapping + the query against that mapping.
>
> Okay, for the other method I needed to change, I think I've put together
complete enough examples and have attached the files.  The 'before'
file is what we have currently (and doesn't quite do the right thing as
it's only using the environment_id), and the 'after' file is what seemingly
worked after my coworker and I tried a different route to avoid having
to use a subquery.  One thing to note is there's a reciprocating rela-
tionship added on the Deployment class to the Package class which
initially caused a lovely stack overflow in Python until the joinedload
options were added; this may be a bad idea, but it was the only way
we knew how to get things working the way we needed. :)

I tried to add all the imports as well in case there were any questions
that might come from the code itself; hopefully I didn't miss any, but
if there's still confusion, let me know.

- Ken


>
>
> On 6/26/14, 4:11 PM, Ken Lareau wrote:
>
> On Thu, Jun 26, 2014 at 5:57 AM, Mike Bayer <mike...@zzzcomputing.com>
> wrote:
>
>>
>> On 6/25/14, 8:06 PM, Ken Lareau wrote:
>>
>> On Wed, Jun 25, 2014 at 6:28 AM, Mike Bayer <mike...@zzzcomputing.com>
>> wrote:
>>
>>>
>>> On 6/25/14, 2:26 AM, Ken Lareau wrote:
>>>
>>> On Tue, Jun 24, 2014 at 3:35 PM, Mike Bayer <mike...@zzzcomputing.com>
>>> wrote:
>>>
>>>>
>>>> On 6/23/14, 8:09 PM, Ken Lareau wrote:
>>>> >
>>>> >     if apptier:
>>>> >         subq = (
>>>> >             Session.query(
>>>> >                 Package.pkg_name,
>>>> >                 Package.version,
>>>> >                 Package.revision,
>>>> >                 AppDefinition.app_type,
>>>> >                 AppDeployment.environment
>>>> >             ).join(Deployment)
>>>> >              .join(AppDeployment)
>>>> >              .join(AppDefinition)
>>>> >              .filter(Package.pkg_name == package_name)
>>>> >              .filter(AppDeployment.environment == env)
>>>> >              .filter(AppDeployment.status != 'invalidated'))
>>>> >
>>>> >         [...]
>>>> >
>>>> >         # The actual column name must be used in the subquery
>>>> >         # usage below; DB itself should be corrected
>>>> >         versions = (Session.query(subq.c.appType,
>>>> >                     subq.c.version,
>>>> >                     subq.c.revision)
>>>> >                     .group_by(subq.c.appType, subq.c.environment)
>>>> >                     .all())
>>>> >
>>>> > The parts in the subquery work fine, but the access of the 'column'
>>>> > in the final query leads to this:
>>>> >
>>>> > Traceback (most recent call last):
>>>> >   File "<stdin>", line 1, in <module>
>>>> >   File "/home/klareau/repos/git/tagopsdb/tagopsdb/deploy/deploy.py",
>>>> > line 234, in find_deployed_version
>>>> >     .group_by(subq.c.appType, subq.c.environment)
>>>> >   File
>>>> >
>>>> "/home/klareau/.virtualenvs/tds/lib/python2.6/site-packages/sqlalchemy/util/_collections.py",
>>>> > line 174, in __getattr__
>>>> >     raise AttributeError(key)
>>>> > AttributeError: environment
>>>> >
>>>> > This is not completely surprising, but I'm uncertain as the best way
>>>> > to fix this... help? :)
>>>>  nothing is obviously wrong, assuming you are calling "subquery()" on
>>>> subq at the end.  It will be a select() construct which will have a
>>>> column called ".environment" on the .c. collection because
>>>> AppDeployment.environment is present.   Can't say why this attribute is
>>>> not here without full details.
>>>>
>>>>
>>> Oops there were some additional filters I left out, but this is added
>>> before the final query:
>>>
>>>  subq = (subq.order_by(AppDeployment.realized.desc())
>>>                     .subquery(name='t_ordered'))
>>>
>>>  Not sure why it isn't working, but today my coworker an I massively
>>> rewrote one of the other methods to avoid this same issue, so maybe
>>> we should try the same for this one... though it would be nice to know
>>> what I goofed here, but not sure what additional information I can
>>> give that would help offhand...
>>>
>>>
>>>  what are the names that are actually present on subq.c ?   "print
>>> list(subq.c)" should give an indication.
>>>
>>>
>> Adding that in to the method gave this:
>>
>> >>> deploy.find_deployed_version('tdstest', 'development', version='9',
>> apptier=True)
>> [Column('pkg_name', String(length=255), table=<t_ordered>,
>> nullable=False), Column('version', String(length=63), table=<t_ordered>,
>> nullable=False), Column('revision', String(length=63), table=<t_ordered>,
>> nullable=False), Column('appType', String(length=100), table=<t_ordered>,
>> nullable=False), <sqlalchemy.sql.elements.ColumnClause at 0x1d20e10;
>> %(30521360 anon)s>]
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>>   File "/home/klareau/repos/git/tagopsdb/tagopsdb/deploy/deploy.py", line
>> 237, in find_deployed_version
>>     .group_by(subq.c.appType, subq.c.environment)
>>   File
>> "/home/klareau/.virtualenvs/tds/lib/python2.6/site-packages/sqlalchemy/util/_collections.py",
>> line 174, in __getattr__
>>     raise AttributeError(key)
>> AttributeError: environment
>>
>>  you need to put .label('environment') on that column before it finds
>> its way into subq.  I dont have the mappings here to review.
>>
>> for the next go around please just provide a one-file example of the
>> mapping plus the query, thanks.
>>
>>  Do you desire the example to be fully functional, or just have the
> relevant
> mapping and query?  We ran into the same issue with another query and
> ended up completely reworking the code to avoid the subquery, but it in-
> volves quite a bit of various mappings and I'm uncertain if it's the proper
> way to move forward.  I could try to post the before/after here, but if it
>  needs to be able to to run standalone it may take me a bit of time to get
>  it into a fully workable state (given how extensive it is).
>
>
> --
> - Ken Lareau
>
>  --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
>
>
>  --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
>



-- 
- Ken Lareau

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
from datetime import datetime, timedelta

from sqlalchemy import ForeignKey, func
from sqlalchemy.dialects.mysql import INTEGER, SMALLINT, TIMESTAMP
from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
from sqlalchemy.orm import joinedload, relationship
from sqlalchemy.sql.expression import func, select

from tagopsdb.model import AppDefinition, AppDeployment, \
    Deployment, Environment, Package

from .meta import Base, Column, String  # Minor customizations for 'Column'
                                        # and 'String', consider them like
                                        # the standard defines in SQLA


class AppDefinition(Base):
    __tablename__ = 'app_definitions'

    id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)
    app_type = Column(u'appType', String(length=100), nullable=False)
    status = Column(
        Enum('active', 'inactive'),
        nullable=False,
        server_default='active'
    )

    app_deployments = relationship('AppDeployment')


class AppDeployment(Base):
    __tablename__ = 'app_deployments'

    id = Column(u'AppDeploymentID', INTEGER(), primary_key=True)
    deployment_id = Column(
        u'DeploymentID',
        INTEGER(),
        ForeignKey('deployments.DeploymentID', ondelete='cascade'),
        nullable=False
    )
    app_id = Column(
        u'AppID',
        SMALLINT(display_width=6),
        ForeignKey('app_definitions.AppID', ondelete='cascade'),
        nullable=False
    )
    user = Column(String(length=32), nullable=False)
    status = Column(
        Enum(
            'complete',
            'incomplete',
            'inprogress',
            'invalidated',
            'validated',
        ),
        nullable=False
    )
    environment_id = Column(
        u'environment_id',
        INTEGER(),
        ForeignKey('environments.environmentID', ondelete='cascade'),
        nullable=False
    )
    realized = Column(
        TIMESTAMP(),
        nullable=False,
        server_default=func.current_timestamp()
    )

    application = relationship('AppDefinition')
    deployment = relationship('Deployment')
    environment_obj = relationship('Environment')

    @hybrid_property
    def environment(self):
        return self.environment_obj.environment

    @environment.expression
    def environment(cls):
        return select(
                [Environment.environment]
            ).where(
                Environment.id == cls.environment_id
            ).correlate(cls).as_scalar()

    @hybrid_property
    def needs_validation(self):
        return (self.status == 'complete') | (self.status == 'incomplete')

    @hybrid_method
    def realized_before(self, dt):
        return self.realized < dt

    @realized_before.expression
    def realized_before(cls, dt):
        return cls.realized < dt


class Deployment(Base):
    __tablename__ = 'deployments'

    id = Column(u'DeploymentID', INTEGER(), primary_key=True)
    package_id = Column(
        INTEGER(),
        ForeignKey('packages.package_id', ondelete='cascade'),
        nullable=False
    )

    app_deployments = relationship('AppDeployment')
    package = relationship('Package')


class Environment(Base):
    __tablename__ = 'environments'

    id = Column(u'environmentID', INTEGER(), primary_key=True)
    environment = Column(String(length=15), nullable=False, unique=True)


class Package(Base):
    __tablename__ = 'packages'

    id = Column(u'package_id', INTEGER(), primary_key=True)
    pkg_name = Column(String(length=255), nullable=False)
    version = Column(String(length=63), nullable=False)
    revision = Column(String(length=63), nullable=False)

    deployments = relationship('Deployment')


def find_unvalidated_versions(time_delta, environment):
    """Find the latest deployments that are not validated in a given
       environment for a given amount of time
    """

    validation_threshold = datetime.now() - timedelta(seconds=time_delta)
    AD = AppDeployment  # Alias to make typing a bit shorter

    app_deployments = AD.query(
        ).options(
            joinedload(AD.deployment),
            joinedload(AD.deployment).joinedload(Deployment.package),
            joinedload(AD.application)
        ).filter(
            AD.realized_before(validation_threshold),
            AD.environment == environment,
            AD.needs_validation
        ).order_by(
            AD.realized.desc()
        ).group_by(
            AD.environment_id, AD.app_id
        ).all()

    found = set()

    for app_dep in app_deployments:
        package = app_dep.deployment.package
        app_def = app_dep.application

        key = app_dep.environment, app_def.app_type, package.pkg_name

        if key in found:
            continue

        found.add(key)

        yield (
            package.pkg_name,
            package.version,
            package.revision,
            app_def.app_type,
            app_dep.environment,
            app_dep.realized,
            app_dep.user,
            app_dep.status
        )
from sqlalchemy import ForeignKey, func
from sqlalchemy.dialects.mysql import INTEGER, SMALLINT, TIMESTAMP
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy.sql import and_
from sqlalchemy.sql.expression import func, select

from tagopsdb.model import Session
from tagopsdb.model import AppDefinition, AppDeployment, \
    Deployment, Environment, Package

from .meta import Base, Column, String  # Minor customizations for 'Column'
                                        # and 'String', consider them like
                                        # the standard defines in SQLA


class AppDefinition(Base):
    __tablename__ = 'app_definitions'

    id = Column(u'AppID', SMALLINT(display_width=2), primary_key=True)
    app_type = Column(u'appType', String(length=100), nullable=False)
    status = Column(
        Enum('active', 'inactive'),
        nullable=False,
        server_default='active'
    )

    app_deployments = relationship('AppDeployment')


class AppDeployment(Base):
    __tablename__ = 'app_deployments'

    id = Column(u'AppDeploymentID', INTEGER(), primary_key=True)
    deployment_id = Column(
        u'DeploymentID',
        INTEGER(),
        ForeignKey('deployments.DeploymentID', ondelete='cascade'),
        nullable=False
    )
    app_id = Column(
        u'AppID',
        SMALLINT(display_width=6),
        ForeignKey('app_definitions.AppID', ondelete='cascade'),
        nullable=False
    )
    user = Column(String(length=32), nullable=False)
    status = Column(
        Enum(
            'complete',
            'incomplete',
            'inprogress',
            'invalidated',
            'validated',
        ),
        nullable=False
    )
    environment_id = Column(
        u'environment_id',
        INTEGER(),
        ForeignKey('environments.environmentID', ondelete='cascade'),
        nullable=False
    )
    realized = Column(
        TIMESTAMP(),
        nullable=False,
        server_default=func.current_timestamp()
    )

    application = relationship('AppDefinition')
    deployment = relationship('Deployment')
    environment_obj = relationship('Environment')

    @hybrid_property
    def environment(self):
        return self.environment_obj.environment

    @environment.expression
    def environment(cls):
        return select(
                [Environment.environment]
            ).where(
                Environment.id == cls.environment_id
            ).correlate(cls).as_scalar()


class Deployment(Base):
    __tablename__ = 'deployments'

    id = Column(u'DeploymentID', INTEGER(), primary_key=True)
    package_id = Column(
        INTEGER(),
        ForeignKey('packages.package_id', ondelete='cascade'),
        nullable=False
    )

    app_deployments = relationship('AppDeployment')


class Environment(Base):
    __tablename__ = 'environments'

    id = Column(u'environmentID', INTEGER(), primary_key=True)
    environment = Column(String(length=15), nullable=False, unique=True)


class Package(Base):
    __tablename__ = 'packages'

    id = Column(u'package_id', INTEGER(), primary_key=True)
    pkg_name = Column(String(length=255), nullable=False)
    version = Column(String(length=63), nullable=False)
    revision = Column(String(length=63), nullable=False)

    deployments = relationship('Deployment')


def find_unvalidated_versions(time_delta, environment):
    """Find the latest deployments that are not validated in a given
       environment for a given amount of time
    """

    subq = (Session.query(Package.pkg_name, Package.version,
                          Package.revision, AppDefinition.app_type,
                          AppDeployment.environment_id,
                          AppDeployment.environment,
                          AppDeployment.realized, AppDeployment.user,
                          AppDeployment.status)
                   .join(Deployment)
                   .join(AppDeployment)
                   .join(AppDefinition)
                   .filter(AppDeployment.status!='invalidated')
                   .filter(AppDeployment.environment==environment)
                   .order_by(AppDeployment.realized.desc())
                   .subquery(name='t_ordered'))

    return (Session.query(subq.c.pkg_name, subq.c.version, subq.c.revision,
                          subq.c.appType, subq.c.environment_id,
                          subq.c.realized, subq.c.user, subq.c.status)
                   .group_by(subq.c.appType, subq.c.environment_id,
                             subq.c.pkg_name)
                   .having(and_(subq.c.status.like('%complete'),
                                func.unix_timestamp(subq.c.realized) <
                                func.unix_timestamp(func.now()) - time_delta))
                   .all())

Reply via email to