1 import copy
2 import datetime
3 import os
4 import flask
5 import json
6 import base64
7 import uuid
8
9 from sqlalchemy.ext.associationproxy import association_proxy
10 from six.moves.urllib.parse import urljoin
11 from libravatar import libravatar_url
12 import zlib
13
14 from copr_common.enums import ActionTypeEnum, BackendResultEnum, FailTypeEnum, ModuleStatusEnum, StatusEnum
15 from coprs import constants
16 from coprs import db
17 from coprs import helpers
18 from coprs import app
19
20 import itertools
21 import operator
22 from coprs.helpers import BuildSourceEnum, JSONEncodedDict
23
24 import gi
25 gi.require_version('Modulemd', '1.0')
26 from gi.repository import Modulemd
32
33
34 -class User(db.Model, helpers.Serializer):
35 """
36 Represents user of the copr frontend
37 """
38
39 id = db.Column(db.Integer, primary_key=True)
40
41
42 username = db.Column(db.String(100), nullable=False, unique=True)
43
44
45 mail = db.Column(db.String(150), nullable=False)
46
47
48 timezone = db.Column(db.String(50), nullable=True)
49
50
51
52 proven = db.Column(db.Boolean, default=False)
53
54
55 admin = db.Column(db.Boolean, default=False)
56
57
58 proxy = db.Column(db.Boolean, default=False)
59
60
61 api_login = db.Column(db.String(40), nullable=False, default="abc")
62 api_token = db.Column(db.String(40), nullable=False, default="abc")
63 api_token_expiration = db.Column(
64 db.Date, nullable=False, default=datetime.date(2000, 1, 1))
65
66
67 openid_groups = db.Column(JSONEncodedDict)
68
69 @property
71 """
72 Return the short username of the user, e.g. bkabrda
73 """
74
75 return self.username
76
78 """
79 Get permissions of this user for the given copr.
80 Caches the permission during one request,
81 so use this if you access them multiple times
82 """
83
84 if not hasattr(self, "_permissions_for_copr"):
85 self._permissions_for_copr = {}
86 if copr.name not in self._permissions_for_copr:
87 self._permissions_for_copr[copr.name] = (
88 CoprPermission.query
89 .filter_by(user=self)
90 .filter_by(copr=copr)
91 .first()
92 )
93 return self._permissions_for_copr[copr.name]
94
114
115 @property
121
122 @property
125
127 """
128 :type group: Group
129 """
130 if group.fas_name in self.user_teams:
131 return True
132 else:
133 return False
134
153
154 @property
156
157 return ["id", "name"]
158
159 @property
161 """
162 Get number of coprs for this user.
163 """
164
165 return (Copr.query.filter_by(user=self).
166 filter_by(deleted=False).
167 filter_by(group_id=None).
168 count())
169
170 @property
172 """
173 Return url to libravatar image.
174 """
175
176 try:
177 return libravatar_url(email=self.mail, https=True)
178 except IOError:
179 return ""
180
181
182 -class Copr(db.Model, helpers.Serializer, CoprSearchRelatedData):
183 """
184 Represents a single copr (private repo with builds, mock chroots, etc.).
185 """
186
187 __table_args__ = (
188 db.Index('copr_webhook_secret', 'webhook_secret'),
189 )
190
191 id = db.Column(db.Integer, primary_key=True)
192
193 name = db.Column(db.String(100), nullable=False)
194 homepage = db.Column(db.Text)
195 contact = db.Column(db.Text)
196
197
198 repos = db.Column(db.Text)
199
200 created_on = db.Column(db.Integer)
201
202 description = db.Column(db.Text)
203 instructions = db.Column(db.Text)
204 deleted = db.Column(db.Boolean, default=False)
205 playground = db.Column(db.Boolean, default=False)
206
207
208 auto_createrepo = db.Column(db.Boolean, default=True)
209
210
211 user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
212 user = db.relationship("User", backref=db.backref("coprs"))
213 group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
214 group = db.relationship("Group", backref=db.backref("groups"))
215 mock_chroots = association_proxy("copr_chroots", "mock_chroot")
216 forked_from_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
217 forked_from = db.relationship("Copr", remote_side=id, backref=db.backref("forks"))
218
219
220 webhook_secret = db.Column(db.String(100))
221
222
223 build_enable_net = db.Column(db.Boolean, default=True,
224 server_default="1", nullable=False)
225
226 unlisted_on_hp = db.Column(db.Boolean, default=False, nullable=False)
227
228
229 latest_indexed_data_update = db.Column(db.Integer)
230
231
232 persistent = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
233
234
235 auto_prune = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
236
237
238 use_bootstrap_container = db.Column(db.Boolean, default=False, nullable=False, server_default="0")
239
240
241 follow_fedora_branching = db.Column(db.Boolean, default=True, nullable=False, server_default="1")
242
243
244 scm_repo_url = db.Column(db.Text)
245 scm_api_type = db.Column(db.Text)
246 scm_api_auth_json = db.Column(db.Text)
247
248 __mapper_args__ = {
249 "order_by": created_on.desc()
250 }
251
252 @property
253 - def main_dir(self):
254 """
255 Return main copr dir for a Copr
256 """
257 return CoprDir.query.filter(CoprDir.copr_id==self.id).filter(CoprDir.main==True).one()
258
259 @property
264
265 @property
267 """
268 Return True if copr belongs to a group
269 """
270 return self.group is not None
271
272 @property
278
279 @property
285
286 @property
288 """
289 Return repos of this copr as a list of strings
290 """
291 return self.repos.split()
292
293 @property
299
300 @property
302 """
303 :rtype: list of CoprChroot
304 """
305 return [c for c in self.copr_chroots if c.is_active]
306
307 @property
309 """
310 Return list of active mock_chroots of this copr
311 """
312 return sorted(self.active_chroots, key=lambda ch: ch.name)
313
314 @property
316 """
317 Return list of active mock_chroots of this copr
318 """
319 chroots = [("{} {}".format(c.os_release, c.os_version), c.arch) for c in self.active_chroots_sorted]
320 output = []
321 for os, chs in itertools.groupby(chroots, operator.itemgetter(0)):
322 output.append((os, [ch[1] for ch in chs]))
323
324 return output
325
326 @property
328 """
329 Return number of builds in this copr
330 """
331 return len(self.builds)
332
333 @property
336
337 @disable_createrepo.setter
340
341 @property
344
345 @property
357
363
364 @property
367
368 @property
371
372 @property
377
378 @property
384
385 @property
387 return "/".join([self.repo_url, "modules"])
388
389 - def to_dict(self, private=False, show_builds=True, show_chroots=True):
390 result = {}
391 for key in ["id", "name", "description", "instructions"]:
392 result[key] = str(copy.copy(getattr(self, key)))
393 result["owner"] = self.owner_name
394 return result
395
396 @property
401
404
405 @property
408
409 @enable_net.setter
412
415
418 """
419 Association class for Copr<->Permission relation
420 """
421
422
423
424 copr_builder = db.Column(db.SmallInteger, default=0)
425
426 copr_admin = db.Column(db.SmallInteger, default=0)
427
428
429 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
430 user = db.relationship("User", backref=db.backref("copr_permissions"))
431 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
432 copr = db.relationship("Copr", backref=db.backref("copr_permissions"))
433
436 """
437 Represents one of data directories for a copr.
438 """
439 id = db.Column(db.Integer, primary_key=True)
440
441 name = db.Column(db.Text, index=True)
442 main = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
443
444 ownername = db.Column(db.Text, index=True, nullable=False)
445
446 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), index=True, nullable=False)
447 copr = db.relationship("Copr", backref=db.backref("dirs"))
448
449 __table_args__ = (
450 db.Index('only_one_main_copr_dir', copr_id, main,
451 unique=True, postgresql_where=(main==True)),
452
453 db.UniqueConstraint('ownername', 'name',
454 name='ownername_copr_dir_uniq'),
455 )
456
461
462 @property
465
466 @property
469
470 @property
474
475 @property
481
482
483 -class Package(db.Model, helpers.Serializer, CoprSearchRelatedData):
484 """
485 Represents a single package in a project_dir.
486 """
487
488 __table_args__ = (
489 db.UniqueConstraint('copr_dir_id', 'name', name='packages_copr_dir_pkgname'),
490 db.Index('package_webhook_sourcetype', 'webhook_rebuild', 'source_type'),
491 )
492
497
498 id = db.Column(db.Integer, primary_key=True)
499 name = db.Column(db.String(100), nullable=False)
500
501 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
502
503 source_json = db.Column(db.Text)
504
505 webhook_rebuild = db.Column(db.Boolean, default=False)
506
507 enable_net = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
508
509
510
511
512
513
514
515 old_status = db.Column(db.Integer)
516
517 builds = db.relationship("Build", order_by="Build.id")
518
519
520 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
521 copr = db.relationship("Copr", backref=db.backref("packages"))
522
523 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
524 copr_dir = db.relationship("CoprDir", backref=db.backref("packages"))
525
526 @property
529
530 @property
535
536 @property
539
540 @property
542 """
543 Package's source type (and source_json) is being derived from its first build, which works except
544 for "link" and "upload" cases. Consider these being equivalent to source_type being unset.
545 """
546 return self.source_type and self.source_type_text != "link" and self.source_type_text != "upload"
547
548 @property
553
554 @property
560
566
567 - def to_dict(self, with_latest_build=False, with_latest_succeeded_build=False, with_all_builds=False):
568 package_dict = super(Package, self).to_dict()
569 package_dict['source_type'] = helpers.BuildSourceEnum(package_dict['source_type'])
570
571 if with_latest_build:
572 build = self.last_build(successful=False)
573 package_dict['latest_build'] = build.to_dict(with_chroot_states=True) if build else None
574 if with_latest_succeeded_build:
575 build = self.last_build(successful=True)
576 package_dict['latest_succeeded_build'] = build.to_dict(with_chroot_states=True) if build else None
577 if with_all_builds:
578 package_dict['builds'] = [build.to_dict(with_chroot_states=True) for build in reversed(self.builds)]
579
580 return package_dict
581
584
585
586 -class Build(db.Model, helpers.Serializer):
587 """
588 Representation of one build in one copr
589 """
590
591 SCM_COMMIT = 'commit'
592 SCM_PULL_REQUEST = 'pull-request'
593
594 __table_args__ = (db.Index('build_canceled', "canceled"),
595 db.Index('build_order', "is_background", "id"),
596 db.Index('build_filter', "source_type", "canceled"))
597
611
612 id = db.Column(db.Integer, primary_key=True)
613
614 pkgs = db.Column(db.Text)
615
616 built_packages = db.Column(db.Text)
617
618 pkg_version = db.Column(db.Text)
619
620 canceled = db.Column(db.Boolean, default=False)
621
622 repos = db.Column(db.Text)
623
624
625 submitted_on = db.Column(db.Integer, nullable=False)
626
627 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
628
629 memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY)
630
631 timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT)
632
633 enable_net = db.Column(db.Boolean, default=False,
634 server_default="0", nullable=False)
635
636 source_type = db.Column(db.Integer, default=helpers.BuildSourceEnum("unset"))
637
638 source_json = db.Column(db.Text)
639
640 fail_type = db.Column(db.Integer, default=FailTypeEnum("unset"))
641
642 is_background = db.Column(db.Boolean, default=False, server_default="0", nullable=False)
643
644 source_status = db.Column(db.Integer, default=StatusEnum("waiting"))
645 srpm_url = db.Column(db.Text)
646
647
648 user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
649 user = db.relationship("User", backref=db.backref("builds"))
650 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
651 copr = db.relationship("Copr", backref=db.backref("builds"))
652 package_id = db.Column(db.Integer, db.ForeignKey("package.id"))
653 package = db.relationship("Package")
654
655 chroots = association_proxy("build_chroots", "mock_chroot")
656
657 batch_id = db.Column(db.Integer, db.ForeignKey("batch.id"))
658 batch = db.relationship("Batch", backref=db.backref("builds"))
659
660 module_id = db.Column(db.Integer, db.ForeignKey("module.id"), index=True)
661 module = db.relationship("Module", backref=db.backref("builds"))
662
663 copr_dir_id = db.Column(db.Integer, db.ForeignKey("copr_dir.id"), index=True)
664 copr_dir = db.relationship("CoprDir", backref=db.backref("builds"))
665
666
667 scm_object_id = db.Column(db.Text)
668 scm_object_type = db.Column(db.Text)
669 scm_object_url = db.Column(db.Text)
670
671
672 update_callback = db.Column(db.Text)
673
674 @property
677
678 @property
681
682 @property
685
686 @property
689
690 @property
693
694 @property
695 - def fail_type_text(self):
696 return FailTypeEnum(self.fail_type)
697
698 @property
700 if self.repos is None:
701 return list()
702 else:
703 return self.repos.split()
704
705 @property
708
709 @property
711 return "{:08d}".format(self.id)
712
713 @property
721
722 @property
724 if app.config["COPR_DIST_GIT_LOGS_URL"]:
725 return "{}/{}.log".format(app.config["COPR_DIST_GIT_LOGS_URL"],
726 self.task_id.replace('/', '_'))
727 return None
728
729 @property
735
736 @property
741
742 @property
745
746 @property
754
755 @property
758
759 @property
766
767 @property
770
771 @property
774
775 @property
778
779 @property
788
789 @property
792
794 """
795 Get build chroots with states which present in `states` list
796 If states == None, function returns build_chroots
797 """
798 chroot_states_map = dict(zip(self.build_chroots, self.chroot_states))
799 if statuses is not None:
800 statuses = set(statuses)
801 else:
802 return self.build_chroots
803
804 return [
805 chroot for chroot, status in chroot_states_map.items()
806 if status in statuses
807 ]
808
809 @property
811 return {b.name: b for b in self.build_chroots}
812
813 @property
815 """
816 Return build status.
817 """
818 if self.canceled:
819 return StatusEnum("canceled")
820
821 for state in ["running", "starting", "pending", "failed", "succeeded", "skipped", "forked", "waiting"]:
822 if StatusEnum(state) in self.chroot_states:
823 if state == "waiting":
824 return self.source_status
825 else:
826 return StatusEnum(state)
827
828 return None
829
830 @property
832 """
833 Return text representation of status of this build.
834 """
835 if self.status != None:
836 return StatusEnum(self.status)
837 return "unknown"
838
839 @property
841 """
842 Find out if this build is cancelable.
843 """
844 return not self.finished and self.status != StatusEnum("starting")
845
846 @property
848 """
849 Find out if this build is repeatable.
850
851 Build is repeatable only if sources has been imported.
852 """
853 return self.source_status == StatusEnum("succeeded")
854
855 @property
857 """
858 Find out if this build is in finished state.
859
860 Build is finished only if all its build_chroots are in finished state or
861 the build was canceled.
862 """
863 return self.canceled or all([chroot.finished for chroot in self.build_chroots])
864
865 @property
867 """
868 Find out if this build is persistent.
869
870 This property is inherited from the project.
871 """
872 return self.copr.persistent
873
874 @property
876 try:
877 return self.package.name
878 except:
879 return None
880
881 - def to_dict(self, options=None, with_chroot_states=False):
894
897 """
898 1:N mapping: branch -> chroots
899 """
900
901
902 name = db.Column(db.String(50), primary_key=True)
903
904
905 -class MockChroot(db.Model, helpers.Serializer):
906 """
907 Representation of mock chroot
908 """
909
910 __table_args__ = (
911 db.UniqueConstraint('os_release', 'os_version', 'arch', name='mock_chroot_uniq'),
912 )
913
914 id = db.Column(db.Integer, primary_key=True)
915
916 os_release = db.Column(db.String(50), nullable=False)
917
918 os_version = db.Column(db.String(50), nullable=False)
919
920 arch = db.Column(db.String(50), nullable=False)
921 is_active = db.Column(db.Boolean, default=True)
922
923
924 distgit_branch_name = db.Column(db.String(50),
925 db.ForeignKey("dist_git_branch.name"),
926 nullable=False)
927
928 distgit_branch = db.relationship("DistGitBranch",
929 backref=db.backref("chroots"))
930
931 @classmethod
940
941 @property
943 """
944 Textual representation of name of this chroot
945 """
946 return "{}-{}-{}".format(self.os_release, self.os_version, self.arch)
947
948 @property
950 """
951 Textual representation of name of this or release
952 """
953 return "{}-{}".format(self.os_release, self.os_version)
954
955 @property
957 """
958 Textual representation of the operating system name
959 """
960 return "{0} {1}".format(self.os_release, self.os_version)
961
962 @property
967
968
969 -class CoprChroot(db.Model, helpers.Serializer):
970 """
971 Representation of Copr<->MockChroot relation
972 """
973
974 buildroot_pkgs = db.Column(db.Text)
975 repos = db.Column(db.Text, default="", server_default="", nullable=False)
976 mock_chroot_id = db.Column(
977 db.Integer, db.ForeignKey("mock_chroot.id"), primary_key=True)
978 mock_chroot = db.relationship(
979 "MockChroot", backref=db.backref("copr_chroots"))
980 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), primary_key=True)
981 copr = db.relationship("Copr",
982 backref=db.backref(
983 "copr_chroots",
984 single_parent=True,
985 cascade="all,delete,delete-orphan"))
986
987 comps_zlib = db.Column(db.LargeBinary(), nullable=True)
988 comps_name = db.Column(db.String(127), nullable=True)
989
990 module_md_zlib = db.Column(db.LargeBinary(), nullable=True)
991 module_md_name = db.Column(db.String(127), nullable=True)
992
993 with_opts = db.Column(db.Text, default="", server_default="", nullable=False)
994 without_opts = db.Column(db.Text, default="", server_default="", nullable=False)
995
997 if isinstance(comps_xml, str):
998 data = comps_xml.encode("utf-8")
999 else:
1000 data = comps_xml
1001 self.comps_zlib = zlib.compress(data)
1002
1004 if isinstance(module_md_yaml, str):
1005 data = module_md_yaml.encode("utf-8")
1006 else:
1007 data = module_md_yaml
1008 self.module_md_zlib = zlib.compress(data)
1009
1010 @property
1013
1014 @property
1016 return (self.repos or "").split()
1017
1018 @property
1022
1023 @property
1027
1028 @property
1034
1035 @property
1041
1042 @property
1045
1046 @property
1049
1051 options = {"__columns_only__": [
1052 "buildroot_pkgs", "repos", "comps_name", "copr_id", "with_opts", "without_opts"
1053 ]}
1054 d = super(CoprChroot, self).to_dict(options=options)
1055 d["mock_chroot"] = self.mock_chroot.name
1056 return d
1057
1060 """
1061 Representation of Build<->MockChroot relation
1062 """
1063
1064 mock_chroot_id = db.Column(db.Integer, db.ForeignKey("mock_chroot.id"),
1065 primary_key=True)
1066 mock_chroot = db.relationship("MockChroot", backref=db.backref("builds"))
1067 build_id = db.Column(db.Integer, db.ForeignKey("build.id"),
1068 primary_key=True)
1069 build = db.relationship("Build", backref=db.backref("build_chroots"))
1070 git_hash = db.Column(db.String(40))
1071 status = db.Column(db.Integer, default=StatusEnum("waiting"))
1072
1073 started_on = db.Column(db.Integer, index=True)
1074 ended_on = db.Column(db.Integer, index=True)
1075
1076
1077 result_dir = db.Column(db.Text, default='', server_default='', nullable=False)
1078
1079 build_requires = db.Column(db.Text)
1080
1081 @property
1083 """
1084 Textual representation of name of this chroot
1085 """
1086 return self.mock_chroot.name
1087
1088 @property
1090 """
1091 Return text representation of status of this build chroot
1092 """
1093 if self.status is not None:
1094 return StatusEnum(self.status)
1095 return "unknown"
1096
1097 @property
1099 return (self.state in ["succeeded", "forked", "canceled", "skipped", "failed"])
1100
1101 @property
1104
1105 @property
1117
1118 @property
1122
1123
1124 -class LegalFlag(db.Model, helpers.Serializer):
1125 id = db.Column(db.Integer, primary_key=True)
1126
1127 raise_message = db.Column(db.Text)
1128
1129 raised_on = db.Column(db.Integer)
1130
1131 resolved_on = db.Column(db.Integer)
1132
1133
1134 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"), nullable=True)
1135
1136 copr = db.relationship(
1137 "Copr", backref=db.backref("legal_flags", cascade="all"))
1138
1139 reporter_id = db.Column(db.Integer, db.ForeignKey("user.id"))
1140 reporter = db.relationship("User",
1141 backref=db.backref("legal_flags_raised"),
1142 foreign_keys=[reporter_id],
1143 primaryjoin="LegalFlag.reporter_id==User.id")
1144
1145 resolver_id = db.Column(
1146 db.Integer, db.ForeignKey("user.id"), nullable=True)
1147 resolver = db.relationship("User",
1148 backref=db.backref("legal_flags_resolved"),
1149 foreign_keys=[resolver_id],
1150 primaryjoin="LegalFlag.resolver_id==User.id")
1151
1152
1153 -class Action(db.Model, helpers.Serializer):
1154 """
1155 Representation of a custom action that needs
1156 backends cooperation/admin attention/...
1157 """
1158
1159 id = db.Column(db.Integer, primary_key=True)
1160
1161 action_type = db.Column(db.Integer, nullable=False)
1162
1163 object_type = db.Column(db.String(20))
1164
1165 object_id = db.Column(db.Integer)
1166
1167 old_value = db.Column(db.String(255))
1168 new_value = db.Column(db.String(255))
1169
1170 data = db.Column(db.Text)
1171
1172 result = db.Column(
1173 db.Integer, default=BackendResultEnum("waiting"))
1174
1175 message = db.Column(db.Text)
1176
1177 created_on = db.Column(db.Integer)
1178
1179 ended_on = db.Column(db.Integer)
1180
1183
1192
1205
1206
1207 -class Krb5Login(db.Model, helpers.Serializer):
1208 """
1209 Represents additional user information for kerberos authentication.
1210 """
1211
1212 __tablename__ = "krb5_login"
1213
1214
1215 user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
1216
1217
1218 config_name = db.Column(db.String(30), nullable=False, primary_key=True)
1219
1220
1221 primary = db.Column(db.String(80), nullable=False, primary_key=True)
1222
1223 user = db.relationship("User", backref=db.backref("krb5_logins"))
1224
1227 """
1228 Generic store for simple statistics.
1229 """
1230
1231 name = db.Column(db.String(127), primary_key=True)
1232 counter_type = db.Column(db.String(30))
1233
1234 counter = db.Column(db.Integer, default=0, server_default="0")
1235
1236
1237 -class Group(db.Model, helpers.Serializer):
1238
1239 """
1240 Represents FAS groups and their aliases in Copr
1241 """
1242
1243 id = db.Column(db.Integer, primary_key=True)
1244 name = db.Column(db.String(127))
1245
1246
1247 fas_name = db.Column(db.String(127))
1248
1249 @property
1251 return u"@{}".format(self.name)
1252
1255
1258
1259
1260 -class Batch(db.Model):
1261 id = db.Column(db.Integer, primary_key=True)
1262
1263
1264 -class Module(db.Model, helpers.Serializer):
1265 id = db.Column(db.Integer, primary_key=True)
1266 name = db.Column(db.String(100), nullable=False)
1267 stream = db.Column(db.String(100), nullable=False)
1268 version = db.Column(db.BigInteger, nullable=False)
1269 summary = db.Column(db.String(100), nullable=False)
1270 description = db.Column(db.Text)
1271 created_on = db.Column(db.Integer, nullable=True)
1272
1273
1274
1275
1276
1277
1278
1279 yaml_b64 = db.Column(db.Text)
1280
1281
1282 copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
1283 copr = db.relationship("Copr", backref=db.backref("modules"))
1284
1285 __table_args__ = (
1286 db.UniqueConstraint("copr_id", "name", "stream", "version", name="copr_name_stream_version_uniq"),
1287 )
1288
1289 @property
1291 return base64.b64decode(self.yaml_b64)
1292
1293 @property
1295 mmd = Modulemd.ModuleStream()
1296 mmd.import_from_string(self.yaml.decode("utf-8"))
1297 return mmd
1298
1299 @property
1302
1303 @property
1306
1307 @property
1310
1311 @property
1313 """
1314 Return numeric representation of status of this build
1315 """
1316 if any(b for b in self.builds if b.status == StatusEnum("failed")):
1317 return ModuleStatusEnum("failed")
1318 return self.action.result if self.action else ModuleStatusEnum("pending")
1319
1320 @property
1322 """
1323 Return text representation of status of this build
1324 """
1325 return ModuleStatusEnum(self.status)
1326
1327 @property
1330
1331 @property
1334
1335 @property
1337 return {k: v.get_rpms().get() for k, v in self.modulemd.get_profiles().items()}
1338
1345