# Recommended naming convensions to make possible to autogenerate migrations # See http://alembic.zzzcomputing.com/en/latest/naming.html NAMING_CONVENSION = { "ix": 'ix_%(column_0_label)s', 'uq': 'uq_%(table_name)s_%(column_0_name)s', 'ck': 'ck_%(table_name)s_%(column_0_name)s', 'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s', 'pk': 'pk_%(table_name)s' } # Prevent constant migrations from sa.DateTime(timezone=True) to mysql.DATETIME(). DATETIME_TYPE = DateTime(timezone=True) DATETIME_TYPE = DATETIME_TYPE.with_variant(mysql.DATETIME(), 'mysql') # mysql boolean BOOLEAN_TYPE = Boolean() BOOLEAN_TYPE = BOOLEAN_TYPE.with_variant(mysql.TINYINT(display_width=1), 'mysql') # Unsigned integer. UNSIGNEDINT_TYPE = Integer() UNSIGNEDINT_TYPE = UNSIGNEDINT_TYPE.with_variant(mysql.INTEGER(unsigned=True), 'mysql') # Unsigned integer. UNSIGNEDSMALLINT_TYPE = SmallInteger() UNSIGNEDSMALLINT_TYPE = UNSIGNEDSMALLINT_TYPE.with_variant( mysql.SMALLINT(unsigned=True), 'mysql') # The “pre ping” feature will normally emit SQL equivalent to “SELECT 1” each time a connection is checked out # from the pool; if an error is raised that is detected as a “disconnect” situation, the connection will be # immediately recycled, and all other pooled connections older than the current time are invalidated, so that
class DistributionCode(Audit, VersionedModel): # pylint:disable=too-many-instance-attributes """This class manages all of the base data about distribution code. Distribution code holds details on the codes for how the collected payment is going to be distributed. """ __tablename__ = 'distribution_codes' distribution_code_id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(50), nullable=True) client = db.Column(db.String(50), nullable=True) responsibility_centre = db.Column(db.String(50), nullable=True) service_line = db.Column(db.String(50), nullable=True) stob = db.Column(db.String(50), nullable=True) project_code = db.Column(db.String(50), nullable=True) start_date = db.Column(db.Date, default=date.today(), nullable=False) end_date = db.Column(db.Date, default=None, nullable=True) stop_ejv = db.Column('stop_ejv', Boolean(), default=False) service_fee_distribution_code_id = db.Column( db.Integer, ForeignKey('distribution_codes.distribution_code_id'), nullable=True) disbursement_distribution_code_id = db.Column( db.Integer, ForeignKey('distribution_codes.distribution_code_id'), nullable=True) # account id for distribution codes for gov account. None for distribution codes for filing types account_id = db.Column(db.Integer, ForeignKey('payment_accounts.id'), nullable=True, index=True) @classmethod def find_all(cls, include_gov_account_gl_codes: bool = False): """Find all distribution codes.""" valid_date = date.today() query = cls.query.filter(DistributionCode.start_date <= valid_date). \ filter((DistributionCode.end_date.is_(None)) | (DistributionCode.end_date >= valid_date)). \ order_by(DistributionCode.name.asc()) query = query.filter(DistributionCode.account_id.isnot(None)) if include_gov_account_gl_codes \ else query.filter(DistributionCode.account_id.is_(None)) return query.all() @classmethod def find_by_service_fee_distribution_id(cls, service_fee_distribution_code_id): """Find by service fee distribution id.""" return cls.query.filter( DistributionCode.service_fee_distribution_code_id == service_fee_distribution_code_id).all() @classmethod def find_by_active_for_fee_schedule(cls, fee_schedule_id: int): """Return active distribution for fee schedule.""" valid_date = date.today() query = db.session.query(DistributionCode). \ join(DistributionCodeLink). \ filter(DistributionCodeLink.fee_schedule_id == fee_schedule_id). \ filter(DistributionCode.start_date <= valid_date). \ filter((DistributionCode.end_date.is_(None)) | (DistributionCode.end_date >= valid_date)) distribution_code = query.one_or_none() return distribution_code @classmethod def find_by_active_for_account(cls, account_id: int): """Return active distribution for account.""" valid_date = date.today() query = db.session.query(DistributionCode). \ filter(DistributionCode.account_id == account_id). \ filter(DistributionCode.start_date <= valid_date). \ filter((DistributionCode.end_date.is_(None)) | (DistributionCode.end_date >= valid_date)) distribution_code = query.one_or_none() return distribution_code
def test_alter_column_schema_schema_type_unnamed(): context = op_fixture('mssql') op.alter_column("t", "c", type_=Boolean(), schema='foo') context.assert_('ALTER TABLE foo.t ALTER COLUMN c BIT', 'ALTER TABLE foo.t ADD CHECK (c IN (0, 1))')
Column('hash', String(length=64), nullable=False, unique=True), Column('owner_key', String(length=128), nullable=False), Column('signature', String(length=128), nullable=True), Column('path', String(length=512), nullable=True), Column('timestamp', TIMESTAMP, nullable=False, default=datetime.utcnow), Column('size', Integer()), Column('client_contract_address', String(length=128), nullable=False), Column('my_monitoring_number', Integer(), default=ColumnDefault(0)), Column('status', String(length=32), nullable=False, default=ColumnDefault('active')), Column('no_deposit_counter', Integer(), default=ColumnDefault(0)), Column('replacing_host_address', String(length=128)), Column('send_data_to_contract_after_uploading_body', Boolean(), default=ColumnDefault(False)), ) hoster_file_m2m = Table( 'hoster_files_m2m', meta, Column('file_id', Integer(), ForeignKey('hoster_files.id'), primary_key=True, nullable=False), Column('host_id', Integer(), ForeignKey('hosts.id'), primary_key=True,
class BusinessTypeFileType(DBBASE): """ Relationship table between :class:`autonomie.models.project.types.BusinessType` and :class:`autonomie.models.files.FileType` """ __tablename__ = "business_type_file_type" __table_args__ = default_table_args file_type_id = Column(ForeignKey("file_type.id"), primary_key=True) business_type_id = Column(ForeignKey("business_type.id"), primary_key=True) # estimation/invoice/cancelinvoice/business doctype = Column(String(14), primary_key=True) file_type = relationship( "FileType", backref=backref("business_type_rel", cascade='all, delete-orphan'), ) business_type = relationship( "BusinessType", backref=backref("file_type_rel", cascade='all, delete-orphan'), ) # project_mandatory / business_mandatory / mandatory / optionnal / # recommended requirement_type = Column( String(20), default=False, info={ 'colanderalchemy': { 'title': u"Obligatoire ?", } }, ) validation = Column( Boolean(), default=False, info={ "colanderalchemy": { "title": u"Validation équipe d'appui ?", "description": u"Ce document doit-il être validé par l'équipe " u"d'appui ?" } } ) PROJECT_MANDATORY = 'project_mandatory' BUSINESS_MANDATORY = "business_mandatory" MANDATORY = "mandatory" RECOMMENDED = "recommended" OPTIONNAL = "optionnal" # requirement qui implique un indicateur de statut STATUS_REQUIREMENT_TYPES = ( PROJECT_MANDATORY, BUSINESS_MANDATORY, MANDATORY, RECOMMENDED, ) @classmethod def get_file_requirements(cls, business_type_id, doctype, mandatory=False): """ Collect file requirements related to a given business_type """ query = cls.query().filter_by(business_type_id=business_type_id) query = query.filter_by(doctype=doctype) if mandatory: query = query.filter( cls.requirement_type.in_(cls.STATUS_REQUIREMENT_TYPES) ) return query @classmethod def get_file_type_options(cls, business_type_id, doctype): """ Collect FileTypes associated to (business_type_id, doctype) :param int business_type_id: The business type id :param str doctype: One of the available doctypes :returns: A :class:`sqlalchemy.orm.Query` """ id_query = cls.query('file_type_id') id_query = id_query.filter_by(business_type_id=business_type_id) id_query = id_query.filter_by(doctype=doctype) ids = [i[0] for i in id_query] result = [] if ids is not None: from autonomie.models.files import FileType query = FileType.query().options(load_only('id', 'label')).filter( FileType.id.in_(ids) ) result = query.all() return result
def test_alter_column_schema_type_existing_type_no_new_type(): context = op_fixture('postgresql') op.alter_column("t", "c", nullable=False, existing_type=Boolean()) context.assert_( 'ALTER TABLE t ALTER COLUMN c SET NOT NULL' )
class Member(Base): ''' Data model ''' __tablename__ = 'members' mem_id = Column(Integer, primary_key=True) mem_fname = Column(Unicode(255), default='') mem_prefix = Column(Unicode(255), default='') mem_lname = Column(Unicode(255), default='') mem_email = Column(Unicode(255), default='') mem_street = Column(Unicode(255), default='') mem_house = Column(Integer, default=0) mem_flatno = Column(Unicode(255), default='') mem_city = Column(Unicode(255), default='') mem_postcode = Column(Unicode(6), default='') mem_home_tel = Column(Unicode(25), default='') mem_work_tel = Column(Unicode(255), default='') mem_mobile = Column(Unicode(25), default='') mem_enc_pwd = Column(Unicode(255), default='') mem_pwd_url = Column(Unicode(255), default='') # tracking mem_cookie = Column(Unicode(255), default='') mem_ip = Column(Unicode(255), default='') # admin-editable mem_active = Column(Boolean(), default=True) mem_membership_paid = Column(Boolean(), default=False) mem_admin = Column(Boolean(), default=False) mem_adm_adj = Column(Boolean(), default=False) mem_adm_comment = Column(Unicode(255), default='') # unused fields - TODO: do we need them? mem_bank_no = Column(Unicode(255), default='') mem_household_size = Column(Integer, default=0) __acl__ = [(Allow, 'group:admins', ('view', 'edit')), (Allow, 'group:this-member', ('view', 'edit')), (Allow, 'group:members', ('view')), DENY_ALL] def __init__(self, request=None, fname='', prefix='', lname=''): ''' receiving request makes this class a factory for views ''' self.mem_active = True self.exists = False self.mem_fname = fname self.mem_prefix = prefix self.mem_lname = lname def __repr__(self): return self.fullname @property def fullname(self): return "{} {} {}".format(self.mem_fname or '', self.mem_prefix or '', self.mem_lname or '') def addr_street(self): return "{} {}{}".format(self.mem_street, self.mem_house, self.mem_flatno) def addr_city(self): return "{} {}".format(self.mem_postcode, self.mem_city) def validate(self): ''' checks on address, bank account, ... ''' # check missing fields missing = [] for f in ('mem_fname', 'mem_lname', 'mem_email'): if not f in self.__dict__ or self.__dict__[f] == '': missing.append(f) if len(missing) > 0: raise VokoValidationError('We still require you to fill in: %s'\ % ', '.join([m[4:] for m in missing])) self.validate_email() # also check unique constraint on email address here for nicer error msg session = DBSession() members = session.query(Member)\ .filter(Member.mem_email == self.mem_email).all() if len(members) > 0: if not (len(members) == 1 and members[0].mem_id == self.mem_id): raise VokoValidationError('The email address already exists '\ 'for a member in the database.') # we want one telephone number, as well sd = self.__dict__ if ((not 'mem_home_tel' in sd and not 'mem_work_tel' in sd and not 'mem_mobile' in sd) or (self.mem_home_tel == "" and self.mem_work_tel == "" and self.mem_mobile == "")): raise VokoValidationError('Please specify at least one telephone '\ 'number.') # check postcode if self.mem_postcode and len(self.mem_postcode) > 0\ and not (self.mem_postcode[:4].isdigit()\ and self.mem_postcode[-2:].isalpha()): raise VokoValidationError('The postcode does not seem to be'\ ' valid (should be NNNNLL, where N=number and L=letter).') # check bank no if self.mem_bank_no: bank_no_clean = self.mem_bank_no.replace(' ', '').replace('-', '') if not len(bank_no_clean) in [0, 7, 8, 9]: # length of 8 is legacy data raise VokoValidationError('Bank number needs to consist of 7 '\ '(postbank) or 9 numbers.') if len(bank_no_clean) > 0 and not bank_no_clean.isdigit(): raise VokoValidationError('Bank number needs to consist of '\ 'only numbers.') # household size if self.mem_household_size is None or self.mem_household_size < 1: raise VokoValidationError('Please specify how many people live '\ 'in the household.') def validate_email(self): ''' check email ''' # check general form: a valid local name + @ + some host # (for local name, see http://en.wikipedia.org/wiki/Email_address#Local_part) if not re.match('[A-Za-z0-9\-\_\.\+\$\%\#\&\*\/\=\?\{\}\|\~]+@[^@]+', self.mem_email): raise VokoValidationError('The email address does not '\ 'seem to be valid.') # check host host = re.findall('[^@]+', self.mem_email)[1] try: # dns.resolver throws an exception when it can't find a mail (MX) # host. The point at the end makes it not try to append domains _ = dns.resolver.query('{}.'.format(host), 'MX') except: raise VokoValidationError('The host {} is not known in the DNS'\ ' system as a mail server.'\ ' Is it spelled correctly?'.format(host)) def validate_pwd(self, req): ''' Check request on password(s), and also check if it is long enough ''' if not 'pwd1' in req.params: raise VokoValidationError('Please specify a password.') if not 'pwd2' in req.params: raise VokoValidationError('Please confirm password.') if not req.params['pwd2'] == req.params['pwd1']: raise VokoValidationError('Passwords do not match.') if not 6 <= len(req.params['pwd1']) <= 30: raise VokoValidationError('The password should be between '\ '6 and 30 characters long.') @property def balance(self): ''' returns the account balance''' balance = 0 for t in self.transactions: balance += t.amount return balance
def test_alter_column_schema_schema_type_named(self): context = op_fixture('mssql', native_boolean=False) op.alter_column("t", "c", type_=Boolean(name="xyz"), schema='foo') context.assert_( 'ALTER TABLE foo.t ALTER COLUMN c BIT', 'ALTER TABLE foo.t ADD CONSTRAINT xyz CHECK (c IN (0, 1))')
class User(Base): id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, index=True) hashed_password = Column(String) is_active = Column(Boolean(), default=True)
class User(BaseModel): email = Column(String, unique=True, index=True) hashed_password = Column(String) is_active = Column(Boolean, default=True) is_superuser = Column(Boolean(), default=False) articles = relationship("Article", back_populates="owner")
def test_alter_column_schema_type_unnamed(self): context = op_fixture('mssql', native_boolean=False) op.alter_column("t", "c", type_=Boolean()) context.assert_('ALTER TABLE t ALTER COLUMN c BIT', 'ALTER TABLE t ADD CHECK (c IN (0, 1))')
class MUC(Base): __tablename__ = "muc" address = Column( "address", JID(), primary_key=True, nullable=False, ) service_domain_id = Column( "domain_id", Integer(), ForeignKey(Domain.id_), nullable=False, ) last_seen = Column( "last_seen", DateTime(), nullable=True, ) nusers = Column( "nusers", Integer(), nullable=True, ) nusers_moving_average = Column( "nusers_moving_average", Float(), nullable=True, ) moving_average_last_update = Column( "moving_average_last_update", DateTime(), nullable=True, ) is_open = Column( "is_open", Boolean(), nullable=False, ) is_hidden = Column( "is_hidden", Boolean(), nullable=False, default=False, ) was_kicked = Column( "was_kicked", Boolean(), nullable=False, ) anonymity_mode = Column( "anonymity_mode", SimpleEnum(AnonymityMode), nullable=True, ) @classmethod def get(cls, session, address): try: return session.query(cls).filter(cls.address == address).one() except sqlalchemy.orm.exc.NoResultFound: return None
class CiCommit(Base): """Refers to a git commit of the code on which we ran some SLAM performance test (likely in the CI). We keep some useful data in the database, but for the rest it used gitpython. """ __tablename__ = 'ci_commits' id = Column(Integer(), primary_key=True) hexsha = Column(String(), index=True, nullable=False) project_id = Column(String(), ForeignKey('projects.id'), index=True) project = relationship("Project", back_populates="ci_commits") __table_args__ = (UniqueConstraint('project_id', 'hexsha', name='_project_hexsha'), ) data = Column(JSON(), default={}) authored_datetime = Column(DateTime(timezone=True), index=True) committer_name = Column(String(), index=True) message = Column(String()) # We use as branch the first branch that the commit was seen on, or the project's reference branch if it was used. # TODO: we should also store the tags we witnessed the commit used with. branch = Column(String(), index=True) # first added as.. we ignore tags? # In the end there having a commit's parents is not all that useful for QA-Board: # not all commits are used for runs: e.g. CI runs only on pushed commits, so # that info is not enough to reconstruct the commit graph. # Right now we don't display parents in the web application, so we also remove it from the API. # Instead of JSON, we could use an Array of String instead, so that we can search for descendents. But do we really need it? parents = Column(JSON()) commit_dir_override = Column(String()) # Right now we don't really use this field, it's always "git". # The client uses "local" in case there is no git info, but # even then it doesn't send the information! commit_type = Column(String(), default='git') batches = relationship( "Batch", back_populates="ci_commit", cascade="all, delete-orphan", order_by=Batch.created_date, ) latest_output_datetime = Column(DateTime(timezone=True)) deleted = Column(Boolean(), default=False) @orm.reconstructor def init_on_load(self): if not self.data: self.data = {} def get_or_create_batch(self, label): matching_batches = [b for b in self.batches if b.label == label] if matching_batches: return matching_batches[0] return Batch(ci_commit=self, label=label) @property def ci_batch(self): return self.get_or_create_batch('default') @property def authored_date(self): return self.authored_datetime.date() @property def commit_dir(self): """Returns the folder in all the data for this commit is stored.""" if self.commit_dir_override is not None: return Path(self.commit_dir_override) committer_name = self.committer_name if self.committer_name else "unknown" commit_dir_name = f'{int(self.authored_datetime.timestamp())}__{committer_name}__{self.hexsha[:8]}' out = self.project.ci_directory / self.project.id_git / 'commits' / commit_dir_name if self.project.id_relative: return out / self.project.id_relative else: return out @property def repo_commit_dir(self): if self.commit_dir_override is not None: # can we do something better? return Path( self.commit_dir_override.replace(str(self.project.id_relative), "")) else: committer_name = self.committer_name if self.committer_name else "unknown" commit_dir_name = f'{int(self.authored_datetime.timestamp())}__{committer_name}__{self.hexsha[:8]}' return self.project.ci_directory / self.project.id_git / 'commits' / commit_dir_name @property def commit_dir_url(self): """The URL at which the data about this commit is stored. It's convenient.""" if self.commit_dir_override is not None: relative_path = self.commit_dir_override return quote(f'/s{relative_path}') return quote(f"/s{self.commit_dir}".replace("/home/arthurf/ci", "")) @property def repo_commit_dir_url(self): """The URL at which the data about this commit is stored. It's convenient.""" if self.commit_dir_override is not None: relative_path = self.commit_dir_override return quote(f'/s{relative_path}') return quote(f"/s{self.repo_commit_dir}") def __repr__(self): outputs = f"ci_batch.outputs={len(self.ci_batch.outputs)}" if len( self.ci_batch.outputs) else '' branch = re.sub('origin/', '', self.branch) return f"<CiCommit project='{self.project.id}' hexsha='{self.hexsha[:8]}' branch='{branch}' {outputs}>" def __init__(self, hexsha, *, project, branch=None, message=None, parents=None, authored_datetime=None, committer_name=None, commit_type='git'): self.hexsha = hexsha self.project = project self.branch = branch self.message = message if message else '<NA>' self.parents = parents self.authored_datetime = authored_datetime self.committer_name = committer_name if committer_name else 'unknown' self.commit_type = commit_type self.latest_output_datetime = authored_datetime def delete(self, ignore=None, keep=None, dryrun=False): """ Delete the commit's artifacts, and mark it as delete. NOTE: We don't touch batches/outputs, you have to deal with them yourself. See hard_delete() in api/webhooks.py and clean.py """ manifest_dir = self.commit_dir / 'manifests' delete_errors = False if manifest_dir.exists(): for manifest in manifest_dir.iterdir(): if keep and manifest in keep: continue print(f' ...delete artifacts: {manifest.name}') has_error = False try: with manifest.open() as f: files = json.load(f) except: delete_errors = True continue for file in files.keys(): if keep and file in keep: continue if ignore: if any([fnmatch.fnmatch(file, i) for i in ignore]): continue print(f'{self.commit_dir / file}') if not dryrun: try: (self.commit_dir / file).unlink() except: has_error = True print( f"WARNING: Could not remove: {self.commit_dir / file}" ) if not has_error: manifest.unlink() delete_errors = delete_errors or has_error if not delete_errors: self.deleted = True @staticmethod def get_or_create(session, hexsha, project_id, data=None): try: ci_commit = (session.query(CiCommit).filter( CiCommit.project_id == project_id, CiCommit.hexsha.startswith(hexsha), ).one()) except NoResultFound: # FIXME: if not cimplete hash. fallback git... ? or raise error? (then docs: ask full 32 hash..) or symetrics if hexsha.strtswith(query) try: from backend.models import Project project = Project.get_or_create(session=session, id=project_id) if data and data.get('qaboard_config'): is_initialization = not project.data or 'qatools_config' not in data reference_branch = data["qaboard_config"]['project'].get( 'reference_branch', 'master') is_reference = data.get( "commit_branch") == reference_branch if is_initialization or is_reference: # FIXME: We put in Project.data.git the content of # https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#push-events # FIXME: We should really have Project.data.gitlab/github/... if "git" not in project.data: project.data["git"] = {} if "path_with_namespace" not in project.data[ "git"] and "name" in data["qaboard_config"].get( "project", {} ): # FIXME: it really should be Project.root # FIXME: Doesn't support updates for now... again should have .id: int, name: str, root: str... project.data["git"]["path_with_namespace"] = data[ "qaboard_config"]["project"]["name"] project.data.update( {'qatools_config': data['qaboard_config']}) if "qaboard_metrics" in data: project.data.update( {'qatools_metrics': data["qaboard_metrics"]}) flag_modified(project, "data") else: # For backward-compatibility we fallback to reading the data from the commit itself # But in regular use QA-Board doesn't require read rights on repositories try: git_commit = project.repo.commit(hexsha) except Exception as e: error = f'[ERROR] Could not find information on commit {hexsha}. {e}' print(error) raise ValueError(error) ci_commit = CiCommit( hexsha, project=project, commit_type='git', # we don't use anything else parents=data["commit_parents"] if (data and "commit_parents" in data) else [c.hexsha for c in git_commit.parents], message=data["commit_message"] if (data and "commit_message" in data) else git_commit.message, committer_name=data["commit_committer_name"] if (data and "commit_committer_name" in data) else git_commit.committer.name, authored_datetime=data["commit_authored_datetime"] if (data and "commit_authored_datetime" in data) else git_commit.authored_datetime, # commits belong to many branches, so this is a guess branch=data["commit_branch"] if (data and "commit_branch" in data) else find_branch( hexsha, project.repo), ) if data and data.get('qaboard_config'): ci_commit.data.update( {'qatools_config': data['qaboard_config']}) if "qaboard_metrics" in data: ci_commit.data.update( {'qatools_metrics': data['qaboard_metrics']}) flag_modified(ci_commit, "data") session.add(ci_commit) session.commit() except ValueError: error = f'[ERROR] ValueError: could not create a commit for {hexsha}' print(error) raise ValueError(error) if not ci_commit.data: ci_commit.data = {} return ci_commit def to_dict(self, with_aggregation=None, with_batches=None, with_outputs=False): users_db = get_users_per_name("") committer_avatar_url = '' if users_db and self.committer_name: name = self.committer_name.lower() if name in users_db: committer_avatar_url = users_db[name]['avatar_url'] elif name.replace('.', '') in users_db: committer_avatar_url = users_db[name.replace('.', '')]['avatar_url'] elif name.replace(' ', '') in users_db: committer_avatar_url = users_db[name.replace(' ', '')]['avatar_url'] else: name_hash = md5(name.encode('utf8')).hexdigest() committer_avatar_url = f'http://gravatar.com/avatar/{name_hash}' out = { 'id': self.hexsha, # 'type': self.commit_type, 'branch': re.sub('origin/', '', self.branch), # Not used anywhere in the web application, and it's not all that useful (see earlier comment) # 'parents': [p for p in self.parents] if self.parents else [], 'message': self.message, 'committer_name': self.committer_name, 'committer_avatar_url': committer_avatar_url, 'authored_datetime': self.authored_datetime.isoformat(), 'authored_date': self.authored_date.isoformat(), 'latest_output_datetime': self.latest_output_datetime.isoformat() if self.latest_output_datetime else None, 'deleted': self.deleted, "data": self.data if with_outputs else None, 'commit_dir_url': str(self.commit_dir_url), 'repo_commit_dir_url': str(self.repo_commit_dir_url), 'batches': { b.label: b.to_dict(with_outputs=with_outputs, with_aggregation=with_aggregation) for b in self.batches if not with_batches or b.label in with_batches }, } if with_outputs: out["data"] = self.data return out
from sqlalchemy.dialects.postgresql import ARRAY, DOUBLE_PRECISION from credovi.schema import metadata, schema from credovi.util.sqlalchemy import Vector3D # RAW CHAINS raw_chains = Table('raw_chains', metadata, Column('pdb', String(4), nullable=False), Column('assembly_serial', Integer, nullable=False, autoincrement=False), Column('entity_serial', Integer, nullable=False, autoincrement=False), Column('pdb_chain_id', String(1), nullable=False), Column('pdb_chain_asu_id', String(1), nullable=False), Column('chain_type', String(50)), Column('rotation', ARRAY(DOUBLE_PRECISION)), Column('translation', ARRAY(DOUBLE_PRECISION)), Column('is_at_identity', Boolean(create_constraint=False), DefaultClause('false'), nullable=False), schema=schema, prefixes=['unlogged']) PrimaryKeyConstraint(raw_chains.c.pdb, raw_chains.c.assembly_serial, raw_chains.c.entity_serial, deferrable=True, initially='deferred') # RAW LIGANDS raw_ligands = Table('raw_ligands', metadata, Column('pdb', String(4), nullable=False, primary_key=True), Column('assembly_serial', Integer, nullable=False, primary_key=True, autoincrement=False), Column('entity_serial', Integer, nullable=False, autoincrement=False, primary_key=True), Column('pdb_chain_id', String(1), nullable=False), Column('res_num', Integer), Column('ligand_name', String(64), nullable=False), Column('num_hvy_atoms', Integer), Column('ism', Text),
class Task(Node): """ Metadata pour une tâche (estimation, invoice) """ __tablename__ = 'task' __table_args__ = default_table_args __mapper_args__ = {'polymorphic_identity': 'task'} _autonomie_service = TaskService file_requirement_service = TaskFileRequirementService mention_service = TaskMentionService id = Column( Integer, ForeignKey('node.id'), info={'export': {'exclude': True}}, primary_key=True, ) phase_id = Column( ForeignKey('phase.id'), info={"export": {'exclude': True}}, ) status = Column( String(10), info={ 'colanderalchemy': {'title': u"Statut"}, 'export': {'exclude': True} } ) status_comment = Column( Text, info={ "colanderalchemy": {"title": u"Commentaires"}, 'export': {'exclude': True} }, default="", ) status_person_id = Column( ForeignKey('accounts.id'), info={ 'colanderalchemy': { "title": u"Dernier utilisateur à avoir modifié le document", }, "export": {'exclude': True}, }, ) status_date = Column( Date(), default=datetime.date.today, info={ 'colanderalchemy': { "title": u"Date du dernier changement de statut", }, 'export': {'exclude': True} } ) date = Column( Date(), info={"colanderalchemy": {"title": u"Date du document"}}, default=datetime.date.today ) owner_id = Column( ForeignKey('accounts.id'), info={ "export": {'exclude': True}, }, ) description = Column( Text, info={'colanderalchemy': {"title": u"Objet"}}, ) ht = Column( BigInteger(), info={ 'colanderalchemy': {"title": u"Montant HT (cache)"}, 'export': {'exclude': True}, }, default=0 ) tva = Column( BigInteger(), info={ 'colanderalchemy': {"title": u"Montant TVA (cache)"}, 'export': {'exclude': True}, }, default=0 ) ttc = Column( BigInteger(), info={ 'colanderalchemy': {"title": u"Montant TTC (cache)"}, 'export': {'exclude': True}, }, default=0 ) company_id = Column( Integer, ForeignKey('company.id'), info={ 'export': {'exclude': True}, }, ) project_id = Column( Integer, ForeignKey('project.id'), info={ 'export': {'exclude': True}, }, ) customer_id = Column( Integer, ForeignKey('customer.id'), info={ 'export': {'exclude': True}, }, ) project_index = deferred( Column( Integer, info={ 'colanderalchemy': { "title": u"Index dans le projet", }, 'export': {'exclude': True}, }, ), group='edit', ) company_index = deferred( Column( Integer, info={ 'colanderalchemy': { "title": u"Index du document à l'échelle de l'entreprise", }, 'export': {'exclude': True}, }, ), group='edit', ) official_number = Column( String(255), info={ 'colanderalchemy': { "title": u"Identifiant du document (facture/avoir)", }, 'export': {'label': u"Numéro de facture"}, }, default=None, ) internal_number = deferred( Column( String(255), default=None, info={ 'colanderalchemy': { "title": u"Identifiant du document dans la CAE", }, 'export': {'exclude': True}, } ), group='edit' ) display_units = deferred( Column( Integer, info={ 'colanderalchemy': { "title": u"Afficher le détail ?", "validator": colander.OneOf((0, 1)) }, 'export': {'exclude': True}, }, default=0 ), group='edit' ) expenses_ht = deferred( Column( BigInteger(), info={ 'colanderalchemy': {'title': u'Frais'}, 'export': {'exclude': True}, }, default=0 ), group='edit', ) address = deferred( Column( Text, default="", info={ 'colanderalchemy': {'title': u'Adresse'}, 'export': {'exclude': True}, }, ), group='edit', ) workplace = deferred( Column( Text, default='', info={ 'colanderalchemy': {'title': u"Lieu d'éxécution des travaux"}, } ) ) payment_conditions = deferred( Column( Text, info={ 'colanderalchemy': { "title": u"Conditions de paiement", }, 'export': {'exclude': True}, }, ), group='edit', ) round_floor = deferred( Column( Boolean(), default=False, info={ 'colanderalchemy': { 'exlude': True, 'title': u"Méthode d'arrondi 'à l'ancienne' ? (floor)" }, 'export': {'exclude': True}, } ), group='edit', ) business_type_id = Column(ForeignKey("business_type.id")) business_id = Column(ForeignKey("business.id")) # Organisationnal Relationships status_person = relationship( "User", primaryjoin="Task.status_person_id==User.id", backref=backref( "taskStatuses", info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ), info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ) owner = relationship( "User", primaryjoin="Task.owner_id==User.id", backref=backref( "ownedTasks", info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ), info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ) phase = relationship( "Phase", primaryjoin="Task.phase_id==Phase.id", backref=backref( "tasks", order_by='Task.date', info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ), info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ) company = relationship( "Company", primaryjoin="Task.company_id==Company.id", info={ 'colanderalchemy': {'exclude': True}, 'export': {'related_key': "name", "label": "Entreprise"}, }, ) project = relationship( "Project", primaryjoin="Task.project_id==Project.id", info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, ) customer = relationship( "Customer", primaryjoin="Customer.id==Task.customer_id", backref=backref( 'tasks', order_by='Task.date', info={ 'colanderalchemy': {'exclude': True}, "export": {'exclude': True}, }, ), info={ 'colanderalchemy': {'exclude': True}, 'export': {'related_key': 'label', 'label': u"Client"}, }, ) business_type = relationship( "BusinessType", info={'colanderalchemy': {'exclude': True}} ) business = relationship( "Business", primaryjoin="Business.id==Task.business_id", info={'colanderalchemy': {'exclude': True}} ) # Content relationships discounts = relationship( "DiscountLine", info={ 'colanderalchemy': {'title': u"Remises"}, 'export': {'exclude': True}, }, order_by='DiscountLine.tva', cascade="all, delete-orphan", back_populates='task', ) payments = relationship( "Payment", primaryjoin="Task.id==Payment.task_id", info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, }, order_by='Payment.date', cascade="all, delete-orphan", back_populates='task', ) mentions = relationship( "TaskMention", secondary=TASK_MENTION, order_by="TaskMention.order", info={'export': {'exclude': True}}, ) mandatory_mentions = relationship( "TaskMention", secondary=MANDATORY_TASK_MENTION, order_by="TaskMention.order", info={'export': {'exclude': True}}, ) line_groups = relationship( "TaskLineGroup", order_by='TaskLineGroup.order', cascade="all, delete-orphan", collection_class=ordering_list('order'), info={ 'colanderalchemy': { 'title': u"Unités d'oeuvre", "validator": colander.Length( min=1, min_err=u"Une entrée est requise" ), "missing": colander.required }, 'export': {'exclude': True}, }, primaryjoin="TaskLineGroup.task_id==Task.id", back_populates='task', ) statuses = relationship( "TaskStatus", order_by="desc(TaskStatus.status_date), desc(TaskStatus.id)", cascade="all, delete-orphan", back_populates='task', info={ 'colanderalchemy': {'exclude': True}, 'export': {'exclude': True}, } ) # Not used in latest invoices expenses = deferred( Column( BigInteger(), info={ 'export': {'exclude': True}, }, default=0 ), group='edit' ) _name_tmpl = u"Task {}" _number_tmpl = u"{s.project.code}_{s.customer.code}_T{s.project_index}\ _{s.date:%m%y}" state_manager = None def __init__(self, user, company, **kw): project = kw['project'] company_index = self._get_company_index(company) project_index = self._get_project_index(project) self.status = 'draft' self.company = company if 'customer' in kw: customer = kw['customer'] self.address = customer.full_address self.owner = user self.status_person = user self.date = datetime.date.today() self.set_numbers(company_index, project_index) for key, value in kw.items(): setattr(self, key, value) # We add a default task line group self.line_groups.append(TaskLineGroup(order=0)) def initialize_business_datas(self, business=None): """ Initialize the business datas related to this task :param obj business: instance of :class:`autonomie.models.project.business.Business` """ if business is not None: self.business = business self.file_requirement_service.populate(self) self.mention_service.populate(self) def _get_project_index(self, project): """ Return the index of the current object in the associated project :param obj project: A Project instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return -1 def _get_company_index(self, company): """ Return the index of the current object in the associated company :param obj company: A Company instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return -1 def set_numbers(self, company_index, project_index): """ Handle all attributes related to the given number :param int company_index: The index of the task in the company :param int project_index: The index of the task in its project """ if company_index is None or project_index is None: raise Exception("Indexes should not be None") self.company_index = company_index self.project_index = project_index self.internal_number = self._number_tmpl.format(s=self) self.name = self._name_tmpl.format(project_index) @property def default_line_group(self): return self.line_groups[0] def __json__(self, request): """ Return the datas used by the json renderer to represent this task """ return dict( id=self.id, name=self.name, created_at=self.created_at.isoformat(), updated_at=self.updated_at.isoformat(), phase_id=self.phase_id, status=self.status, status_comment=self.status_comment, status_person_id=self.status_person_id, # status_date=self.status_date.isoformat(), date=self.date.isoformat(), owner_id=self.owner_id, description=self.description, ht=integer_to_amount(self.ht, 5), tva=integer_to_amount(self.tva, 5), ttc=integer_to_amount(self.ttc, 5), company_id=self.company_id, project_id=self.project_id, customer_id=self.customer_id, project_index=self.project_index, company_index=self.company_index, official_number=self.official_number, internal_number=self.internal_number, display_units=self.display_units, expenses_ht=integer_to_amount(self.expenses_ht, 5), address=self.address, workplace=self.workplace, payment_conditions=self.payment_conditions, status_history=[ status.__json__(request) for status in self.statuses ], discounts=[ discount.__json__(request) for discount in self.discounts ], payments=[ payment.__json__(request) for payment in self.payments ], mentions=[mention.id for mention in self.mentions], line_groups=[ group.__json__(request) for group in self.line_groups ], attachments=[ file_.__json__(request) for file_ in self.files ], file_requirements=[ file_req.__json__(request) for file_req in self.file_requirements ] ) def set_status(self, status, request, **kw): """ set the status of a task through the state machine """ return self.state_manager.process( status, self, request, **kw ) def check_status_allowed(self, status, request, **kw): return self.state_manager.check_allowed(status, self, request) @validates('status') def change_status(self, key, status): """ fired on status change, stores a new taskstatus for each status change """ log.debug(u"# Task status change #") actual_status = self.status log.debug(u" + was {0}, becomes {1}".format(actual_status, status)) return status def get_company(self): """ Return the company owning this task """ return self.company def get_customer(self): """ Return the customer of the current task """ return self.customer def get_company_id(self): """ Return the id of the company owning this task """ return self.company.id def __repr__(self): return u"<Task status:{s.status} id:{s.id}>".format(s=self) def get_groups(self): return [group for group in self.line_groups if group.lines] @property def all_lines(self): """ Returns a list with all task lines of the current task """ result = [] for group in self.line_groups: result.extend(group.lines) return result def get_tva_objects(self): return self._autonomie_service.get_tva_objects(self) @classmethod def get_valid_tasks(cls, *args): return cls._autonomie_service.get_valid_tasks(cls, *args) @classmethod def get_waiting_estimations(cls, *args): return cls._autonomie_service.get_waiting_estimations(*args) @classmethod def get_waiting_invoices(cls, *args): return cls._autonomie_service.get_waiting_invoices(cls, *args) def gen_business(self): """ Generate a business based on this Task :returns: A new business instance :rtype: :class:`autonomie.models.project.business.Business` """ business = Business( name=self.name, project_id=self.project_id, business_type_id=self.business_type_id, ) DBSESSION().add(business) DBSESSION().flush() business.file_requirement_service.populate(business) self.business_id = business.id DBSESSION().merge(self) return business def is_training(self): return self.business_type.name == 'training'
# Import Table, Column, String, Integer, Float, Boolean from sqlalchemy from sqlalchemy import Table, Column, String, Integer, Float, Boolean # Define a new table with a name, count, amount, and valid column: data data = Table('data', metadata, Column('name', String(255)), Column('count', Integer()), Column('amount', Float()), Column('valid', Boolean()) ) # Use the metadata to create the table metadata.create_all(engine) # Print table details print(repr(engine.table_names())) # Import Table, Column, String, Integer, Float, Boolean from sqlalchemy from sqlalchemy import Table, Column, String, Integer, Float, Boolean # Define a new table with a name, count, amount, and valid column: data data = Table('data', metadata, Column('name', String(255), unique=True), Column('count', Integer(), default=1), Column('amount', Float()), Column('valid', Boolean(), default=False) ) # Use the metadata to create the table
def test_alter_column_schema_type_existing_type_no_const(): context = op_fixture('postgresql') op.alter_column("t", "c", type_=String(10), existing_type=Boolean()) context.assert_( 'ALTER TABLE t ALTER COLUMN c TYPE VARCHAR(10)' )
class Post(BaseModel, CommentMixin, ReactMixin): STATUSES = ( STATUS_UNPUBLISHED, STATUS_ONLINE ) = range(2) status = Column(SmallInteger(), default=STATUS_UNPUBLISHED) (TYPE_ARTICLE, TYPE_PAGE) = range(2) created_at = Column(DateTime, server_default=func.now(), nullable=False) title = Column(String(100), unique=True) author_id = Column(Integer()) slug = Column(String(100)) summary = Column(String(255)) can_comment = Column(Boolean(), default=True) type = Column(Integer(), default=TYPE_ARTICLE) pageview = Column(Integer(), default=0) kind = config.K_POST @cache(MC_KEY_RELATED % ('{self.id}', '{limit}')) async def get_related(self, limit: int=4): tag_ids = [tag.id for tag in await self.tags] if not tag_ids: return [] post_ids = set([ item['post_id'] for item in await PostTag.async_in('tag_id', tag_ids)]) post_ids -= set([self.id]) if not post_ids: return [] related_posts = [ Post(**p) for p in await Post.async_in('id', post_ids) ] return related_posts[:limit] if len(related_posts) >= limit else related_posts @classmethod async def acreate(cls, **kwargs): tags = kwargs.pop('tags', []) content = kwargs.pop('content') obj_id = await super().acreate(**kwargs) kwargs['id'] = obj_id if tags: try: await PostTag.update_multi(obj_id, tags) except: await Post.adelete(id=obj_id) return obj = cls(**(await cls.async_first(id=obj_id))) await obj.set_content(content) return obj async def update_tags(self, tagnames): if tagnames: await PostTag.update_multi(self.id, tagnames) return True @property @cache(MC_KEY_TAGS_BY_POST_ID % ('{self.id}')) async def tags(self): pts = await PostTag.async_filter(post_id=self.id) if not pts: return [] ids = [item['tag_id'] for item in pts] tags = await Tag.async_in('id', ids) tags = [Tag(**t) for t in tags] return tags @property async def author(self): print('user_id', self.author_id) rv = await User.cache(id=self.author_id) return {'name': rv['name'], 'id': self.author_id, 'avatar': rv['avatar']} @property def is_page(self): return self.type == self.TYPE_PAGE @property def preview_url(self): return f'/{self.__class__.__name__.lower()}/{self.id}/preview' async def set_content(self, content): return await self.set_props_by_key('content', content) async def asave(self, *args, **kwargs): content = kwargs.pop('content', None) if content is not None: await self.set_content('content', content) return await super().asave(*args, **kwargs) @property async def content(self): rv = await self.get_props_by_key('content') if rv: return rv.decode('utf-8') @classmethod @cache(MC_KEY_POST_BY_SLUG % '{slug}') async def get_by_slug(cls, slug): return await cls.async_first(slug=slug) @classmethod @cache(MC_KEY_ALL_POSTS % '{with_page}') async def get_all(cls, with_page=True): if with_page: posts = await Post.async_filter(status=Post.STATUS_ONLINE) else: posts = await Post.async_filter(status=Post.STATUS_ONLINE, type=Post.TYPE_ARTICLE) return sorted(posts, key=lambda p: p['created_at'], reverse=True) @property def url(self): if self.is_page: return f'/page/{self.slug}' return f'/post/{getattr(self, config.PERMALINK_TYPE) or self.id}/' @property async def html_content(self): content = await self.content if not content: return '' return markdown(content) @property async def excerpt(self): if self.summary: return self.summary s = MLStripper() s.feed(await self.html_content) return trunc_utf8(BQ_REGEX.sub('', s.get_data()).replace('\n', ''), 100) @property async def toc(self): content = await self.content if not content: return '' toc.reset_toc() toc_md.parse(content) return toc.render_toc(level=4) @classmethod async def cache(cls, ident): if str(ident).isdigit(): return await super().cache(id=ident) return await cls.get_by_slug(ident) async def clear_mc(self): print('Clear POst MC', self.created_at) try: keys = [ MC_KEY_FEED, MC_KEY_SITEMAP, MC_KEY_SEARCH, MC_KEY_ARCHIVES, MC_KEY_TAGS, MC_KEY_RELATED % (self.id, 4), MC_KEY_POST_BY_SLUG % self.slug, MC_KEY_ARCHIVE % self.created_at.year ] except: import traceback traceback.print_exc() for i in [True, False]: keys.append(MC_KEY_ALL_POSTS % i) for tag in await self.tags: keys.append(MC_KEY_TAG % tag.id) await clear_mc(*keys) async def incr_pageview(self, increment=1): redis = await self.redis try: await redis.sadd(RK_ALL_POST_IDS,self.id) await redis.sadd(RK_VISITED_POST_IDS, self.id) return await redis.hincrby(RK_PAGEVIEW.format(self.id), PAGEVIEW_FIELD, increment) except: return self.pageview @property async def pageview_(self): try: return int(await (await self.redis).hget( RK_PAGEVIEW.format(self.id), PAGEVIEW_FIELD) or 0 ) except RedisError: return self.pageview
def upgrade(migrate_engine): meta = MetaData() meta.bind = migrate_engine volumes = Table('volumes', meta, autoload=True) # # New Tables # volume_types = Table( 'volume_types', meta, Column('created_at', DateTime(timezone=False)), Column('updated_at', DateTime(timezone=False)), Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), Column('name', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False), unique=True)) volume_type_extra_specs_table = Table( 'volume_type_extra_specs', meta, Column('created_at', DateTime(timezone=False)), Column('updated_at', DateTime(timezone=False)), Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), Column('volume_type_id', Integer(), ForeignKey('volume_types.id'), nullable=False), Column( 'key', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False)), Column( 'value', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False))) volume_metadata_table = Table( 'volume_metadata', meta, Column('created_at', DateTime(timezone=False)), Column('updated_at', DateTime(timezone=False)), Column('deleted_at', DateTime(timezone=False)), Column('deleted', Boolean(create_constraint=True, name=None)), Column('id', Integer(), primary_key=True, nullable=False), Column('volume_id', Integer(), ForeignKey('volumes.id'), nullable=False), Column( 'key', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False)), Column( 'value', String(length=255, convert_unicode=False, assert_unicode=None, unicode_error=None, _warn_on_bytestring=False))) new_tables = (volume_types, volume_type_extra_specs_table, volume_metadata_table) for table in new_tables: try: table.create() except Exception: LOG.info(repr(table)) LOG.exception('Exception while creating table') raise # # New Columns # volume_type_id = Column('volume_type_id', Integer(), nullable=True) volumes.create_column(volume_type_id)
) permissions: Callable[[MetaData], Table] = lambda metadata: Table( "permissions", metadata, Column( "id", String(64), primary_key=True, nullable=False, unique=True, default=lambda: str(uuid.uuid4()), ), Column("resource", String(128), nullable=False), Column("action", Enum(model.PermissionActionEnum), nullable=False), Column("is_conditional", Boolean(), nullable=False), UniqueConstraint("resource", "action", "is_conditional"), ) roles: Callable[[MetaData], Table] = lambda metadata: Table( "roles", metadata, Column( "id", String(64), primary_key=True, nullable=False, unique=True, default=lambda: str(uuid.uuid4()), ), Column("code", String(128), nullable=False, unique=True, primary_key=True),
class UserLoginAudit(Base): __tablename__ = 'tb_user_login_audit' id = Column(Integer, primary_key=True) loginID = Column(Integer, ForeignKey('tb_user_master.id')) #foreign key attemptedTimeStamp = Column(TIMESTAMP, nullable=False) statusFlag = Column(Boolean(), nullable=False)
class Invoice(Task, InvoiceCompute): """ Invoice Model """ __tablename__ = 'invoice' __table_args__ = default_table_args __mapper_args__ = { 'polymorphic_identity': 'invoice', } id = Column(ForeignKey('task.id'), primary_key=True, info={ 'colanderalchemy': { 'exclude': True }, }) # Common with CancelInvoice financial_year = Column( Integer, info={'colanderalchemy': { 'title': u"Année fiscale de référence" }}, default=0) exported = deferred(Column( Boolean(), info={'colanderalchemy': { 'title': u"A déjà été exportée ?" }}, default=False), group="edit") # Specific to Invoice paid_status = Column( String(10), default='waiting', info={'colanderalchemy': { 'title': u'Statut de la facture', }}, ) estimation_id = Column(ForeignKey('estimation.id')) estimation = relationship( "Estimation", primaryjoin="Invoice.estimation_id==Estimation.id", info={ 'colanderalchemy': forms.EXCLUDED, 'export': { 'exclude': True }, }, ) state_manager = DEFAULT_ACTION_MANAGER['invoice'] paid_states = ('resulted', ) not_paid_states = ( 'valid', 'paid', ) valid_states = paid_states + not_paid_states _number_tmpl = u"{s.company.name} {s.date:%Y-%m} F{s.company_index}" _name_tmpl = u"Facture {0}" _deposit_name_tmpl = u"Facture d'acompte {0}" _sold_name_tmpl = u"Facture de solde {0}" def _get_project_index(self, project): """ Return the index of the current object in the associated project :param obj project: A Project instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return project.get_next_invoice_index() def _get_company_index(self, company): """ Return the index of the current object in the associated company :param obj company: A Company instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return company.get_next_invoice_index() def set_deposit_label(self): self.name = self._deposit_name_tmpl.format(self.project_index) def set_sold_label(self): self.name = self._sold_name_tmpl.format(self.project_index) def set_project(self, project): self.project = project def gen_cancelinvoice(self, user): """ Return a cancel invoice with self's informations """ cancelinvoice = CancelInvoice( user=user, company=self.company, project=self.project, customer=self.customer, phase_id=self.phase_id, address=self.address, workplace=self.workplace, description=self.description, invoice=self, expenses_ht=-1 * self.expenses_ht, financial_year=self.financial_year, display_units=self.display_units, business_type_id=self.business_type_id, business_id=self.business_id, ) cancelinvoice.line_groups = [] for group in self.line_groups: cancelinvoice.line_groups.append(group.gen_cancelinvoice_group()) order = self.get_next_row_index() for discount in self.discounts: discount_line = TaskLine( cost=discount.amount, tva=discount.tva, quantity=1, description=discount.description, order=order, unity='', ) discount_line.product_id = Product.first_by_tva_value(discount.tva) order += 1 cancelinvoice.default_line_group.lines.append(discount_line) for index, payment in enumerate(self.payments): paid_line = TaskLine( cost=math_utils.reverse_tva( payment.amount, payment.tva.value, False, ), tva=payment.tva.value, quantity=1, description=u"Paiement {0}".format(index + 1), order=order, unity='NONE', ) paid_line.product_id = Product.first_by_tva_value( payment.tva.value) order += 1 cancelinvoice.default_line_group.lines.append(paid_line) cancelinvoice.mentions = self.mentions cancelinvoice.payment_conditions = u"Réglé" return cancelinvoice def get_next_row_index(self): return len(self.default_line_group.lines) + 1 def record_payment(self, **kw): """ Record a payment for the current invoice """ resulted = kw.pop('resulted', False) if kw['amount'] > 0: payment = Payment() for key, value in kw.iteritems(): setattr(payment, key, value) logger.info(u"Amount : {0}".format(payment.amount)) self.payments.append(payment) return self.check_resulted( force_resulted=resulted, user_id=kw['user_id'], ) def check_resulted(self, force_resulted=False, user_id=None): """ Check if the invoice is resulted or not and set the appropriate status """ logger.debug(u"-> There still to pay : %s" % self.topay()) if self.topay() <= 0 or force_resulted: self.paid_status = 'resulted' elif len(self.payments) > 0 or self.cancelinvoice_amount() > 0: self.paid_status = 'paid' else: self.paid_status = 'waiting' if user_id is not None: status_record = TaskStatus(status_code=self.paid_status, status_person_id=user_id, status_comment='') self.statuses.append(status_record) return self def duplicate(self, user, **kw): """ Duplicate the current invoice Mandatory args : user The user duplicating this estimation customer project """ invoice = Invoice(user=user, company=self.company, **kw) if invoice.customer.id == self.customer_id: invoice.address = self.address invoice.workplace = self.workplace invoice.description = self.description invoice.notes = self.notes invoice.payment_conditions = self.payment_conditions invoice.display_units = self.display_units invoice.expenses_ht = self.expenses_ht invoice.financial_year = datetime.date.today().year invoice.line_groups = [] for group in self.line_groups: invoice.line_groups.append(group.duplicate()) for line in self.discounts: invoice.discounts.append(line.duplicate()) invoice.mentions = self.mentions return invoice def __repr__(self): return u"<Invoice id:{s.id}>".format(s=self) def __json__(self, request): datas = Task.__json__(self, request) datas.update( dict( financial_year=self.financial_year, exported=self.exported, estimation_id=self.estimation_id, )) return datas def is_tolate(self): """ Return True if a payment is expected since more than 45 days """ res = False if self.paid_status in ('waiting', 'paid'): today = datetime.date.today() elapsed = today - self.date if elapsed > datetime.timedelta(days=45): res = True else: res = False return res
class Instance(BASE, NovaBase): """Represents a guest vm.""" __tablename__ = 'instances' injected_files = [] id = Column(Integer, primary_key=True, autoincrement=True) @property def name(self): try: base_name = FLAGS.instance_name_template % self.id except TypeError: # Support templates like "uuid-%(uuid)s", etc. info = {} for key, value in self.iteritems(): # prevent recursion if someone specifies %(name)s # %(name)s will not be valid. if key == 'name': continue info[key] = value try: base_name = FLAGS.instance_name_template % info except KeyError: base_name = self.uuid if getattr(self, '_rescue', False): base_name += "-rescue" return base_name user_id = Column(String(255)) project_id = Column(String(255)) image_ref = Column(String(255)) kernel_id = Column(String(255)) ramdisk_id = Column(String(255)) server_name = Column(String(255)) # image_ref = Column(Integer, ForeignKey('images.id'), nullable=True) # kernel_id = Column(Integer, ForeignKey('images.id'), nullable=True) # ramdisk_id = Column(Integer, ForeignKey('images.id'), nullable=True) # ramdisk = relationship(Ramdisk, backref=backref('instances', order_by=id)) # kernel = relationship(Kernel, backref=backref('instances', order_by=id)) # project = relationship(Project, backref=backref('instances', order_by=id)) launch_index = Column(Integer) key_name = Column(String(255)) key_data = Column(Text) power_state = Column(Integer) vm_state = Column(String(255)) task_state = Column(String(255)) memory_mb = Column(Integer) vcpus = Column(Integer) root_gb = Column(Integer) ephemeral_gb = Column(Integer) hostname = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) # *not* flavor_id instance_type_id = Column(Integer) user_data = Column(Text) reservation_id = Column(String(255)) scheduled_at = Column(DateTime) launched_at = Column(DateTime) terminated_at = Column(DateTime) availability_zone = Column(String(255)) # User editable field for display in user-facing UIs display_name = Column(String(255)) display_description = Column(String(255)) # To remember on which host a instance booted. # An instance may have moved to another host by live migraiton. launched_on = Column(Text) locked = Column(Boolean) os_type = Column(String(255)) architecture = Column(String(255)) vm_mode = Column(String(255)) uuid = Column(String(36)) root_device_name = Column(String(255)) default_ephemeral_device = Column(String(255), nullable=True) default_swap_device = Column(String(255), nullable=True) config_drive = Column(String(255)) # User editable field meant to represent what ip should be used # to connect to the instance access_ip_v4 = Column(String(255)) access_ip_v6 = Column(String(255)) auto_disk_config = Column(Boolean()) progress = Column(Integer) # EC2 instance_initiated_shutdown_teminate # True: -> 'terminate' # False: -> 'stop' shutdown_terminate = Column(Boolean(), default=True, nullable=False) # EC2 disable_api_termination disable_terminate = Column(Boolean(), default=False, nullable=False) # Openstack zone name zone_name = Column(String(255))
class CancelInvoice(Task, TaskCompute): """ CancelInvoice model Could also be called negative invoice """ __tablename__ = 'cancelinvoice' __table_args__ = default_table_args __mapper_args__ = {'polymorphic_identity': 'cancelinvoice'} id = Column(Integer, ForeignKey('task.id'), primary_key=True, info={ 'colanderalchemy': { 'exclude': True }, }) # Common with Invoice financial_year = Column( Integer, info={'colanderalchemy': { 'title': u"Année fiscale de référence" }}, default=0) exported = deferred(Column( Boolean(), info={'colanderalchemy': { "title": "A déjà été exportée ?" }}, default=False), group="edit") # Specific to CancelInvoice invoice_id = Column(Integer, ForeignKey('invoice.id'), info={ 'colanderalchemy': { 'title': u"Identifiant de la facture associée", } }, default=None) invoice = relationship("Invoice", backref=backref("cancelinvoices", info={ 'colanderalchemy': forms.EXCLUDED, }), primaryjoin="CancelInvoice.invoice_id==Invoice.id", info={ 'colanderalchemy': forms.EXCLUDED, }) state_manager = DEFAULT_ACTION_MANAGER['cancelinvoice'] valid_states = ('valid', ) _number_tmpl = u"{s.company.name} {s.date:%Y-%m} A{s.company_index}" _name_tmpl = u"Avoir {0}" def _get_project_index(self, project): """ Return the index of the current object in the associated project :param obj project: A Project instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return project.get_next_invoice_index() def _get_company_index(self, company): """ Return the index of the current object in the associated company :param obj company: A Company instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return company.get_next_invoice_index() def is_tolate(self): """ Return False """ return False def __repr__(self): return u"<CancelInvoice id:{s.id}>".format(s=self) def __json__(self, request): datas = Task.__json__(self, request) datas.update( dict( invoice_id=self.invoice_id, financial_year=self.financial_year, exported=self.exported, )) return datas
class User(db.Model): __tablename__ = "users" id = Column(Integer, primary_key=True) password = Column(String(128)) active = Column(Boolean()) confirmed_at = Column(ArrowType()) username = Column(String(255), nullable=False, unique=True) email = Column(String(128), unique=True) profile_picture = Column(String(255)) roles = relationship( "Role", secondary=roles_users, passive_deletes=True, backref=db.backref("user"), lazy="dynamic", ) certificates = relationship("Certificate", backref=db.backref("user"), lazy="dynamic") pending_certificates = relationship("PendingCertificate", backref=db.backref("user"), lazy="dynamic") authorities = relationship("Authority", backref=db.backref("user"), lazy="dynamic") keys = relationship("ApiKey", backref=db.backref("user"), lazy="dynamic") logs = relationship("Log", backref=db.backref("user"), lazy="dynamic") sensitive_fields = ("password", ) def check_password(self, password): """ Hash a given password and check it against the stored value to determine it's validity. :param password: :return: """ if self.password: return bcrypt.check_password_hash(self.password, password) def hash_password(self): """ Generate the secure hash for the password. :return: """ if self.password: self.password = bcrypt.generate_password_hash( self.password).decode("utf-8") @property def is_admin(self): """ Determine if the current user has the 'admin' role associated with it. :return: """ for role in self.roles: if role.name == "admin": return True def __repr__(self): return "User(username={username})".format(username=self.username)
class Payment(DBBASE, PersistentACLMixin): """ Payment entry """ __tablename__ = 'payment' __table_args__ = default_table_args id = Column(Integer, primary_key=True) created_at = Column( DateTime(), info={'colanderalchemy': { 'exclude': True, 'title': u"Créé(e) le", }}, default=datetime.datetime.now, ) updated_at = Column(DateTime(), info={ 'colanderalchemy': { 'exclude': True, 'title': u"Mis(e) à jour le", } }, default=datetime.datetime.now, onupdate=datetime.datetime.now) mode = Column(String(50), info={'colanderalchemy': { 'title': u"Mode de paiement" }}) amount = Column( BigInteger(), info={'colanderalchemy': { "title": u"Montant" }}, ) bank_remittance_id = Column( String(255), info={ 'colanderalchemy': { 'title': u"Identifiant de remise en banque" } }, ) date = Column( DateTime(), info={'colanderalchemy': { 'title': u"Date de remise" }}, default=datetime.datetime.now, ) exported = Column(Boolean(), default=False) task_id = Column( Integer, ForeignKey('task.id', ondelete="cascade"), info={'colanderalchemy': { 'title': u"Identifiant du document" }}, ) bank_id = Column( ForeignKey('bank_account.id'), info={'colanderalchemy': { 'title': u"Compte en banque" }}, ) tva_id = Column( ForeignKey('tva.id'), info={'colanderalchemy': { 'title': u"Tva associée à ce paiement" }}, nullable=True) user_id = Column( ForeignKey('accounts.id'), info={'colanderalchemy': { 'title': u"Utilisateur" }}, ) user = relationship( "User", info={'colanderalchemy': { 'exclude': True }}, ) bank = relationship("BankAccount", back_populates='payments', info={'colanderalchemy': { 'exclude': True }}) tva = relationship("Tva", info={'colanderalchemy': {'exclude': True}}) task = relationship( "Task", primaryjoin="Task.id==Payment.task_id", ) # Formatting precision precision = 5 # Usefull aliases @property def invoice(self): return self.task @property def parent(self): return self.task # Simple function def get_amount(self): return self.amount def __json__(self, request): """ Build a Json representation of this object :rtype: dict """ return dict( id=self.id, created_at=self.created_at, updated_at=self.updated_at, mode=self.mode, amount=math_utils.integer_to_amount(self.amount, 5), bank_remittance_id=self.bank_remittance_id, date=self.date, exporter=self.exported, task_id=self.task_id, bank_id=self.bank_id, tva_id=self.tva_id, user_id=self.user_id, ) def __unicode__(self): return u"<Payment id:{s.id} task_id:{s.task_id} amount:{s.amount}\ mode:{s.mode} date:{s.date}".format(s=self)
class User(db.Model, CRUDMixin, UserMixin): """ A user of the app. """ __tablename__ = "users" user_id = Column(Integer, primary_key=True) username = Column(UnicodeText, unique=True, nullable=False) email = Column(UnicodeText, unique=True, nullable=False) salt = Column(UnicodeText, nullable=True) password = Column(UnicodeText, nullable=True) created_at = Column(DateTime, nullable=False, default=dt.datetime.utcnow) first_name = Column(UnicodeText, nullable=True) last_name = Column(UnicodeText, nullable=True) active = Column(Boolean(), default=False) is_admin = Column(Boolean(), default=False) api_token = Column(UnicodeText, nullable=True) reports = relationship("Report", back_populates="user") uploads = relationship("Upload", back_populates="user") roles = relationship("Role", back_populates="user") filters = relationship("SampleFilter", back_populates="user") favourite_plots = relationship("PlotFavourite", back_populates="user") dashboards = relationship("Dashboard", back_populates="user") def __init__(self, password=None, **kwargs): """ Create instance. """ db.Model.__init__(self, **kwargs) # Config adjusts the default active status if "active" not in kwargs: self.active = not current_app.config["USER_REGISTRATION_APPROVAL"] self.salt = getrandstr(rng, digits + letters, 80) self.api_token = getrandstr(rng, digits + letters, 80) if password: self.set_password(password) else: self.password = None def enforce_admin(self): """ Enforce that the first user is an active admin. This is included as a method that isn't automatically called, because there are cases where we don't want this behaviour to happen, such as during testing. """ if db.session.query(User).count() == 0: self.is_admin = True self.active = True # users = User.__table__ # if target.user_id == 1: # connection.execute(users.update().where(users.c.user_id == 1).values(is_admin=True, active=True)) @hybrid_property def full_name(self): return self.first_name + " " + self.last_name def reset_password(self): password = getrandstr(rng, digits + letters, 10) self.set_password(password) return password def set_password(self, password): """ Set password. """ self.password = argon2.using(rounds=4).hash(password + self.salt) def check_password(self, value): """ Check password. """ return argon2.verify(value + self.salt, self.password) def is_authenticated(self): return True def is_active(self): return self.active def get_id(self): # must return unicode return str(self.user_id) def __repr__(self): """ Represent instance as a unique string. """ return "<User({username!r})>".format(username=self.username)
def test_render_literal_bool(self): self._literal_round_trip(Boolean(), [True, False], [True, False])
def test_alter_column_schema_type_named(): context = op_fixture('mssql') op.alter_column("t", "c", type_=Boolean(name="xyz")) context.assert_('ALTER TABLE t ALTER COLUMN c BIT', 'ALTER TABLE t ADD CONSTRAINT xyz CHECK (c IN (0, 1))')
accounts_gpgkey = Table( "accounts_gpgkey", db.metadata, Column("id", Integer(), primary_key=True, nullable=False), Column( "user_id", Integer(), ForeignKey( "accounts_user.id", deferrable=True, initially="DEFERRED", ), nullable=False, ), Column("key_id", CIText(), nullable=False), Column("verified", Boolean(), nullable=False), UniqueConstraint("key_id", name="accounts_gpgkey_key_id_key"), CheckConstraint( "key_id ~* '^[A-F0-9]{8}$'::citext", name="accounts_gpgkey_valid_key_id", ), ) Index("accounts_gpgkey_user_id", accounts_gpgkey.c.user_id) browse_tally = Table( "browse_tally", db.metadata, Column("trove_id", Integer(), primary_key=True, nullable=False), Column("tally", Integer()), )