class CounterStat(db.Model, helpers.Serializer): """ Generic store for simple statistics. """ name = db.Column(db.String(127), primary_key=True) counter_type = db.Column(db.String(30)) counter = db.Column(db.Integer, default=0, server_default="0")
class MockChroot(db.Model, helpers.Serializer): """ Representation of mock chroot """ id = db.Column(db.Integer, primary_key=True) # fedora/epel/..., mandatory os_release = db.Column(db.String(50), nullable=False) # 18/rawhide/..., optional (mock chroot doesn"t need to have this) os_version = db.Column(db.String(50), nullable=False) # x86_64/i686/..., mandatory arch = db.Column(db.String(50), nullable=False) is_active = db.Column(db.Boolean, default=True) @property def name(self): """ Textual representation of name of this chroot """ return "{}-{}-{}".format(self.os_release, self.os_version, self.arch) @property def name_release(self): """ Textual representation of name of this or release """ return "{}-{}".format(self.os_release, self.os_version) @property def name_release_human(self): """ Textual representation of name of this or release """ return "{} {}".format(self.os_release, self.os_version) @property def os(self): """ Textual representation of the operating system name """ return "{0} {1}".format(self.os_release, self.os_version) @property def serializable_attributes(self): attr_list = super(MockChroot, self).serializable_attributes attr_list.extend(["name", "os"]) return attr_list
class Krb5Login(db.Model, helpers.Serializer): """ Represents additional user information for kerberos authentication. """ __tablename__ = "krb5_login" # FK to User table user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) # 'string' from 'copr.conf' from KRB5_LOGIN[string] config_name = db.Column(db.String(30), nullable=False, primary_key=True) # krb's primary, i.e. 'username' from '*****@*****.**' primary = db.Column(db.String(80), nullable=False, primary_key=True) user = db.relationship("User", backref=db.backref("krb5_logins"))
class Group(db.Model, helpers.Serializer): """ Represents FAS groups and their aliases in Copr """ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(127)) # TODO: add unique=True fas_name = db.Column(db.String(127)) @property def at_name(self): return u"@{}".format(self.name) def __str__(self): return self.__unicode__() def __unicode__(self): return "{} (fas: {})".format(self.name, self.fas_name)
class Action(db.Model, helpers.Serializer): """ Representation of a custom action that needs backends cooperation/admin attention/... """ id = db.Column(db.Integer, primary_key=True) # delete, rename, ...; see helpers.ActionTypeEnum action_type = db.Column(db.Integer, nullable=False) # copr, ...; downcase name of class of modified object object_type = db.Column(db.String(20)) # id of the modified object object_id = db.Column(db.Integer) # old and new values of the changed property old_value = db.Column(db.String(255)) new_value = db.Column(db.String(255)) # additional data data = db.Column(db.Text) # result of the action, see helpers.BackendResultEnum result = db.Column(db.Integer, default=helpers.BackendResultEnum("waiting")) # optional message from the backend/whatever message = db.Column(db.Text) # time created as returned by int(time.time()) created_on = db.Column(db.Integer) # time ended as returned by int(time.time()) ended_on = db.Column(db.Integer) def __str__(self): return self.__unicode__() def __unicode__(self): if self.action_type == helpers.ActionTypeEnum("delete"): return "Deleting {0} {1}".format(self.object_type, self.old_value) elif self.action_type == helpers.ActionTypeEnum("rename"): return "Renaming {0} from {1} to {2}.".format( self.object_type, self.old_value, self.new_value) elif self.action_type == helpers.ActionTypeEnum("legal-flag"): return "Legal flag on copr {0}.".format(self.old_value) return "Action {0} on {1}, old value: {2}, new value: {3}.".format( self.action_type, self.object_type, self.old_value, self.new_value)
class CoprChroot(db.Model, helpers.Serializer): """ Representation of Copr<->MockChroot relation """ buildroot_pkgs = db.Column(db.Text) mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("copr_chroots")) copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True) copr = db.relationship("Copr", backref=db.backref( "copr_chroots", single_parent=True, cascade="all,delete,delete-orphan")) comps_zlib = db.Column(db.LargeBinary(), nullable=True) comps_name = db.Column(db.String(127), nullable=True) def update_comps(self, comps_xml): self.comps_zlib = zlib.compress(comps_xml.encode("utf-8")) @property def buildroot_pkgs_list(self): return self.buildroot_pkgs.split() @property def comps(self): if self.comps_zlib: return zlib.decompress(self.comps_zlib).decode("utf-8") @property def comps_len(self): if self.comps_zlib: return len(zlib.decompress(self.comps_zlib)) else: return 0 @property def name(self): return self.mock_chroot.name @property def is_active(self): return self.mock_chroot.is_active
class Package(db.Model, helpers.Serializer): """ Represents a single package in a project. """ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) # Source of the build: type identifier source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset")) # Source of the build: description in json, example: git link, srpm url, etc. source_json = db.Column(db.Text) # True if the package is built automatically via webhooks webhook_rebuild = db.Column(db.Boolean, default=False) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # relations copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("packages")) @property def dist_git_repo(self): if self.copr.is_a_group_project: return "@{}/{}/{}".format(self.copr.group.name, self.copr.name, self.name) else: return "{}/{}/{}".format(self.copr.owner.name, self.copr.name, self.name) @property def source_json_dict(self): return json.loads(self.source_json) @property def source_type_text(self): return helpers.BuildSourceEnum(self.source_type) @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git".format(app.config["DIST_GIT_URL"], self.dist_git_repo) return None
class Copr(db.Model, helpers.Serializer): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations owner_id = db.Column(db.Integer, db.ForeignKey("user.id")) owner = db.relationship("User", backref=db.backref("coprs")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) __mapper_args__ = {"order_by": created_on.desc()} @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) def check_copr_chroot(self, chroot): """ Return object of chroot, if is related to our copr or None """ result = None # there will be max ~10 chroots per build, iteration will be probably # faster than sql query for copr_chroot in self.copr_chroots: if copr_chroot.mock_chroot_id == chroot.id: result = copr_chroot break return result def buildroot_pkgs(self, chroot): """ Return packages in minimal buildroot for given chroot. """ result = "" # this is ugly as user can remove chroot after he submit build, but # lets call this feature copr_chroot = self.check_copr_chroot(chroot) if copr_chroot: result = copr_chroot.buildroot_pkgs return result @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.active_chroots: if self.buildroot_pkgs(chroot): modified_chroots.append(chroot) return modified_chroots
class User(db.Model, helpers.Serializer): """ Represents user of the copr frontend """ # PK; TODO: the 'username' could be also PK id = db.Column(db.Integer, primary_key=True) # unique username username = db.Column(db.String(100), nullable=False, unique=True) # email mail = db.Column(db.String(150), nullable=False) # optional timezone timezone = db.Column(db.String(50), nullable=True) # is this user proven? proven users can modify builder memory and # timeout for single builds proven = db.Column(db.Boolean, default=False) # is this user admin of the system? admin = db.Column(db.Boolean, default=False) # stuff for the cli interface api_login = db.Column(db.String(40), nullable=False, default="abc") api_token = db.Column(db.String(40), nullable=False, default="abc") api_token_expiration = db.Column(db.Date, nullable=False, default=datetime.date(2000, 1, 1)) @property def name(self): """ Return the short username of the user, e.g. bkabrda """ return self.username def permissions_for_copr(self, copr): """ Get permissions of this user for the given copr. Caches the permission during one request, so use this if you access them multiple times """ if not hasattr(self, "_permissions_for_copr"): self._permissions_for_copr = {} if copr.name not in self._permissions_for_copr: self._permissions_for_copr[copr.name] = ( CoprPermission.query.filter_by(user=self).filter_by( copr=copr).first()) return self._permissions_for_copr[copr.name] def can_build_in(self, copr): """ Determine if this user can build in the given copr. """ can_build = False if copr.owner == self: can_build = True if (self.permissions_for_copr(copr) and self.permissions_for_copr(copr).copr_builder == helpers.PermissionEnum("approved")): can_build = True return can_build def can_edit(self, copr): """ Determine if this user can edit the given copr. """ can_edit = False if copr.owner == self: can_edit = True if (self.permissions_for_copr(copr) and self.permissions_for_copr(copr).copr_admin == helpers.PermissionEnum("approved")): can_edit = True return can_edit @property def serializable_attributes(self): # enumerate here to prevent exposing credentials return ["id", "name"] @property def coprs_count(self): """ Get number of coprs for this user. """ return (Copr.query.filter_by(owner=self).filter_by( deleted=False).count()) @property def gravatar_url(self): """ Return url to libravatar image. """ try: return libravatar_url(email=self.mail, https=True) except IOError: return ""
class BuildChroot(db.Model, helpers.Serializer): """ Representation of Build<->MockChroot relation """ mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True) mock_chroot = db.relationship("MockChroot", backref=db.backref("builds")) build_id = db.Column(db.Integer, db.ForeignKey("build.id"), primary_key=True) build = db.relationship("Build", backref=db.backref("build_chroots")) git_hash = db.Column(db.String(40)) status = db.Column(db.Integer, default=StatusEnum("importing")) started_on = db.Column(db.Integer) ended_on = db.Column(db.Integer) last_deferred = db.Column(db.Integer) @property def name(self): """ Textual representation of name of this chroot """ return self.mock_chroot.name @property def state(self): """ Return text representation of status of this build chroot """ if self.status is not None: return StatusEnum(self.status) return "unknown" @property def task_id(self): return "{}-{}".format(self.build_id, self.name) @property def import_task_id(self): return "{}-{}".format(self.build_id, helpers.chroot_to_branch(self.name)) @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git/commit/?id={}".format( app.config["DIST_GIT_URL"], self.build.package.dist_git_repo, self.git_hash) return None @property def import_log_url(self): if app.config["COPR_DIST_GIT_LOGS_URL"]: return "{}/{}.log".format(app.config["COPR_DIST_GIT_LOGS_URL"], self.import_task_id) return None @property def result_dir_url(self): return "/".join( [app.config["BACKEND_BASE_URL"], u"results", self.result_dir]) @property def result_dir(self): # hide changes occurred after migration to dist-git # if build has defined dist-git, it means that new schema should be used # otherwise use older structure # old: results/valtri/ruby/fedora-rawhide-x86_64/rubygem-aws-sdk-resources-2.1.11-1.fc24/ # new: results/asamalik/rh-perl520/epel-7-x86_64/00000187-rh-perl520/ parts = [self.build.copr.owner_name] parts.extend([ self.build.copr.name, self.name, ]) if self.git_hash is not None and self.build.package: parts.append(self.build.result_dir_name) else: parts.append(self.build.src_pkg_name) return os.path.join(*parts) def __str__(self): return "<BuildChroot: {}>".format(self.to_dict())
class Package(db.Model, helpers.Serializer, CoprSearchRelatedData): """ Represents a single package in a project. """ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) # Source of the build: type identifier source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset")) # Source of the build: description in json, example: git link, srpm url, etc. source_json = db.Column(db.Text) # True if the package is built automatically via webhooks webhook_rebuild = db.Column(db.Boolean, default=False) # enable networking during a build process enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False) # @TODO Remove me few weeks after Copr migration # Contain status of the Package before migration # Normally the `status` is not stored in `Package`. It is computed from `status` variable of `BuildChroot`, # but `old_status` has to be stored here, because we migrate whole `package` table, but only succeeded builds. # Therefore if `old_status` was in `BuildChroot` we wouldn't be able to know old state of non-succeeded packages # even though it would be known before migration. old_status = db.Column(db.Integer) builds = db.relationship("Build", order_by="Build.id") # relations copr_id = db.Column(db.Integer, db.ForeignKey("copr.id")) copr = db.relationship("Copr", backref=db.backref("packages")) @property def dist_git_repo(self): return "{}/{}".format(self.copr.full_name, self.name) @property def source_json_dict(self): if not self.source_json: return {} return json.loads(self.source_json) @property def source_type_text(self): return helpers.BuildSourceEnum(self.source_type) @property def has_source_type_set(self): """ Package's source type (and source_json) is being derived from its first build, which works except for "srpm_link" and "srpm_upload" cases. Consider these being equivalent to source_type being unset. """ return self.source_type and self.source_type_text != "srpm_link" and self.source_type_text != "srpm_upload" @property def dist_git_url(self): if app.config["DIST_GIT_URL"]: return "{}/{}.git".format(app.config["DIST_GIT_URL"], self.dist_git_repo) return None def last_build(self, successful=False): for build in reversed(self.builds): if not successful or build.state == "succeeded": return build return None def to_dict(self, with_latest_build=False, with_latest_succeeded_build=False, with_all_builds=False): package_dict = super(Package, self).to_dict() package_dict['source_type'] = helpers.BuildSourceEnum( package_dict['source_type']) if with_latest_build: build = self.last_build(successful=False) package_dict['latest_build'] = build.to_dict( with_chroot_states=True) if build else None if with_latest_succeeded_build: build = self.last_build(successful=True) package_dict['latest_succeeded_build'] = build.to_dict( with_chroot_states=True) if build else None if with_all_builds: package_dict['builds'] = [ build.to_dict(with_chroot_states=True) for build in reversed(self.builds) ] return package_dict def get_search_related_copr_id(self): return self.copr.id
class User(db.Model, helpers.Serializer): """ Represents user of the copr frontend """ # PK; TODO: the 'username' could be also PK id = db.Column(db.Integer, primary_key=True) # unique username username = db.Column(db.String(100), nullable=False, unique=True) # email mail = db.Column(db.String(150), nullable=False) # optional timezone timezone = db.Column(db.String(50), nullable=True) # is this user proven? proven users can modify builder memory and # timeout for single builds proven = db.Column(db.Boolean, default=False) # is this user admin of the system? admin = db.Column(db.Boolean, default=False) # stuff for the cli interface api_login = db.Column(db.String(40), nullable=False, default="abc") api_token = db.Column(db.String(40), nullable=False, default="abc") api_token_expiration = db.Column(db.Date, nullable=False, default=datetime.date(2000, 1, 1)) # list of groups as retrieved from openid openid_groups = db.Column(JSONEncodedDict) @property def name(self): """ Return the short username of the user, e.g. bkabrda """ return self.username def permissions_for_copr(self, copr): """ Get permissions of this user for the given copr. Caches the permission during one request, so use this if you access them multiple times """ if not hasattr(self, "_permissions_for_copr"): self._permissions_for_copr = {} if copr.name not in self._permissions_for_copr: self._permissions_for_copr[copr.name] = ( CoprPermission.query.filter_by(user=self).filter_by( copr=copr).first()) return self._permissions_for_copr[copr.name] def can_build_in(self, copr): """ Determine if this user can build in the given copr. """ can_build = False if copr.user_id == self.id: can_build = True if (self.permissions_for_copr(copr) and self.permissions_for_copr(copr).copr_builder == helpers.PermissionEnum("approved")): can_build = True # a bit dirty code, here we access flask.session object if copr.group is not None and \ copr.group.fas_name in self.user_teams: return True return can_build @property def user_teams(self): if self.openid_groups and 'fas_groups' in self.openid_groups: return self.openid_groups['fas_groups'] else: return [] @property def user_groups(self): return Group.query.filter(Group.fas_name.in_(self.user_teams)).all() def can_build_in_group(self, group): """ :type group: Group """ if group.fas_name in self.user_teams: return True else: return False def can_edit(self, copr): """ Determine if this user can edit the given copr. """ if copr.user == self or self.admin: return True if (self.permissions_for_copr(copr) and self.permissions_for_copr(copr).copr_admin == helpers.PermissionEnum("approved")): return True if copr.group is not None and \ copr.group.fas_name in self.user_teams: return True return False @property def serializable_attributes(self): # enumerate here to prevent exposing credentials return ["id", "name"] @property def coprs_count(self): """ Get number of coprs for this user. """ return (Copr.query.filter_by(user=self).filter_by( deleted=False).filter_by(group_id=None).count()) @property def gravatar_url(self): """ Return url to libravatar image. """ try: return libravatar_url(email=self.mail, https=True) except IOError: return ""
class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) homepage = db.Column(db.Text) contact = db.Column(db.Text) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations user_id = db.Column(db.Integer, db.ForeignKey("user.id")) user = db.relationship("User", backref=db.backref("coprs")) group_id = db.Column(db.Integer, db.ForeignKey("group.id")) group = db.relationship("Group", backref=db.backref("groups")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id")) forked_from = db.relationship("Copr", remote_side=id, backref=db.backref("forks")) # a secret to be used for webhooks authentication webhook_secret = db.Column(db.String(100)) # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) unlisted_on_hp = db.Column(db.Boolean, default=False, nullable=False) # information for search index updating latest_indexed_data_update = db.Column(db.Integer) # builds and the project are immune against deletion persistent = db.Column(db.Boolean, default=False, nullable=False, server_default="0") __mapper_args__ = {"order_by": created_on.desc()} @property def is_a_group_project(self): """ Return True if copr belongs to a group """ return self.group_id is not None @property def owner(self): """ Return owner (user or group) of this copr """ return self.group if self.is_a_group_project else self.user @property def owner_name(self): """ Return @group.name for a copr owned by a group and user.name otherwise """ return self.group.at_name if self.is_a_group_project else self.user.name @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def active_copr_chroots(self): """ :rtype: list of CoprChroot """ return [c for c in self.copr_chroots if c.is_active] @property def active_chroots_sorted(self): """ Return list of active mock_chroots of this copr """ return sorted(self.active_chroots, key=lambda ch: ch.name) @property def active_chroots_grouped(self): """ Return list of active mock_chroots of this copr """ chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted] output = [] for os, chs in itertools.groupby(chroots, operator.itemgetter(0)): output.append((os, [ch[1] for ch in chs])) return output @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.copr_chroots: if chroot.buildroot_pkgs and chroot.is_active: modified_chroots.append(chroot) return modified_chroots def is_release_arch_modified(self, name_release, arch): if "{}-{}".format(name_release, arch) in \ [chroot.name for chroot in self.modified_chroots]: return True return False @property def full_name(self): return "{}/{}".format(self.owner_name, self.name) @property def repo_name(self): return "{}-{}".format(self.owner_name, self.name) @property def repo_url(self): return "/".join( [app.config["BACKEND_BASE_URL"], u"results", self.full_name]) @property def modules_url(self): return "/".join([self.repo_url, "modules"]) @property def repo_id(self): if self.is_a_group_project: return "group_{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.user.name, self.name) def to_dict(self, private=False, show_builds=True, show_chroots=True): result = {} for key in ["id", "name", "description", "instructions"]: result[key] = str(copy.copy(getattr(self, key))) result["owner"] = self.owner_name return result @property def still_forking(self): return bool( Action.query.filter( Action.result == helpers.BackendResultEnum("waiting")).filter( Action.action_type == helpers.ActionTypeEnum("fork")). filter(Action.new_value == self.full_name).all()) def get_search_related_copr_id(self): return self.id
class Copr(db.Model, helpers.Serializer): """ Represents a single copr (private repo with builds, mock chroots, etc.). """ id = db.Column(db.Integer, primary_key=True) # name of the copr, no fancy chars (checked by forms) name = db.Column(db.String(100), nullable=False) homepage = db.Column(db.Text) contact = db.Column(db.Text) # string containing urls of additional repos (separated by space) # that this copr will pull dependencies from repos = db.Column(db.Text) # time of creation as returned by int(time.time()) created_on = db.Column(db.Integer) # description and instructions given by copr owner description = db.Column(db.Text) instructions = db.Column(db.Text) deleted = db.Column(db.Boolean, default=False) playground = db.Column(db.Boolean, default=False) # should copr run `createrepo` each time when build packages are changed auto_createrepo = db.Column(db.Boolean, default=True) # relations owner_id = db.Column(db.Integer, db.ForeignKey("user.id")) owner = db.relationship("User", backref=db.backref("coprs")) group_id = db.Column(db.Integer, db.ForeignKey("group.id")) group = db.relationship("Group", backref=db.backref("groups")) mock_chroots = association_proxy("copr_chroots", "mock_chroot") # a secret to be used for webhooks authentication webhook_secret = db.Column(db.String(100)) # enable networking for the builds by default build_enable_net = db.Column(db.Boolean, default=True, server_default="1", nullable=False) __mapper_args__ = {"order_by": created_on.desc()} @property def is_a_group_project(self): return self.group_id is not None @property def owner_name(self): return self.owner.name @property def group_name(self): return self.group.name @property def repos_list(self): """ Return repos of this copr as a list of strings """ return self.repos.split() @property def active_chroots(self): """ Return list of active mock_chroots of this copr """ return filter(lambda x: x.is_active, self.mock_chroots) @property def active_copr_chroots(self): """ :rtype: list of CoprChroot """ return [c for c in self.copr_chroots if c.is_active] @property def active_chroots_sorted(self): """ Return list of active mock_chroots of this copr """ return sorted(self.active_chroots, key=lambda ch: ch.name) @property def active_chroots_grouped(self): """ Return list of active mock_chroots of this copr """ chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted] output = [] for os, chs in itertools.groupby(chroots, operator.itemgetter(0)): output.append((os, [ch[1] for ch in chs])) return output @property def build_count(self): """ Return number of builds in this copr """ return len(self.builds) @property def disable_createrepo(self): return not self.auto_createrepo @disable_createrepo.setter def disable_createrepo(self, value): self.auto_createrepo = not bool(value) @property def modified_chroots(self): """ Return list of chroots which has been modified """ modified_chroots = [] for chroot in self.copr_chroots: if chroot.buildroot_pkgs and chroot.is_active: modified_chroots.append(chroot) return modified_chroots def is_release_arch_modified(self, name_release, arch): if "{}-{}".format(name_release, arch) in \ [chroot.name for chroot in self.modified_chroots]: return True return False @property def full_name(self): if self.is_a_group_project: return "@{}/{}".format(self.group.name, self.name) else: return "{}/{}".format(self.owner.username, self.name) @property def repo_name(self): if self.is_a_group_project: return "@{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.owner.username, self.name) @property def repo_id(self): if self.is_a_group_project: return "group_{}-{}".format(self.group.name, self.name) else: return "{}-{}".format(self.owner.username, self.name) def to_dict(self, private=False, show_builds=True, show_chroots=True): result = {} for key in ["id", "name", "description", "instructions"]: result[key] = str(copy.copy(getattr(self, key))) result["owner"] = self.owner.name return result