def ready(self): from django.contrib.auth import get_user_model User = get_user_model() import compat compat.User = User from relationships import models as rmodels rmodels.User = User from django.db.models import ManyToManyField from relationships.models import RelationshipsDescriptor, Relationship field = ManyToManyField(User, through=Relationship, symmetrical=False, related_name='related_to') field.contribute_to_class(User, 'relationships') setattr(User, 'relationships', RelationshipsDescriptor())
class QuestionSet(Model): def __str__(self): return u"{}".format(self.name) def get_absolute_url(self): return reverse('questionset_detail', kwargs={'pk': str(self.id)}) slug = SlugField(unique=True, verbose_name=_("Slug")) name = CharField(max_length=255, verbose_name=_("Name")) questions = ManyToManyField('Question', blank=True, verbose_name=_("Questions")) resource_caches = ManyToManyField('ResourceCache', blank=True) def question_mapping(self, random_seed): q = self.questions.order_by('identifier').values_list('identifier') d = dict() r = random.Random(random_seed) c = r.sample(range(2**24), len(q)) for n, i in enumerate(q): d[i[0]] = c[n] return d def ordered_question_ids(self): cache_id = 'questionset_question_ids_' + str(self.id) q_ids = cache.get(cache_id, None) if q_ids is None: q_ids = self.questions.order_by('id').values_list('id', flat=True) cache.set(cache_id, q_ids) return q_ids def cache_dir(self): return str(self.id) + "-" + self.slug def reverse_question_mapping(self, random_seed): return {v: k for k, v in self.question_mapping(random_seed).items()} def rebuild_caches(self, embed_images=True): html_resources = {} self.resource_caches.all().delete() html_cache = ResourceCache(format='zip') html_cache.file.name = os.path.join("caches", self.cache_dir(), "html_cache.zip") html_cache.save() ensure_dir_exists(html_cache.file.path) embeded_resource_ids = [] html_resource_zip = zipfile.ZipFile(html_cache.file.path, 'w') for q in self.questions.all(): for r in q.resource_set.filter(resource_type="html", part_of_solution=False): html_resources[q.identifier + '/' + r.relative_url] = r html_resource_zip.writestr( q.identifier + '/' + 'Manifest.json', json.dumps(q.manifest(safe=True))) for url, r in html_resources.items(): if embed_images: index_soup = BeautifulSoup(r.as_bytes(), "lxml") imgs = index_soup.find_all('img') objs = index_soup.find_all('object') scripts = index_soup.find_all('script') for items, item_type, url_property in [(imgs, 'image', 'src'), (objs, 'image', 'data'), (scripts, 'javascript', 'src')]: for i in items: url_str = i.get(url_property, None) if url_str is not None: try: data_res = r.question.resource_set.get( relative_url=url_str) i[url_property] = ( "data:" + data_res.mimetype + ";base64," + data_res.as_base64().decode('utf-8')) embeded_resource_ids.append(data_res.id) except Exception as e: # TODO: handle exception, not print it! print("error embedding", url, url_str, e) embeded_resource_ids.append(r.id) index_str = bytes(index_soup.prettify().encode('utf-8')) else: index_str = r.as_bytes() html_resource_zip.writestr(url, index_str) html_cache.resources.add(r) html_resource_zip.close() self.resource_caches.add(html_cache) for q in self.questions.all(): for r in q.resource_set.exclude(part_of_solution=True).exclude( id__in=embeded_resource_ids): print("must create cache for ", r.id, r.question.identifier, r.file.name)
class Profile(Model): def __str__(self): return str(self.user) def get_absolute_url(self): return reverse('profile_detail', kwargs={'pk': str(self.pk)}) user = OneToOneField(User, on_delete=CASCADE) feature_level = IntegerField(choices=FEATURE_LEVELS, default=1) date_of_birth = DateField(null=True, blank=True, verbose_name=_('Date of birth')) gender = CharField(choices=GENDERS, max_length=16, blank=True) managed_profiles = ManyToManyField('Profile', related_name='managers', blank=True) created_codes = ManyToManyField(Code, blank=True, related_name='creator_set') received_codes = ManyToManyField(Code, blank=True, related_name='recipient_set') used_codes = ManyToManyField(Code, blank=True, related_name='user_set') question_sets = ManyToManyField(QuestionSet, blank=True) created_question_sets = ManyToManyField(QuestionSet, blank=True, related_name='creator_set') questions = ManyToManyField(Question, blank=True) merged_with = ForeignKey('Profile', null=True, blank=True, related_name='former_profile_set', on_delete=CASCADE) update_used_codes_timestamp = DateTimeField(null=True, blank=True) update_managers_timestamp = DateTimeField(null=True, blank=True) vcard = TextField(blank=True) @property def first_name(self): return self.user.first_name @property def last_name(self): return self.user.last_name @property def email(self): return self.user.email @property def username(self): return self.user.username def __superiors(self, codegen, known): for c in self.received_codes.filter(format=codegen.format, salt=codegen.salt): for o in c.owner_set.all(): if o not in known: s1 = Profile.__superiors(o, codegen, known) known = s1.union(known) known.add(o) return known def manages_self(self): return self.managed_profiles.filter(id=self.id).exists() def managed_others(self): return self.managed_profiles.exclude(id=self.id) def update_used_codes(self): if self.update_used_codes_timestamp is None: attempts = self.attempt_set.all() else: attempts = self.attempt_set.filter( start__gte=self.update_used_codes_timestamp) self.update_used_codes_timestamp = timezone.now() for a in attempts: try: codegen = a.competitionquestionset.competition.competitor_code_generator codes = Code.objects.filter(value=a.access_code, salt=codegen.salt, format=codegen.format) for c in codes: self.used_codes.add(c) except Exception: # TODO: handle exception pass def apply_code_effects(self, codes=None): if codes is None: codes = self.used_codes for c in codes: for effect in c.code_effect_set: effect.apply(users=[self]) def update_managers(self, codes=None): for c in codes: if c.format.code_matches(c.salt, c.value, {'code_effects': ['let_manage']}): for u in c.owner_set: u.managed_profiles.add(self) if c.format.code_matches( c.salt, c.value, {'code_effects': ['let_manage_recursive']}): competitions = Competition.objects.filter( competitor_code_generator__salt=c.salt, competitor_code_generator__format=c.format).unique() superiors = set() for u in c.owner_set: superiors.add(u) for competition in competitions: superiors = u.__superiors( competition.administrator_code_generator, superiors) for superior in superiors: superior.managed_profiles.add(self) def update_managed_profiles(self, codes=None): if codes is None: codes = Code.objects.filter(owner_set=self).unique() for c in codes: if c.format.code_matches(c.salt, c.value, {'code_effects': ['let_manage']}): for u in c.user_set: u.managers.add(self.user) if c.format.code_matches( c.salt, c.value, {'code_effects': ['let_manage_recursive']}): competitions = Competition.objects.filter( competitor_code_generator__salt=c.salt, competitor_code_generator__format=c.format).unique() for u in c.user_set: u.managers.add(self.user) for competition in competitions: for superior in u.__superiors( competition.administrator_code_generator): u.managers.add(superior) def merge_to_top(self, limit=None): if self.merged_with is not None: # detect cycles, merge profiles profile = self old_profiles = set() old_profiles.add(None) while profile not in old_profiles and \ limit is not None and \ len(old_profiles) < limit: old_profiles.add(profile) prev_profile = profile profile = profile.merged_with if profile is None: profile = prev_profile elif profile in old_profiles: # this is a cycle. Break it. old_profiles.remove(profile) profile.merged_with = None profile.save() else: # limit exceeded. pass old_profiles.remove(None) for old_profile in old_profiles: old_profile.merged_with = profile old_profile.save() return profile # profile not merged return self
class BMC(CleanSave, TimestampedModel): """A `BMC` represents an existing 'baseboard management controller'. For practical purposes in MAAS, this is any addressable device that can control the power state of Nodes. The BMC associated with a Node is the one expected to control its power. Power parameters that apply to all nodes controlled by a BMC are stored here in the BMC. Those that are specific to different Nodes on the same BMC are stored in the Node model instances. :ivar ip_address: This `BMC`'s IP Address. :ivar power_type: The power type defines which type of BMC this is. Its value must match a power driver class name. :ivar power_parameters: Some JSON containing arbitrary parameters this BMC's power driver requires to function. :ivar objects: The :class:`BMCManager`. """ class Meta(DefaultMeta): unique_together = ("power_type", "power_parameters", "ip_address") objects = Manager() bmcs = BMCManager() bmc_type = IntegerField(choices=BMC_TYPE_CHOICES, editable=False, default=BMC_TYPE.DEFAULT) ip_address = ForeignKey(StaticIPAddress, default=None, blank=True, null=True, editable=False, on_delete=SET_NULL) # The possible choices for this field depend on the power types advertised # by the rack controllers. This needs to be populated on the fly, in # forms.py, each time the form to edit a node is instantiated. power_type = CharField(max_length=10, null=False, blank=True, default='') # JSON-encoded set of parameters for power control, limited to 32kiB when # encoded as JSON. These apply to all Nodes controlled by this BMC. power_parameters = JSONObjectField(max_length=(2**15), blank=True, default='') # Rack controllers that have access to the BMC by routing instead of # having direct layer 2 access. routable_rack_controllers = ManyToManyField( "RackController", blank=True, editable=True, through="BMCRoutableRackControllerRelationship", related_name="routable_bmcs") # Values for Pod's. # 1. Name of the Pod. # 2. List of architectures that a Pod supports. # 3. Capabilities that the Pod supports. # 4. Total cores in the Pod. # 5. Fastest CPU speed in the Pod. # 6. Total amount of memory in the Pod. # 7. Total about in bytes of local storage available in the Pod. # 8. Total number of available local disks in the Pod. # 9. The resource pool machines in the pod should belong to by default. # 10. The zone of the Pod. # 11. The tags of the Pod. # 12. CPU over commit ratio multiplier ('over_commit' capabilities). # 13. Memory over commit ratio multiplier ('over_commit' capabilities). name = CharField(max_length=255, default='', blank=True, unique=True) architectures = ArrayField(TextField(), blank=True, null=True, default=list) capabilities = ArrayField(TextField(), blank=True, null=True, default=list) cores = IntegerField(blank=False, null=False, default=0) cpu_speed = IntegerField(blank=False, null=False, default=0) # MHz memory = IntegerField(blank=False, null=False, default=0) local_storage = BigIntegerField( # Bytes blank=False, null=False, default=0) local_disks = IntegerField(blank=False, null=False, default=-1) iscsi_storage = BigIntegerField( # Bytes blank=False, null=False, default=-1) default_pool = ForeignKey(ResourcePool, default=None, null=True, blank=True, editable=True, on_delete=PROTECT) zone = ForeignKey(Zone, verbose_name="Physical zone", default=get_default_zone, editable=True, db_index=True, on_delete=SET_DEFAULT) tags = ArrayField(TextField(), blank=True, null=True, default=list) cpu_over_commit_ratio = FloatField(default=1, validators=[MinValueValidator(0)]) memory_over_commit_ratio = FloatField(default=1, validators=[MinValueValidator(0)]) def __str__(self): return "%s (%s)" % (self.id, self.ip_address if self.ip_address else "No IP") def _as(self, model): """Create a `model` that shares underlying storage with `self`. In other words, the newly returned object will be an instance of `model` and its `__dict__` will be `self.__dict__`. Not a copy, but a reference to, so that changes to one will be reflected in the other. """ new = object.__new__(model) new.__dict__ = self.__dict__ return new def as_bmc(self): """Return a reference to self that behaves as a `BMC`.""" return self._as(BMC) def as_pod(self): """Return a reference to self that behaves as a `Pod`.""" return self._as(Pod) _as_self = { BMC_TYPE.BMC: as_bmc, BMC_TYPE.POD: as_pod, } def as_self(self): """Return a reference to self that behaves as its own type.""" return self._as_self[self.bmc_type](self) def delete(self): """Delete this BMC.""" maaslog.info("%s: Deleting BMC", self) super(BMC, self).delete() def save(self, *args, **kwargs): """Save this BMC.""" super(BMC, self).save(*args, **kwargs) # We let name be blank for the initial save, but fix it before the # save completes. This is because set_random_name() operates by # trying to re-save the BMC with a random hostname, and retrying until # there is no conflict. if self.name == '': self.set_random_name() def set_random_name(self): """Set a random `name`.""" while True: self.name = petname.Generate(2, "-") try: self.save() except ValidationError: pass else: break def clean(self): """ Update our ip_address if the address extracted from our power parameters has changed. """ new_ip = BMC.extract_ip_address(self.power_type, self.power_parameters) current_ip = None if self.ip_address is None else self.ip_address.ip # Set the ip_address field. If we have a bracketed address, assume # it's IPv6, and strip the brackets. if new_ip and new_ip.startswith('[') and new_ip.endswith(']'): new_ip = new_ip[1:-1] if new_ip != current_ip: if new_ip is None: self.ip_address = None else: # Update or create a StaticIPAddress for the new IP. try: # This atomic block ensures that an exception within will # roll back only this block's DB changes. This allows us to # swallow exceptions in here and keep all changes made # before or after this block is executed. with transaction.atomic(): subnet = Subnet.objects.get_best_subnet_for_ip(new_ip) (self.ip_address, _) = StaticIPAddress.objects.get_or_create( ip=new_ip, defaults={ 'alloc_type': IPADDRESS_TYPE.STICKY, 'subnet': subnet, }) except Exception as error: maaslog.info( "BMC could not save extracted IP " "address '%s': '%s'", new_ip, error) @staticmethod def scope_power_parameters(power_type, power_params): """Separate the global, bmc related power_parameters from the local, node-specific ones.""" if not power_type: # If there is no power type, treat all params as node params. return ({}, power_params) power_driver = PowerDriverRegistry.get_item(power_type) if power_driver is None: # If there is no power driver, treat all params as node params. return ({}, power_params) power_fields = power_driver.settings if not power_fields: # If there is no parameter info, treat all params as node params. return ({}, power_params) bmc_params = {} node_params = {} for param_name in power_params: power_field = power_driver.get_setting(param_name) if (power_field and power_field.get('scope') == SETTING_SCOPE.BMC): bmc_params[param_name] = power_params[param_name] else: node_params[param_name] = power_params[param_name] return (bmc_params, node_params) @staticmethod def extract_ip_address(power_type, power_parameters): """ Extract the ip_address from the power_parameters. If there is no power_type, no power_parameters, or no valid value provided in the power_address field, returns None. """ if not power_type or not power_parameters: # Nothing to extract. return None power_driver = PowerDriverRegistry.get_item(power_type) if power_driver is None: maaslog.warning("No power driver for power type %s" % power_type) return None power_type_parameters = power_driver.settings if not power_type_parameters: maaslog.warning("No power driver settings for power type %s" % power_type) return None ip_extractor = power_driver.ip_extractor if not ip_extractor: maaslog.info("No IP extractor configured for power type %s. " "IP will not be extracted." % power_type) return None field_value = power_parameters.get(ip_extractor.get('field_name')) if not field_value: maaslog.warning("IP extractor field_value missing for %s" % power_type) return None extraction_pattern = ip_extractor.get('pattern') if not extraction_pattern: maaslog.warning("IP extractor extraction_pattern missing for %s" % power_type) return None match = re.match(extraction_pattern, field_value) if match: return match.group('address') # no match found - return None return None def get_layer2_usable_rack_controllers(self, with_connection=True): """Return a list of `RackController`'s that have the ability to access this `BMC` directly through a layer 2 connection.""" ip_address = self.ip_address if ip_address is None or ip_address.ip is None or ip_address.ip == '': return set() # The BMC has a valid StaticIPAddress set. Make sure that the subnet # is correct for that BMC. subnet = Subnet.objects.get_best_subnet_for_ip(ip_address.ip) if subnet is not None and self.ip_address.subnet_id != subnet.id: self.ip_address.subnet = subnet self.ip_address.save() # Circular imports. from maasserver.models.node import RackController return RackController.objects.filter_by_url_accessible( ip_address.ip, with_connection=with_connection) def get_routable_usable_rack_controllers(self, with_connection=True): """Return a list of `RackController`'s that have the ability to access this `BMC` through a route on the rack controller.""" routable_racks = [ relationship.rack_controller for relationship in (self.routable_rack_relationships.all( ).select_related("rack_controller")) if relationship.routable ] if with_connection: conn_rack_ids = [client.ident for client in getAllClients()] return [ rack for rack in routable_racks if rack.system_id in conn_rack_ids ] else: return routable_racks def get_usable_rack_controllers(self, with_connection=True): """Return a list of `RackController`'s that have the ability to access this `BMC` either using layer2 or routable if no layer2 are available. """ racks = self.get_layer2_usable_rack_controllers( with_connection=with_connection) if len(racks) == 0: # No layer2 routable rack controllers. Use routable rack # controllers. racks = self.get_routable_usable_rack_controllers( with_connection=with_connection) return racks def get_client_identifiers(self): """Return a list of identifiers that can be used to get the `rpc.common.Client` for this `BMC`. :raise NoBMCAccessError: Raised when no rack controllers have access to this `BMC`. """ rack_controllers = self.get_usable_rack_controllers() identifers = [controller.system_id for controller in rack_controllers] return identifers def is_accessible(self): """If the BMC is accessible by at least one rack controller.""" racks = self.get_usable_rack_controllers(with_connection=False) return len(racks) > 0 def update_routable_racks(self, routable_racks_ids, non_routable_racks_ids): """Set the `routable_rack_controllers` relationship to the new information.""" BMCRoutableRackControllerRelationship.objects.filter( bmc=self.as_bmc()).delete() self._create_racks_relationship(routable_racks_ids, True) self._create_racks_relationship(non_routable_racks_ids, False) def _create_racks_relationship(self, rack_ids, routable): """Create `BMCRoutableRackControllerRelationship` for list of `rack_ids` and wether they are `routable`.""" # Circular imports. from maasserver.models.node import RackController for rack_id in rack_ids: try: rack = RackController.objects.get(system_id=rack_id) except RackController.DoesNotExist: # Possible it was delete before this call, but very very rare. pass BMCRoutableRackControllerRelationship(bmc=self, rack_controller=rack, routable=routable).save()
class Channel(Model): title = CharField(max_length=50) tags = ManyToManyField(Tag)
class AppRelease(TranslatableModel): version = CharField(max_length=256, verbose_name=_('Version'), help_text=_('Version follows Semantic Versioning')) app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'), related_name='releases') # dependencies php_extensions = ManyToManyField( 'PhpExtension', blank=True, through='PhpExtensionDependency', verbose_name=_('PHP extension dependency')) databases = ManyToManyField('Database', blank=True, through='DatabaseDependency', verbose_name=_('Database dependency')) licenses = ManyToManyField('License', verbose_name=_('License')) shell_commands = ManyToManyField( 'ShellCommand', blank=True, verbose_name=_('Shell command dependency')) php_version_spec = CharField(max_length=256, verbose_name=_('PHP version requirement')) platform_version_spec = CharField( max_length=256, verbose_name=_('Platform version requirement')) raw_php_version_spec = CharField( max_length=256, verbose_name=_('PHP version requirement (raw)')) raw_platform_version_spec = CharField( max_length=256, verbose_name=_('Platform version requirement (raw)')) min_int_size = IntegerField(blank=True, default=32, verbose_name=_('Minimum Integer bits'), help_text=_('e.g. 32 for 32bit Integers')) download = URLField(max_length=256, blank=True, verbose_name=_('Archive download URL')) created = DateTimeField(auto_now_add=True, editable=False, verbose_name=_('Created at')) last_modified = DateTimeField(auto_now=True, editable=False, db_index=True, verbose_name=_('Updated at')) signature = TextField( verbose_name=_('Signature'), help_text=_('A signature using the app\'s certificate')) signature_digest = CharField(max_length=256, verbose_name=_('Signature hashing algorithm')) translations = TranslatedFields(changelog=TextField( verbose_name=_('Changelog'), help_text=_('The release changelog. Can contain Markdown'), default='')) is_nightly = BooleanField(verbose_name=_('Nightly'), default=False) class Meta: verbose_name = _('App release') verbose_name_plural = _('App releases') unique_together = (('app', 'version', 'is_nightly'), ) ordering = ['-version'] def can_update(self, user: User) -> bool: return self.app.owner == user or user in self.app.co_maintainers.all() def can_delete(self, user: User) -> bool: return self.can_update(user) def __str__(self) -> str: return '%s %s' % (self.app, self.version) def is_compatible(self, platform_version, inclusive=False): """Checks if a release is compatible with a platform version :param platform_version: the platform version, not required to be semver compatible :param inclusive: if True the check will also return True if an app requires 9.0.1 and the given platform version is 9.0 :return: True if compatible, otherwise false """ min_version = Version(pad_min_version(platform_version)) spec = Spec(self.platform_version_spec) if inclusive: max_version = Version(pad_max_inc_version(platform_version)) return (min_version in spec or max_version in spec) else: return min_version in spec @property def is_unstable(self): return self.is_nightly or '-' in self.version
class Image(Model, AuthorMixin): img = ImageField(upload_to='images/%Y/%m') title = CharField(max_length=255, blank=True, null=True) width = PositiveIntegerField(blank=True, null=True) height = PositiveIntegerField(blank=True, null=True) authors = ManyToManyField(Author, related_name='image_authors') tags = ManyToManyField('Tag') created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) SIZES = {'large': (1600, 900), 'medium': (800, 600), 'square': (250, 250)} JPG_FORMATS = ( 'jpg', 'JPG', 'JPEG', 'jpeg', ) GIF_FORMATS = ( 'gif', 'GIF', ) IMAGE_FORMATS = '.(jpg|JPEG|jpeg|JPG|gif|png|PNG|tiff|tif|dng)' THUMBNAIL_SIZE = 'square' AuthorModel = Author def is_gif(self): """Returns true if image is a gif.""" return self.get_extension() in self.GIF_FORMATS def get_filename(self): """Returns the image filename.""" return os.path.basename(self.img.name) def get_name(self): """Returns the filename without its extension.""" return os.path.splitext(self.img.name)[0] def get_extension(self): """Returns the file extension.""" ext = os.path.splitext(self.img.name)[1] if ext: # Remove period from extension return ext[1:] return ext def get_absolute_url(self): """Returns the full size image URL.""" return settings.MEDIA_URL + str(self.img) def get_medium_url(self): """Returns the medium size image URL.""" if self.is_gif(): return self.get_absolute_url() return '%s%s-%s.jpg' % (settings.MEDIA_URL, self.get_name(), 'medium') def get_thumbnail_url(self): """Returns the thumbnail URL.""" return '%s%s-%s.jpg' % (settings.MEDIA_URL, self.get_name(), self.THUMBNAIL_SIZE) # Overriding def save(self, **kwargs): """Custom save method to process thumbnails and save image dimensions.""" is_new = self.pk is None if is_new: # Make filenames lowercase self.img.name = self.img.name.lower() # Call super method super(Image, self).save(**kwargs) if is_new and self.img: data = self.img.read() if not data: return image = Img.open(StringIO.StringIO(data)) self.width, self.height = image.size super(Image, self).save() name = self.get_name() ext = self.get_extension() for size in self.SIZES.keys(): self.save_thumbnail(image, self.SIZES[size], name, size, ext) def save_thumbnail(self, image, size, name, label, file_type): """Processes and saves a resized thumbnail version of the image.""" width, height = size (imw, imh) = image.size # If image is larger than thumbnail size, resize image if (imw > width) or (imh > height): image.thumbnail(size, Img.ANTIALIAS) # Attach new thumbnail label to image filename name = "%s-%s.jpg" % (name, label) # Image.save format takes JPEG not jpg if file_type in self.JPG_FORMATS: file_type = 'JPEG' # Write new thumbnail to StringIO object image_io = StringIO.StringIO() image.save(image_io, format=file_type, quality=75) # Convert StringIO object to Django File object thumb_file = InMemoryUploadedFile(image_io, None, name, 'image/jpeg', image_io.len, None) # Save the new file to the default storage system default_storage.save(name, thumb_file) def save_tags(self, tag_ids): self.tags.clear() for tag_id in tag_ids: try: tag = Tag.objects.get(id=int(tag_id)) self.tags.add(tag) except Tag.DoesNotExist: pass
class UserProfile(AbstractUser): USER_TYPE_CHOICES = ( (0, "后台用户"), (1, "前台用户"), ) objects = UserManager() username = CharField(max_length=150, unique=True, db_index=True, verbose_name='用户账号') secret = CharField(max_length=255, default=uuid4, verbose_name='加密秘钥') email = CharField(max_length=255, verbose_name="邮箱", null=True, blank=True) mobile = CharField(max_length=255, verbose_name="电话", null=True, blank=True) avatar = TextField(verbose_name="头像", null=True, blank=True) name = CharField(max_length=40, verbose_name="姓名") gender = CharField(max_length=8, verbose_name="性别", null=True, blank=True) remark = TextField(verbose_name="备注", null=True) user_type = IntegerField(default=0, verbose_name="用户类型") post = ManyToManyField(to='Post', verbose_name='关联岗位', db_constraint=False) role = ManyToManyField(to='Role', verbose_name='关联角色', db_constraint=False) dept = ForeignKey(to='Dept', verbose_name='归属部门', on_delete=CASCADE, db_constraint=False, null=True, blank=True) dept_belong_id = CharField(max_length=64, verbose_name="数据归属部门", null=True, blank=True) create_datetime = CreateDateTimeField() update_datetime = UpdateDateTimeField() @property def get_user_interface_dict(self): interface_dict = cache.get(f'permission_interface_dict{self.username}', {}) if not interface_dict: for ele in self.role.filter(status='1', menu__status='1').values( 'menu__interface_path', 'menu__interface_method').distinct(): interface_path = ele.get('menu__interface_path') if interface_path is None or interface_path == '': continue if ele.get('menu__interface_method') in interface_dict: interface_dict[ele.get('menu__interface_method', '')].append(interface_path) else: interface_dict[ele.get('menu__interface_method', '')] = [interface_path] cache.set(f'permission_interface_dict_{self.username}', interface_dict, 84600) return interface_dict @property def delete_cache(self): """ 清空缓存中的接口列表 :return: """ return cache.delete(f'permission_interface_dict_{self.username}') class Meta: verbose_name = '用户管理' verbose_name_plural = verbose_name def __str__(self): if self.name: return f"{self.username}({self.name})" return f"{self.username}"
class EquipmentDataField(Model): """Equipment Data Field.""" RELATED_NAME = 'equipment_data_fields' RELATED_QUERY_NAME = 'equipment_data_field' DEFAULT_UPPER_NUMERIC_NULL = 2**30 # << MaxInt = 2 ** 31 - 1 DEFAULT_LOWER_NUMERIC_NULL = -DEFAULT_UPPER_NUMERIC_NULL equipment_general_type = \ ForeignKey( to=EquipmentGeneralType, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=False, null=False, on_delete=PROTECT) name = \ CharField( verbose_name='Equipment Data Field', blank=False, null=False, db_index=True, max_length=MAX_CHAR_LEN) equipment_data_field_type = \ ForeignKey( to=EquipmentDataFieldType, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=False, null=False, on_delete=PROTECT) logical_data_type = \ ForeignKey( to=LogicalDataType, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=True, null=True, on_delete=PROTECT) numeric_measurement_unit = \ ForeignKey( to=NumericMeasurementUnit, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=True, null=True, on_delete=PROTECT) lower_numeric_null = \ FloatField( blank=False, null=False, default=DEFAULT_LOWER_NUMERIC_NULL) upper_numeric_null = \ FloatField( blank=False, null=False, default=DEFAULT_UPPER_NUMERIC_NULL) min_val = \ FloatField( blank=True, null=True) max_val = \ FloatField( blank=True, null=True) equipment_unique_types = \ ManyToManyField( to='EquipmentUniqueType', related_name=RELATED_NAME + '_reverse', related_query_name=RELATED_QUERY_NAME, blank=True) class Meta: """Metadata.""" verbose_name = 'Equipment Data Field' verbose_name_plural = 'Equipment Data Fields' unique_together = 'equipment_general_type', 'name' ordering = 'equipment_general_type', 'name' def __str__(self): """Return string repr.""" return ( (f'{self.equipment_general_type.name.upper()} ' f'[{self.equipment_data_field_type.name}] ' f'{self.name} [') + (self.logical_data_type.name if self.logical_data_type else 'UNTYPED') + ( f', unit {self.numeric_measurement_unit.name.upper()}' if self.numeric_measurement_unit and self.numeric_measurement_unit.name # noqa: E501 else '') + f', nulls ({self.lower_numeric_null}, {self.upper_numeric_null})' + # noqa: E501 ('' if self.min_val is None else f', min {self.min_val}') + ('' if self.max_val is None else f', max {self.max_val}') + ']') def save(self, *args, **kwargs): """Save.""" self.name = clean_lower_str(self.name) super().save(*args, **kwargs)
class Index(Model): name = CharField(max_length=60) country = CharField(max_length=60) stocks = ManyToManyField(Stock, through="StockOnIndex")
class DiscordUser(Model): id = IntegerField(primary_key=True) has_elements = ManyToManyField(Element) last_ingredients = TextField(null=True)
class Game(Model): """game model""" bgg_id = PositiveIntegerField(primary_key=True) name = CharField(max_length=255, db_index=True) alt_name = JSONField(default=list) year = SmallIntegerField(blank=True, null=True, db_index=True) description = TextField(blank=True, null=True) designer = ManyToManyField("Person", blank=True, related_name="designer_of") artist = ManyToManyField("Person", blank=True, related_name="artist_of") # publisher = ListField(CharField(), blank=True) url = URLField(blank=True, null=True) image_url = JSONField(default=list) video_url = JSONField(default=list) external_link = JSONField(default=list) # list_price = CharField(max_length=100, blank=True, null=True) min_players = PositiveSmallIntegerField(blank=True, null=True, db_index=True) max_players = PositiveSmallIntegerField(blank=True, null=True, db_index=True) min_players_rec = PositiveSmallIntegerField(blank=True, null=True, db_index=True) max_players_rec = PositiveSmallIntegerField(blank=True, null=True, db_index=True) min_players_best = PositiveSmallIntegerField(blank=True, null=True, db_index=True) max_players_best = PositiveSmallIntegerField(blank=True, null=True, db_index=True) min_age = PositiveSmallIntegerField(blank=True, null=True, db_index=True) max_age = PositiveSmallIntegerField(blank=True, null=True, db_index=True) min_age_rec = FloatField(blank=True, null=True, db_index=True) max_age_rec = FloatField(blank=True, null=True, db_index=True) min_time = PositiveSmallIntegerField(blank=True, null=True, db_index=True) max_time = PositiveSmallIntegerField(blank=True, null=True, db_index=True) game_type = ManyToManyField("GameType", blank=True, related_name="games") category = ManyToManyField("Category", blank=True, related_name="games") mechanic = ManyToManyField("Mechanic", blank=True, related_name="games") cooperative = BooleanField(default=False, db_index=True) compilation = BooleanField(default=False, db_index=True) compilation_of = ManyToManyField("self", symmetrical=False, blank=True, related_name="contained_in") # family = ListField(CharField(), blank=True) # expansion = ListField(CharField(), blank=True) implements = ManyToManyField("self", symmetrical=False, blank=True, related_name="implemented_by") integrates_with = ManyToManyField("self", symmetrical=True, blank=True) bgg_rank = PositiveIntegerField(blank=True, null=True, db_index=True) num_votes = PositiveIntegerField(default=0, db_index=True) avg_rating = FloatField(blank=True, null=True, db_index=True) stddev_rating = FloatField(blank=True, null=True, db_index=True) bayes_rating = FloatField(blank=True, null=True, db_index=True) rec_rank = PositiveIntegerField(blank=True, null=True, db_index=True) rec_rating = FloatField(blank=True, null=True, db_index=True) rec_stars = FloatField(blank=True, null=True, db_index=True) complexity = FloatField(blank=True, null=True, db_index=True) language_dependency = FloatField(blank=True, null=True, db_index=True) kennerspiel_score = FloatField(blank=True, null=True, db_index=True) freebase_id = JSONField(default=list) wikidata_id = JSONField(default=list) wikipedia_id = JSONField(default=list) dbpedia_id = JSONField(default=list) luding_id = JSONField(default=list) spielen_id = JSONField(default=list) bga_id = JSONField(default=list) def highest_ranking(self, ranking_type=Ranking.BGG): """Find the highest ever rank of the given type.""" return ( # pylint: disable=no-member self.ranking_set.filter(ranking_type=ranking_type ).order_by("rank", "-date").first()) @property def highest_ranking_bgg(self): """Highest BGG ranking.""" return self.highest_ranking(ranking_type=Ranking.BGG) @property def highest_ranking_factor(self): """Highest factor model ranking.""" return self.highest_ranking(ranking_type=Ranking.FACTOR) @property def highest_ranking_similarity(self): """Highest similarity model ranking.""" return self.highest_ranking(ranking_type=Ranking.SIMILARITY) class Meta: """meta""" ordering = ("-rec_rating", "-bayes_rating", "-avg_rating") indexes = (Index(fields=("-rec_rating", "-bayes_rating", "-avg_rating")), ) def __str__(self): return str(self.name)
class AbstractMailingList(CremeEntity): name = CharField(_('Name of the mailing list'), max_length=80) children = ManyToManyField( settings.EMAILS_MLIST_MODEL, verbose_name=_('Child mailing lists'), symmetrical=False, related_name='parents_set', editable=False, ) contacts = ManyToManyField( settings.PERSONS_CONTACT_MODEL, verbose_name=_('Contacts recipients'), editable=False, ) organisations = ManyToManyField( settings.PERSONS_ORGANISATION_MODEL, verbose_name=_('Organisations recipients'), editable=False, ) creation_label = _('Create a mailing list') save_label = _('Save the mailing list') class Meta: abstract = True # manager_inheritance_from_future = True app_label = 'emails' verbose_name = _('Mailing list') verbose_name_plural = _('Mailing lists') ordering = ('name', ) def __str__(self): return self.name def get_absolute_url(self): return reverse('emails__view_mlist', args=(self.id, )) @staticmethod def get_create_absolute_url(): return reverse('emails__create_mlist') def get_edit_absolute_url(self): return reverse('emails__edit_mlist', args=(self.id, )) @staticmethod def get_lv_absolute_url(): return reverse('emails__list_mlists') def already_in_parents(self, other_ml_id): parents = self.parents_set.all() for parent in parents: if parent.id == other_ml_id: return True for parent in parents: if parent.already_in_parents(other_ml_id): return True return False def already_in_children(self, other_ml_id): children = self.children.all() for child in children: if child.id == other_ml_id: return True for child in children: if child.already_in_children(other_ml_id): return True return False def get_family(self): """Return a dictionary<pk: MailingList> with self and all children, small children etc... """ family = {} self.get_family_aux(family) return family def get_family_aux(self, dic): dic[self.id] = self for child in self.children.filter(is_deleted=False): child.get_family_aux(dic)
class Test(Model): id = UUIDField( primary_key=True, default=uuid4, editable=False, ) label = CharField( verbose_name=_("label"), max_length=32, blank=False, db_index=True, unique=True, ) category = ForeignKey( verbose_name=_("category"), related_name="test_set", to=Category, null=True, blank=True, db_index=True, on_delete=PROTECT, ) theme_set = ManyToManyField( verbose_name=_("theme_set"), related_name="test_set", to=Theme, blank=True, ) category2 = ForeignKey( verbose_name=_("category 2"), related_name="test2_set", to=Category, null=True, blank=True, db_index=True, on_delete=CASCADE, ) theme_set2 = ManyToManyField( verbose_name=_("theme_set 2"), related_name="test2_set", to=Theme, blank=True, ) category3 = ForeignKey( verbose_name=_("category 3"), related_name="test3_set", to=Category, null=True, blank=True, db_index=True, on_delete=CASCADE, ) theme_set3 = ManyToManyField( verbose_name=_("theme_set 3"), related_name="test3_set", to=Theme, blank=True, ) category4 = ForeignKey( verbose_name=_("category 4"), related_name="test4_set", to=Category, null=True, blank=True, db_index=True, on_delete=CASCADE, ) theme_set4 = ManyToManyField( verbose_name=_("theme_set 4"), related_name="test4_set", to=Theme, blank=True, ) category5 = ForeignKey( verbose_name=_("category 5"), related_name="test5_set", to=Category, null=True, blank=True, db_index=True, on_delete=CASCADE, ) theme_set5 = ManyToManyField( verbose_name=_("theme_set 5"), related_name="test5_set", to=Theme, blank=True, ) category6 = ForeignKey( verbose_name=_("category 6"), related_name="test6_set", to=Category, null=True, blank=True, db_index=True, on_delete=CASCADE, ) theme_set6 = ManyToManyField( verbose_name=_("theme_set 6"), related_name="test6_set", to=Theme, blank=True, ) tag_set = ManyToManyField( verbose_name=_("theme_set"), related_name="test_set", to=Tag, through="Mapping", blank=True, ) number = PositiveSmallIntegerField( verbose_name=_("number"), unique=True, ) percent = DecimalField( verbose_name=_("percent"), max_digits=5, decimal_places=2, validators=[MinValueValidator(0), MaxValueValidator(100)], blank=True, null=True, ) progress = FloatField( verbose_name=_("progress"), validators=[MinValueValidator(0), MaxValueValidator(100)], blank=True, null=True, ) grade = IntegerField( verbose_name=_("grade"), validators=[MinValueValidator(-10), MaxValueValidator(10)], blank=True, null=True, ) slug = SlugField( unique=True, max_length=32, editable=True, db_index=True, ) owner = EmailField( verbose_name=_("owner"), blank=True, null=True, ) url = URLField( verbose_name=_("url"), blank=True, null=True, ) key = UUIDField(default=uuid4, ) description = TextField(verbose_name=_("description"), ) active = BooleanField(verbose_name=_("active"), ) highlight = NullBooleanField(verbose_name=_("highlight"), ) creation_date = DateField( verbose_name=_("creation date"), auto_now_add=True, ) last_modification_date = DateField( verbose_name=_("last modification date"), auto_now=True, ) random_date = DateField( verbose_name=_("random date"), blank=True, null=True, ) creation_datetime = DateTimeField( verbose_name=_("creation datetime"), auto_now_add=True, ) last_modification_datetime = DateTimeField( verbose_name=_("last modification datetime"), auto_now=True, ) random_datetime = DateTimeField( verbose_name=_("random datetime"), blank=True, null=True, ) duration = DurationField( verbose_name=_("duration"), blank=True, null=True, ) creation_time = TimeField( verbose_name=_("creation time"), auto_now_add=True, ) last_modification_time = TimeField( verbose_name=_("last modification time"), auto_now=True, ) random_time = TimeField( verbose_name=_("random time"), blank=True, null=True, ) ip = GenericIPAddressField( verbose_name=_("IP v4 ou 6"), protocol="both", blank=True, null=True, ) ipv4 = GenericIPAddressField( verbose_name=_("IP v4 as is"), protocol="IPv4", blank=True, null=True, ) ipv6_forced = GenericIPAddressField( verbose_name=_("IP v6 (ipv4 will be converted)"), protocol="both", unpack_ipv4=True, blank=True, null=True, ) ipv6 = GenericIPAddressField( verbose_name=_("IP v6"), protocol="IPv6", blank=True, null=True, ) raw_data = BinaryField( verbose_name=_("raw data"), max_length=127, editable=True, blank=True, null=True, ) def compute_upload_path(current_object, sub_path, filename): """Describe the image storage path""" today = now() return str( Path.joinpath(*list( map( Path, ( current_object._meta.app_label, # pylint: disable=protected-access current_object._meta.model_name, # pylint: disable=protected-access sub_path, str(today.year), str(today.month), str(uuid4()) + Path(filename).suffix))))) file = FileField( verbose_name=_("file"), max_length=256, upload_to=partial(compute_upload_path, subpath="file"), null=True, blank=True, ) image = ImageField( verbose_name=_("image"), max_length=256, upload_to=partial(compute_upload_path, subpath="image"), null=True, blank=True, ) path = FilePathField( verbose_name=_("path"), path=settings.STATIC_PATH, ) def __str__(self): """Return a string that represent the current object to an end user.""" return self.label class Meta: # pylint: disable=too-few-public-methods """Test Meta class""" verbose_name = _("test") verbose_name_plural = _("tests") ordering = ("number", )
class Product(TimeStampedModel): title = CharField(_("Product Name"), max_length=255, null=True, blank=False) slug = SlugField(unique=True, null=True, blank=True, max_length=500) description = RichTextUploadingField() price = DecimalField(decimal_places=2, max_digits=20, default=0.99) comments = GenericRelation(Comment) added_by = CharField(_("added_by"), max_length=255, null=True, blank=True) inventory = IntegerField(null=True, blank=True, default=0) categories = ManyToManyField("category.Category", help_text="Categorize this Product.") tags = ManyToManyField("category.Tag", help_text="Tag this Product.") featured = BooleanField(default=False) old_stock = BooleanField(default=False) draft = BooleanField(default=False) product_id = CharField(max_length=255, null=True, blank=True) is_digital = BooleanField(default=False) objects = ProductManager() def __str__(self): return str(self.title) @property def old_product(self): created_on = self.created.day ttdays = datetime.timedelta(days=60) if created_on > ttdays: return old_stock == True return old_stock == False def get_downloads(self): qs = self.productfile_set.all() return qs def get_image_url(self): img = self.productimage_set.first() if img: return img.image.url return img @property def get_related_products_by_tags(self): return Product.objects.filter(tags__in=self.tags.all())[0:4] @staticmethod def autocomplete_search_fields(): return "title" class Meta: managed = True verbose_name = "Product" verbose_name_plural = "Products" ordering = ["title", "-created"] def get_absolute_url(self): return f"/shop/products/{self.slug}/{self.id}" def get_update_url(self): return f"{self.get_absolute_url}/update" def get_delete_url(self): return f"{self.get_absolute_url}/delete"
class Conversation(BaseModel): participants = ManyToManyField(User) type = enum.EnumField(ConversationType, default=ConversationType.ONE_ON_ONE) name = MaxLengthCharField(null=True)
class EquipmentInstance(Model): """Equipment Instance.""" RELATED_NAME = 'equipment_instances' RELATED_QUERY_NAME = 'equipment_instance' equipment_general_type = \ ForeignKey( to=EquipmentGeneralType, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=False, null=False, on_delete=PROTECT) equipment_unique_type = \ ForeignKey( to=EquipmentUniqueType, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=True, null=True, on_delete=PROTECT) equipment_facility = \ ForeignKey( to=EquipmentFacility, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=True, null=True, on_delete=PROTECT) name = \ CharField( verbose_name='Equipment Instance', blank=False, null=False, unique=True, db_index=True, max_length=MAX_CHAR_LEN) info = \ JSONField( blank=True, null=True) equipment_unique_type_groups = \ ManyToManyField( to=EquipmentUniqueTypeGroup, related_name=RELATED_NAME, related_query_name=RELATED_QUERY_NAME, blank=True) class Meta: """Metadata.""" verbose_name = 'Equipment Instance' verbose_name_plural = 'Equipment Instances' ordering = 'equipment_general_type', 'equipment_unique_type', 'name' def __str__(self): """Return string repr.""" return (self.equipment_general_type.name.upper() + (f' UnqTp {self.equipment_unique_type.name}' if self.equipment_unique_type else '') + f' #{self.name}') def save(self, *args, **kwargs): """Save.""" self.name = clean_lower_str(self.name) if self.equipment_unique_type and ( self.equipment_unique_type.equipment_general_type != self.equipment_general_type): warnings.warn( message=(f'*** EQUIPMENT INSTANCE #{self.name}: ' f'EQUIPMENT UNIQUE TYPE {self.equipment_unique_type} ' 'NOT OF EQUIPMENT GENERAL TYPE ' f'{self.equipment_general_type} ***')) self.equipment_unique_type = None super().save(*args, **kwargs)
class Article(Publishable, AuthorMixin): parent = ForeignKey('Article', related_name='article_parent', blank=True, null=True) headline = CharField(max_length=255) section = ForeignKey('Section') subsection = ForeignKey('Subsection', related_name='article_subsection', blank=True, null=True) authors = ManyToManyField('Author', related_name='article_authors') topic = ForeignKey('Topic', null=True) tags = ManyToManyField('Tag') is_breaking = BooleanField(default=False) breaking_timeout = DateTimeField(blank=True, null=True) IMPORTANCE_CHOICES = [(i, i) for i in range(1, 6)] importance = PositiveIntegerField(validators=[MaxValueValidator(5)], choices=IMPORTANCE_CHOICES, default=3) READING_CHOICES = ( ('anytime', 'Anytime'), ('morning', 'Morning'), ('midday', 'Midday'), ('evening', 'Evening'), ) reading_time = CharField(max_length=100, choices=READING_CHOICES, default='anytime') class Meta: unique_together = ( ('slug', 'head'), ('parent', 'slug', 'head'), ('parent', 'slug', 'is_published'), ) AuthorModel = Author @property def title(self): return self.headline def get_related(self): return Article.objects.exclude(pk=self.id).filter( section=self.section, is_published=True).order_by('-published_at')[:5] def get_reading_list(self, ref=None, dur=None): articles = self.get_related() name = self.section.name return { 'ids': ",".join([str(a.parent_id) for a in articles]), 'name': name } def is_currently_breaking(self): if self.is_published and self.is_breaking: if self.breaking_timeout: return timezone.now() < self.breaking_timeout return False def save_tags(self, tag_ids): self.tags.clear() for tag_id in tag_ids: try: tag = Tag.objects.get(id=int(tag_id)) self.tags.add(tag) except Tag.DoesNotExist: pass def save_topic(self, topic_id): if topic_id is None: self.topic = None else: try: topic = Topic.objects.get(id=int(topic_id)) topic.update_timestamp() self.topic = topic except Topic.DoesNotExist: pass def get_absolute_url(self): """ Returns article URL. """ return "%s%s/%s/" % (settings.BASE_URL, self.section.slug, self.slug) def get_subsection(self): """ Returns the subsection set in the parent article """ return self.parent.subsection def save_subsection(self, subsection_id): """ Save the subsection to the parent article """ Article.objects.filter(parent_id=self.parent.id).update( subsection_id=subsection_id)
class ElementDeProgramme(CommonModel): evenement = ForeignKey('Evenement', related_name='programme', verbose_name=_('événement'), on_delete=CASCADE) oeuvre = ForeignKey( 'Oeuvre', related_name='elements_de_programme', verbose_name=_('œuvre'), blank=True, null=True, on_delete=PROTECT, help_text=_('Vous pouvez croiser le titre et le nom des auteurs. ' 'Évitez les termes généraux comme « de », « la », « le », ' '« avec ».')) autre = CharField(_('autre'), max_length=500, blank=True) caracteristiques = ManyToManyField(CaracteristiqueDeProgramme, related_name='elements_de_programme', blank=True, verbose_name=_('caractéristiques')) NUMEROTATIONS = ( ('O', _('Numéros')), # O pour Ordered ('B', _('Numéros entre crochets (supposition)')), # B pour Brackets ('U', _('Puce')), # U pour Unordered ('E', _('Absente (entracte, etc)')), # E pour Empty ) numerotation = CharField(_('numérotation'), choices=NUMEROTATIONS, max_length=1, default='O') NUMEROTATIONS_SANS_ORDRE = ( 'U', 'E', ) position = PositiveSmallIntegerField(_('position'), db_index=True) part_d_auteur = DecimalField(_('P. A.'), max_digits=6, decimal_places=2, blank=True, null=True) objects = ElementDeProgrammeManager() class Meta(object): verbose_name = _('élément de programme') verbose_name_plural = _('éléments de programme') ordering = ('position', ) @staticmethod def invalidated_relations_when_saved(all_relations=False): if all_relations: return ('evenement', ) return () def calc_caracteristiques(self, tags=False): if self.pk is None: return '' return self.caracteristiques.html(tags=tags, caps=False) calc_caracteristiques.allow_tags = True calc_caracteristiques.short_description = _('caractéristiques') @property @model_method_cached() def numero(self): if hasattr(self, '_numero'): return self._numero if self.numerotation in self.NUMEROTATIONS_SANS_ORDRE: return '' return self.evenement.programme.exclude( Q(position__gt=self.position) | Q(numerotation__in=self.NUMEROTATIONS_SANS_ORDRE)).count() def save(self, *args, **kwargs): if self.position is None: n = self.evenement.programme.aggregate(n=Max('position'))['n'] self.position = 0 if n is None else n + 1 super(ElementDeProgramme, self).save(*args, **kwargs) @model_method_cached() def html(self, tags=True): has_pk = self.pk is not None distribution = '' add_distribution = False if has_pk: distribution = self.distribution.all() if distribution: distribution = distribution.html(tags=tags) add_distribution = True if self.oeuvre: out = self.oeuvre.html(tags) elif self.autre: out = self.autre elif distribution: out = distribution add_distribution = False else: warnings.warn(f'Il manque des champs ' f'dans <{self.__class__.__name__} pk={self.pk}>') return '' caracteristiques = self.calc_caracteristiques(tags=tags) if caracteristiques: out += f' [{caracteristiques}]' if add_distribution: out += f'. — {distribution}' return mark_safe(out) html.short_description = _('rendu HTML') html.allow_tags = True def __str__(self): return strip_tags(self.html(False)) @staticmethod def autocomplete_search_fields(): return ( 'oeuvre__prefixe_titre__unaccent__icontains', 'oeuvre__titre__unaccent__icontains', 'oeuvre__prefixe_titre_secondaire__unaccent__icontains', 'oeuvre__titre_secondaire__unaccent__icontains', 'oeuvre__genre__nom__unaccent__icontains', )
class Dummy(Model): name = CharField(max_length='100') moron = ForeignKey('Moron') idiots = ManyToManyField('Idiot')
class Evenement(AutoriteModel): debut = AncrageSpatioTemporel(('date', ), verbose_name=_('début')) fin = AncrageSpatioTemporel(verbose_name=_('fin')) programme_incomplet = BooleanField(_('programme incomplet'), default=False) relache = BooleanField(_('relâche'), default=False, db_index=True) circonstance = CharField(_('circonstance'), max_length=500, blank=True) caracteristiques = ManyToManyField(CaracteristiqueDeProgramme, related_name='evenements', blank=True, verbose_name=_('caractéristiques')) recette_generale = DecimalField(_('recette générale'), max_digits=7, decimal_places=2, blank=True, null=True) recette_par_billets = CharField( _('recette par titre de billets'), max_length=30, validators=[plus_separated_integers_validator], blank=True) objects = EvenementManager() class Meta(object): verbose_name = _('événement') verbose_name_plural = _('événements') ordering = ('debut_date', 'debut_heure', 'debut_lieu', 'debut_lieu_approx') permissions = (('can_change_status', _('Peut changer l’état')), ) @staticmethod def invalidated_relations_when_saved(all_relations=False): if all_relations: return ('dossiers', ) return () @permalink def get_absolute_url(self): return 'evenement_pk', (self.pk, ) def permalien(self): return self.get_absolute_url() def link(self): return href(self.get_absolute_url(), force_text(self)) link.short_description = _('lien') link.allow_tags = True def calc_caracteristiques(self, tags=True, caps=True): if self.pk is None: return '' return self.caracteristiques.html(tags=tags, caps=caps) def get_meta_name(self, tags=False): if self.circonstance: out = self.circonstance else: distribution = self.distribution.all() if distribution: out = distribution.html(tags=tags) else: programme = self.programme.all() if programme.exists(): element = programme[0] out = element.oeuvre or element.autre else: return '' return microdata(out, 'summary', tags=tags) def html(self, tags=True): relache = '' circonstance = '' if self.circonstance: circonstance = hlp(self.circonstance, ugettext('circonstance'), tags) if self.relache: relache = microdata(ugettext('Relâche'), 'eventType', tags=tags) lieu = microdata(self.debut.lieu_str(tags), 'location', tags=tags) return mark_safe( str_list((lieu, circonstance, self.debut.heure_str(), relache))) html.short_description = _('rendu HTML') html.allow_tags = True def has_program(self): if self.relache: return True if hasattr(self, '_has_program'): return self._has_program return self.programme.exists() has_program.short_description = _('programme') has_program.boolean = True def has_source(self): if hasattr(self, '_has_source'): return self._has_source return self.sources.exists() has_source.short_description = _('source') has_source.boolean = True has_source.admin_order_field = 'sources' @property def oeuvres(self): return apps.get_model( 'libretto', 'Oeuvre').objects.filter(elements_de_programme__evenement=self) def get_saisons(self): # TODO: Gérer les lieux de fin. qs = apps.get_model('libretto', 'Saison').objects.filter( debut__lte=self.debut_date, fin__gte=self.debut_date) extra_where = """ ensemble_id IN (( SELECT ensemble_id FROM libretto_elementdedistribution WHERE evenement_id = %s ) UNION ( SELECT distribution.ensemble_id FROM libretto_elementdeprogramme AS programme INNER JOIN libretto_elementdedistribution AS distribution ON (distribution.element_de_programme_id = programme.id) WHERE programme.evenement_id = %s))""" extra_params = [self.pk, self.pk] if self.debut_lieu_id is not None: extra_where += ' OR lieu_id = %s' extra_params.append(self.debut_lieu_id) return qs.extra(where=(extra_where, ), params=extra_params) def clean(self): if self.fin_lieu is not None and self.debut_lieu is None: raise ValidationError( _('Le lieu de fin est rempli sans lieu de début. ' 'Merci de retirer le lieu de fin ' 'ou remplir le lieu de début.')) def __str__(self): out = self.debut.date_str(False) out = capfirst(out) out += f'\u00A0> {self.html(False)}' return strip_tags(out) def related_label(self): return href(reverse('admin:libretto_evenement_change', args=(self.pk, )), force_text(self), new_tab=True) @staticmethod def autocomplete_search_fields(): return ( 'circonstance__unaccent__icontains', 'debut_lieu__nom__unaccent__icontains', 'debut_lieu__parent__nom__unaccent__icontains', 'debut_date__icontains', 'debut_heure__icontains', 'debut_lieu_approx__unaccent__icontains', 'debut_date_approx__unaccent__icontains', 'debut_heure_approx__unaccent__icontains', )
class App(TranslatableModel): objects = AppManager() id = CharField(max_length=256, unique=True, primary_key=True, verbose_name=_('ID'), help_text=_('app ID, identical to folder name')) categories = ManyToManyField('Category', verbose_name=_('Category')) translations = TranslatedFields( name=CharField(max_length=256, verbose_name=_('Name'), help_text=_('Rendered app name for users')), summary=CharField( max_length=256, verbose_name=_('Summary'), help_text=_('Short text describing the app\'s purpose')), description=TextField(verbose_name=_('Description'), help_text=_('Will be rendered as Markdown'))) # resources user_docs = URLField(max_length=256, blank=True, verbose_name=_('User documentation URL')) admin_docs = URLField(max_length=256, blank=True, verbose_name=_('Admin documentation URL')) developer_docs = URLField(max_length=256, blank=True, verbose_name=_('Developer documentation URL')) issue_tracker = URLField(max_length=256, blank=True, verbose_name=_('Issue tracker URL')) website = URLField(max_length=256, blank=True, verbose_name=_('Homepage')) discussion = URLField(max_length=256, blank=True, verbose_name=_('Forum')) created = DateTimeField(auto_now_add=True, editable=False, verbose_name=_('Created at')) last_modified = DateTimeField(auto_now=True, editable=False, db_index=True, verbose_name=_('Updated at')) owner = ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('App owner'), on_delete=CASCADE, related_name='owned_apps') co_maintainers = ManyToManyField(settings.AUTH_USER_MODEL, blank=True, verbose_name=_('Co-Maintainers'), related_name='co_maintained_apps') authors = ManyToManyField('AppAuthor', blank=True, related_name='apps', verbose_name=_('App authors')) is_featured = BooleanField(verbose_name=_('Featured'), default=False) rating_recent = FloatField(verbose_name=_('Recent rating'), default=0.5) rating_overall = FloatField(verbose_name=_('Overall rating'), default=0.5) rating_num_recent = IntegerField( verbose_name=_('Number of recently submitted ratings'), default=0) rating_num_overall = IntegerField( verbose_name=_('Number of overall submitted ratings'), default=0) last_release = DateTimeField(editable=False, db_index=True, verbose_name=_('Last release at'), default=timezone.now) certificate = TextField(verbose_name=_('Certificate')) ownership_transfer_enabled = BooleanField( verbose_name=_('Ownership transfer enabled'), default=False, help_text=_('If enabled, a user can try to register the same app ' 'again using the public certificate and signature. If he ' 'does, the app will be transferred to him.')) is_integration = BooleanField(verbose_name=_('Integration (i.e. Outlook ' 'plugin)'), default=False) approved = BooleanField(verbose_name=_('Used to approve integrations'), default=False) class Meta: verbose_name = _('App') verbose_name_plural = _('Apps') def __str__(self) -> str: return self.name def can_update(self, user: User) -> bool: return self.owner == user or user in self.co_maintainers.all() def can_delete(self, user: User) -> bool: return self.owner == user @property def discussion_url(self): if self.discussion: return self.discussion else: return '%s/c/apps/%s' % (settings.DISCOURSE_URL, self.id.replace('_', '-')) def _get_grouped_releases(self, get_release_func): releases = NextcloudRelease.objects.all() versions = map(lambda r: r.version, releases) compatible_releases = map(lambda v: (v, get_release_func(v)), versions) grouped_releases = group_by_main_version(dict(compatible_releases)) # deduplicate releases result = {} for version, releases in grouped_releases.items(): result[version] = list(distinct(releases, lambda r: r.version)) return result def releases_by_platform_v(self): """Looks up all compatible stable releases for each platform version. Example of returned dict: {'9.1': [<AppRelease object>, <AppRelease object>], '9.0': [<AppRelease object>]} :return dict with all compatible stable releases for each platform version. """ return self._get_grouped_releases(self.compatible_releases) def unstable_releases_by_platform_v(self): """Looks up all compatible unstable releases for each platform version. Example of returned dict: {'9.1': [<AppRelease object>, <AppRelease object>], '9.0': [<AppRelease object>]} :return dict with all compatible unstable releases for each platform version. """ return self._get_grouped_releases(self.compatible_unstable_releases) def latest_releases_by_platform_v(self): """Looks up the latest stable and unstable release for each platform version. Example of returned dict: {'9.1': { 'stable': <AppRelease object>, 'unstable': <AppRelease object> }, '9.0': { 'stable': <AppRelease object> }} :return dict with the latest stable and unstable release for each platform version. """ stable = self.releases_by_platform_v() unstable = self.unstable_releases_by_platform_v() def filter_latest(pair): version, releases = pair return (version, self._latest(releases)) latest_stable = dict(map(filter_latest, stable.items())) latest_unstable = dict(map(filter_latest, unstable.items())) all_versions = set(chain(latest_stable.keys(), latest_unstable.keys())) def stable_or_unstable_releases(ver): return (ver, { 'stable': latest_stable.get(ver, None), 'unstable': latest_unstable.get(ver, None) }) return dict(map(stable_or_unstable_releases, all_versions)) def compatible_releases(self, platform_version, inclusive=True): """Returns all stable releases of this app that are compatible with the given platform version. :param inclusive: Use inclusive version check (see AppRelease.is_compatible()). :return a sorted list of all compatible stable releases. """ return sorted( filter( lambda r: r.is_compatible(platform_version, inclusive) and not r.is_unstable, self.releases.all()), key=lambda r: AppSemVer(r.version, r.is_nightly, r.last_modified), reverse=True) def compatible_unstable_releases(self, platform_version, inclusive=True): """Returns all unstable releases of this app that are compatible with the given platform version. :param inclusive: Use inclusive version check (see AppRelease.is_compatible()). :return a sorted list of all compatible unstable releases. """ return sorted( filter( lambda r: r.is_compatible(platform_version, inclusive) and r. is_unstable, self.releases.all()), key=lambda r: AppSemVer(r.version, r.is_nightly, r.last_modified), reverse=True) def _latest(self, releases): try: return max(releases, key=lambda r: AppSemVer(r.version, r.is_nightly, r. last_modified)) except ValueError: return None def save(self, *args, **kwargs): # If the certificate has changed, delete all releases. try: if self.pk is not None: orig = App.objects.get(pk=self.pk) current = self.certificate.replace('\r', '').strip() former = orig.certificate.replace('\r', '').strip() # for some reason the django admin inserts \r\n for \n so # saving a model in the admin with the same cert kills all # releases if current != former: self.releases.all().delete() except self.DoesNotExist: pass super().save(*args, **kwargs)
class TBaz(Model): foo = ManyToManyField(TFoo) class Meta: ordering = ('pk', )
class DNSResource(CleanSave, TimestampedModel): """A `DNSResource`. :ivar name: The leftmost label for the resource. (No dots.) :ivar domain: Which (forward) DNS zone does this resource go in. :ivar ip_addresses: many-to-many linkage to `StaticIPAddress`. :ivar objects: An instance of the class :class:`DNSResourceManager`. """ class Meta(DefaultMeta): """Needed for South to recognize this model.""" verbose_name = "DNSResource" verbose_name_plural = "DNSResources" objects = DNSResourceManager() # If name is blank or None, then we'll use $IFACE.$NODENAME.$DOMAIN (and # $NODENAME.$DOMAIN if this is the pxeboot interface), otherwise we'll use # only NAME.$DOMAIN. # There can be more than one name=None entry, so unique needs to be False. # We detect and reject duplicates in clean() # This could be as many as 3 dot-separated labels, because of SRV records. name = CharField( max_length=191, editable=True, null=True, blank=True, unique=False ) # Different resource types can have different TTL values, though all of the # records of a given RRType for a given FQDN "must" have the same TTL. # If the DNS zone file has different TTLs for the same RRType on a label, # then BIND uses the first one in the file, and logs something similar to: # /etc/bind/maas/zone.maas:25: TTL set to prior TTL (10) # We allow this condition to happen so that the user has a hope of changing # TTLs for a multi-entry RRset. # TTL for any ip_addresses: non-address TTLs come from DNSData address_ttl = PositiveIntegerField(default=None, null=True, blank=True) domain = ForeignKey( Domain, default=get_default_domain, editable=True, on_delete=PROTECT ) ip_addresses = ManyToManyField( "StaticIPAddress", editable=True, blank=True ) # DNSData model has non-ipaddress entries. def __unicode__(self): return "name=%s" % self.get_name() def __str__(self): return "name=%s" % self.get_name() @property def fqdn(self): """Fully qualified domain name for this DNSResource. Return the FQDN for this DNSResource. """ if self.name == "@": return self.domain.name else: return "%s.%s" % (self.name, self.domain.name) def has_static_ip(self): return self.ip_addresses.exclude( alloc_type=IPADDRESS_TYPE.DISCOVERED ).exists() def get_addresses(self): """Return all addresses associated with this FQDN.""" # Since Node.hostname is unique, this will be at most 1 node. node = Node.objects.filter( hostname=self.name, domain_id=self.domain_id ) ips = [ip.get_ip() for ip in self.ip_addresses.all()] if node.exists(): ips += node[0].static_ip_addresses() return ips def get_name(self): """Return the name of the dnsresource.""" return self.name def clean(self, *args, **kwargs): # Avoid recursive imports. from maasserver.models.dnsdata import DNSData # make sure that we have a domain if self.domain is None or self.domain == "": self.domain = Domain.objects.get_default_domain() # if we have a name, make sure that it is unique in our dns zone. if self.id is None and self.name is not None and self.name != "": rrset = DNSResource.objects.filter( name=self.name, domain=self.domain ) if rrset.count() > 0: raise ValidationError( "Labels must be unique within their zone." ) # If we have an ip addresses, then we need to have a valid name. # TXT records don't require that we have much at all. if self.id is not None and self.ip_addresses.count() > 0: validate_dnsresource_name(self.name, "A") # This path could be followed if the user is adding a USER_RESERVED # ip address, where the FQDN already has a CNAME assigned to it. # Node.fqdn takes a different path, and should win when it comes to # DNS generation. num_cname = DNSData.objects.filter( dnsresource_id=self.id, rrtype="CNAME" ).count() if num_cname > 0: raise ValidationError("Cannot add address: CNAME present.") super().clean(*args, **kwargs) def render_json(self, system_id): """Render json. System_id is the system_id for the node, if one exists. Addresses are rendered in the calling function.""" return sorted( [ { "hostname": self.name, "ttl": data.ttl, "rrtype": data.rrtype, "rrdata": data.rrdata, "system_id": system_id, } for data in self.dnsdata_set.all() ] )
class FieldFromModelManyToManyTest(Model): foo_many_to_many = ManyToManyField(Foo)
class TestChild(TestParent): public = BooleanField(default=False) permissions = ManyToManyField('auth.Permission', blank=True)
class Project(models.Model): """ The make_cdc_flu_contests_project_app class representing a forecast challenge, including metadata, core data, targets, and model entries. """ # w/out related_name we get: forecast_app.Project.model_owners: # (fields.E304) Reverse accessor for 'Project.model_owners' clashes with reverse accessor for 'Project.owner'. owner = models.ForeignKey(User, related_name='project_owner', on_delete=models.SET_NULL, blank=True, null=True, help_text="The project's owner.") is_public = models.BooleanField( default=True, help_text= "Controls project visibility. False means the project is private and " "can only be accessed by the project's owner or any of its model_owners. " "True means it is publicly accessible.") model_owners = ManyToManyField( User, blank=True, help_text= "Users who are allowed to create, edit, and delete ForecastModels " "in this project. Or: non-editing users who simply need access " "to a private project. Use control/command click to add/remove from " "the list. ") name = models.TextField() WEEK_TIME_INTERVAL_TYPE = 'w' BIWEEK_TIME_INTERVAL_TYPE = 'b' MONTH_TIME_INTERVAL_TYPE = 'm' TIME_INTERVAL_TYPE_CHOICES = ((WEEK_TIME_INTERVAL_TYPE, 'Week'), (BIWEEK_TIME_INTERVAL_TYPE, 'Biweek'), (MONTH_TIME_INTERVAL_TYPE, 'Month')) time_interval_type = models.CharField( max_length=1, choices=TIME_INTERVAL_TYPE_CHOICES, default=WEEK_TIME_INTERVAL_TYPE, help_text="Used when visualizing the x axis label.") visualization_y_label = models.TextField( help_text="Used when visualizing the Y axis label.") description = models.TextField( help_text= "A few paragraphs describing the project. Please see documentation for" "what should be included here - 'real-time-ness', time_zeros, etc.") home_url = models.URLField(help_text="The project's home site.") logo_url = models.URLField(blank=True, null=True, help_text="The project's optional logo image.") core_data = models.URLField( help_text= "Directory or Zip file containing data files (e.g., CSV files) made made available to everyone in " "the challenge, including supplemental data like Google queries or weather." ) def __repr__(self): return str((self.pk, self.name)) def __str__(self): # todo return basic_str(self) def save(self, *args, **kwargs): """ Validates my TimeZero.timezero_dates for uniqueness. """ found_timezero_dates = [] for timezero in self.timezeros.all(): if timezero.timezero_date not in found_timezero_dates: found_timezero_dates.append(timezero.timezero_date) else: raise ValidationError( "found duplicate TimeZero.timezero_date: {}".format( timezero.timezero_date)) # done super().save(*args, **kwargs) def time_interval_type_as_str(self): """ :return: my time_interval_type as a human-friendly string from TIME_INTERVAL_TYPE_CHOICES """ for db_value, human_readable_value in Project.TIME_INTERVAL_TYPE_CHOICES: if db_value == self.time_interval_type: return human_readable_value def get_absolute_url(self): return reverse('project-detail', args=[str(self.pk)]) def get_class(self): """ :return: view utility that simply returns a my class as a string. used by delete_modal_snippet.html """ return self.__class__.__name__ def html_id(self): """ :return: view utility that returns a unique HTML id for this object. used by delete_modal_snippet.html """ return self.__class__.__name__ + '_' + str(self.pk) # # season-related utilities # def seasons(self): """ :return: list of season names for this project based on my timezeros """ return list( self.timezeros.filter( is_season_start=True).order_by('timezero_date').values_list( 'season_name', flat=True)) def timezeros_in_season(self, season_name): """ Utility that returns a sorted list of TimeZeros for season_name. :param season_name: a valid season name (see seasons()) or None, which is used to access TimeZeros that have no season. For the latter, there are two cases: 1) there are no seasons at all 2) there are seasons, but the first starts after the first TimeZero, i.e., my TimeZeros start with some non-season ones that are followed by some seasons :return: two cases based on whether season_name is None. 1) If not None: returns a list of TimeZeros that are within season_name, i.e., those that start with the TimeZero named season_name and go TO the next season, or to the end if season_name is the last season. 2) If None: returns based on the two cases listed above for season_name: 1) no seasons at all: return all TimeZeros. 2) starts with some non-seasons: return those up TO the first season. """ # start with all TimeZeros - case #1 (no seasons at all), and filter as needed season_timezeros_qs = self.timezeros.all() if season_name: season_tz = season_timezeros_qs.filter( season_name=season_name).first() if not season_tz: raise RuntimeError( "invalid season_name. season_name={}, seasons={}".format( season_name, self.seasons())) season_timezeros_qs = season_timezeros_qs.filter( timezero_date__gte=season_tz.timezero_date) next_season_tz = season_timezeros_qs \ .filter(is_season_start=True, timezero_date__gt=season_tz.timezero_date) \ .first() if next_season_tz: season_timezeros_qs = season_timezeros_qs.filter( timezero_date__lt=next_season_tz.timezero_date) else: # no season_name first_season_tz = season_timezeros_qs.filter( is_season_start=True).first() if first_season_tz: # case #2 (seasons after initial TZs) season_timezeros_qs = season_timezeros_qs.filter( timezero_date__lt=first_season_tz.timezero_date) return list(season_timezeros_qs.order_by('timezero_date')) def start_end_dates_for_season(self, season_name): """ :param season_name: same as timezeros_in_season() - can be None :return: 2-tuple: (start_date, end_date) for season_name. this is a closed interval - both are included. Note that start_date == end_date if there is only one TimeZero. returns None if no TimeZeros found """ timezeros = self.timezeros_in_season(season_name) if len(timezeros) == 0: return None return timezeros[0].timezero_date, timezeros[-1].timezero_date def season_name_containing_timezero(self, timezero, timezeros=None): """ :return: season_name of the season that contains timezero, or None if it's not in a season. timezeros, if passed, allows optimizing by callers who compute timezeros only once. """ timezeros = timezeros or self.timezeros.all() if timezero not in timezeros: raise RuntimeError( "TimeZero not found in timezeros: timezero={}, timezeros={}". format(timezero, timezeros)) # order my timezeros by date and then iterate from earliest to latest, keeping track of the current season and # returning the first match. must handle two cases: the earliest timezero defines a season, or not containing_season_name = None # return value. updated in loop for project_timezero in timezeros.order_by('timezero_date'): if project_timezero.is_season_start: containing_season_name = project_timezero.season_name if project_timezero == timezero: return containing_season_name def timezero_to_season_name(self): """ :return: a dict mapping each of my timezeros -> containing season name """ _timezero_to_season_name = {} containing_season_name = None for timezero in self.timezeros.order_by('timezero_date'): if timezero.is_season_start: containing_season_name = timezero.season_name _timezero_to_season_name[timezero] = containing_season_name return _timezero_to_season_name # # time-related utilities # def forecasts_for_timezero(self, timezero): """ :param timezero: a TimeZero :return: a list of Forecasts for timezero for each of my models """ return [ forecast_model.forecast_for_time_zero(timezero) for forecast_model in self.models.all() ] def time_zero_for_timezero_date(self, timezero_date): """ :return: the first TimeZero in me that has a timezero_date matching timezero_date """ return self.timezeros.filter(timezero_date=timezero_date).first() def time_interval_type_to_foresight(self): """ :return: my time_interval_type formatted for D3-Foresight's pointType """ return dict(Project.TIME_INTERVAL_TYPE_CHOICES)[ self.time_interval_type].lower() def last_update(self): """ Returns the datetime.datetime of the last time this project was "updated": the latest Forecast's created_at. Returns None if no forecasts. """ from .forecast import Forecast # avoid circular imports latest_forecast = Forecast.objects.filter( forecast_model__project=self).order_by('-created_at').first() return latest_forecast.created_at if latest_forecast else None # # count-related functions # def num_models_forecasts(self): """ :return: a 2-tuple: (num_models, num_forecasts) """ from .forecast import Forecast # avoid circular imports num_models = self.models.filter(project=self, is_oracle=False).count() num_forecasts = Forecast.objects.filter( forecast_model__project=self, forecast_model__is_oracle=False).count() return num_models, num_forecasts def num_pred_ele_rows_all_models(self, is_oracle=True): """ :return: the total number of PredictionElements across all my models' forecasts, for all types of Predictions. can be very slow for large databases """ from forecast_app.models import PredictionElement # avoid circular imports return PredictionElement.objects.filter( forecast__forecast_model__project=self, forecast__forecast_model__is_oracle=is_oracle).count() # # visualization-related functions # def step_ahead_targets(self): return self.targets.filter(is_step_ahead=True) \ .order_by('name') def numeric_targets(self): """ :return: a list of Targets whose values are numeric - either int or float. used by scoring """ from forecast_app.models import Target # avoid circular imports return self.targets.filter(type__in=[Target.CONTINUOUS_TARGET_TYPE, Target.DISCRETE_TARGET_TYPE]) \ .order_by('name')
class Competition(Model): def __str__(self): s = self.slug s += ": " + ", ".join([i.slug for i in self.questionsets.all()]) return s def get_absolute_url(self): return reverse('competition_detail', kwargs={'slug': str(self.slug)}) title = CharField(max_length=256, null=True, blank=True, verbose_name=_("title")) promoted = BooleanField(default=False, verbose_name=_("promoted")) public = BooleanField( default=True, verbose_name=_("public competition"), help_text=_( "Public competitions are listed on the competiton list page")) slug = SlugField(unique=True, verbose_name=_("slug")) administrator_code_generator = ForeignKey( CodeGenerator, related_name='administrator_code_competition_set', verbose_name=_("administrator code generator"), on_delete=CASCADE) competitor_code_generator = ForeignKey( CodeGenerator, related_name='competitor_code_competition_set', verbose_name=_("competitor code generator"), on_delete=CASCADE) questionsets = ManyToManyField('QuestionSet', through='CompetitionQuestionSet', verbose_name=_("Question sets")) start = DateTimeField(verbose_name=_("start")) # duration in seconds duration = IntegerField( default=45 * 60, # 60s * 45 = 1h. verbose_name=_("duration"), help_text=_("Duration of the competition in seconds")) end = DateTimeField(verbose_name=_("end")) motd = TextField(blank=True, verbose_name=_("message of the day")) @property def is_over(self): """ Return true when competition is finished. """ return self.end < timezone.now() @property def guests_allowed(self): return self.competitionquestionset_set.filter( guest_code__isnull=False).exists() @staticmethod def ongoing_competitions(): """ Return a list of ongoing competitions. Complexity: linearly dependent on the number of all competitions. """ now = timezone.now() return Competition.objects.filter(start__lte=now, end__gte=now) @property def is_ongoing(self): return self.start <= timezone.now() <= self.end @classmethod def get_cached_by_slug(cls, slug): c = cache.get('competition_by_slug__' + slug, None) if c is None: c = cls.objects.select_related( 'administrator_code_generator', 'competitor_code_generator', 'administrator_code_generator__format', 'competitor_code_generator__format', ).get(slug=slug) print(" adding", slug, "to cache") cache.set('competition_by_slug__' + slug, c) return c def expand_competitor_code(self, short_code, competition_questionset): sep = self.competitor_code_generator.format.separator return competition_questionset.slug_str() + sep + short_code def split_competitor_code(self, access_code): sep = self.competitor_code_generator.format.separator return access_code.split(sep) def grade_answers(self, grader_runtime_manager=None, update_graded=False, regrade=False): grader_runtime_manager = graders.init_runtimes(grader_runtime_manager) if grader_runtime_manager is None: grader_runtime_manager = graders.RuntimeManager() grader_runtime_manager.start_runtimes() if update_graded: self.update_graded_answers( regrade=regrade, grader_runtime_manager=grader_runtime_manager) if regrade: return for cq in CompetitionQuestionSet.objects.filter(competition=self): cq.grade_answers(grader_runtime_manager=grader_runtime_manager, update_graded=False, regrade=regrade) def update_graded_answers(self, regrade=False, grader_runtime_manager=None): grader_runtime_manager = graders.init_runtimes(grader_runtime_manager) for cq in CompetitionQuestionSet.objects.filter(competition=self): cq.update_graded_answers( regrade=regrade, grader_runtime_manager=grader_runtime_manager) def admin_privilege_choices(self, access_code): return filter( lambda x: self.administrator_code_generator.code_matches( access_code, {'admin_privileges': [x[0]]}), ADMIN_PRIVILEGES) def allowed_effect_choices(self, access_code): return filter( lambda x: self.administrator_code_generator.code_matches( access_code, {'allowed_effects': [x[0]]}), CODE_EFFECTS) def competitor_privilege_choices(self, access_code): return filter( lambda x: self.administrator_code_generator.code_matches( access_code, {'competitor_privileges': [x[0]]}), COMPETITOR_PRIVILEGES) def max_admin_code_data(self, access_code): return { 'admin_privileges': [i[0] for i in self.admin_privilege_choices(access_code)], 'allowed_effects': [i[0] for i in self.allowed_effect_choices(access_code)], 'competitor_privileges': [i[0] for i in self.competitor_privilege_choices(access_code)] } def max_competitor_code_data(self, access_code): return { 'competitor_privileges': [i[0] for i in self.competitor_privilege_choices(access_code)] } def competitor_code_create(self, access_code, competition_questionset=None, code_data=None): if code_data is None: code_data = self.max_competitor_code_data(access_code) if competition_questionset is not None: code_data['competition_questionset'] = [ competition_questionset.slug_str() ] c = self.competitor_code_generator.create_code(code_data) c.save() return c def master_code_create(self): c = self.administrator_code_generator.create_code({ 'admin_privileges': [i[0] for i in ADMIN_PRIVILEGES], 'competitor_privileges': [i[0] for i in COMPETITOR_PRIVILEGES], 'allowed_effects': [i[0] for i in CODE_EFFECTS], }) c.save() return c def admin_code_create(self, access_code, code_data=None): if code_data is None: code_data = self.max_admin_code_data(access_code) c = self.administrator_code_generator.create_code(code_data) c.save() return c
class AbstractDocument(CremeEntity): title = CharField(_(u'Name'), max_length=100) description = TextField(_(u'Description'), blank=True).set_tags(optional=True) filedata = FileField(_(u'File'), max_length=500, upload_to='upload/documents') linked_folder = ForeignKey( settings.DOCUMENTS_FOLDER_MODEL, verbose_name=_(u'Folder'), on_delete=PROTECT, ) mime_type = ForeignKey( MimeType, verbose_name=_(u'MIME type'), editable=False, on_delete=PROTECT, null=True, ) categories = ManyToManyField( DocumentCategory, verbose_name=_(u'Categories'), # related_name='+', blank=True, ).set_tags(optional=True) creation_label = _(u'Create a document') save_label = _(u'Save the document') class Meta: abstract = True manager_inheritance_from_future = True app_label = 'documents' verbose_name = _(u'Document') verbose_name_plural = _(u'Documents') ordering = ('title', ) def __str__(self): return u'{} - {}'.format(self.linked_folder, self.title) def get_absolute_url(self): return reverse('documents__view_document', args=(self.id, )) @staticmethod def get_create_absolute_url(): return reverse('documents__create_document') def get_edit_absolute_url(self): return reverse('documents__edit_document', args=(self.id, )) @staticmethod def get_lv_absolute_url(): return reverse('documents__list_documents') @staticmethod def get_linkeddoc_relations(entity): return Relation.objects.filter(subject_entity=entity.id, type=REL_SUB_RELATED_2_DOC) def get_dl_url(self): import os return settings.MEDIA_URL + str(self.filedata).replace(os.sep, '/') def get_entity_summary(self, user): if not user.has_perm_to_view(self): return self.allowed_str(user) if self.mime_type.is_image: return format_html( u'<img class="entity-summary" src="{url}" alt="{name}" title="{name}"/>', url=self.get_dl_url(), name=self.title, ) # return super(AbstractDocument, self).get_entity_summary(user) return super().get_entity_summary(user) def save(self, *args, **kwargs): if not self.pk: # Creation mime_name = guess_type(self.filedata.name)[0] if mime_name is not None: self.mime_type = MimeType.objects.get_or_create( name=mime_name)[0] # super(AbstractDocument, self).save(*args, **kwargs) super().save(*args, **kwargs)
class Study(Model): EEA = 'eea' OTHER = 'other' REQUESTED_BY_CHOICES = ( (EEA, 'EEA'), (OTHER, 'Other'), ) YES = 1 NO = 0 YES_NO_CHOICES = ( ('', '----'), (YES, 'Yes'), (NO, 'No'), ) POLICY = 'policy' NON_POLICY = 'non_policy' PURPOSE_CHOICES = ( (POLICY, 'Support to policy'), (NON_POLICY, 'Non-policy (research, civil initiative, NGOs...'), ) ACTIVITY = 'activity' EVALUATION = 'evaluation' TYPE_CHOICES = ( (ACTIVITY, 'Forward looking activity'), (EVALUATION, 'Evaluation'), ) BLOSSOM_CHOICES = ( (YES, 'BLOSSOM study'), (NO, 'other study'), ) draft = BooleanField(default=True) created_on = DateTimeField(auto_now_add=True) last_updated = DateTimeField(auto_now_add=True, auto_now=True) user_id = CharField(max_length=64, blank=True) title = CharField('title in English', max_length=255) languages = ManyToManyField('Language', verbose_name='language of the study', through='StudyLanguage') url = URLField(blank=True) study_type = CharField( 'I want to add a new', choices=TYPE_CHOICES, max_length=128, null=True, ) blossom = IntegerField( 'Approach to evaluation', choices=BLOSSOM_CHOICES, default=NO, ) requested_by = CharField('who requested the study?', max_length=64, choices=REQUESTED_BY_CHOICES, blank=True) start_date = DateField('start date', null=True, blank=True) end_date = DateField('end date') lead_author = TextField('lead author') other = TextField('other organisations/authors or contact persons', blank=True) purpose_and_target = CharField( 'purpose and target audience', max_length=128, choices=PURPOSE_CHOICES, blank=True, ) additional_information = TextField('additional information', blank=True) phase_of_policy = ForeignKey('PhasesOfPolicy', verbose_name='phases of policy cycle', null=True, blank=True) additional_information_phase = TextField( ('additional information about the application'), blank=True) foresight_approaches = ManyToManyField( 'ForesightApproaches', verbose_name='foresight approaches used') additional_information_foresight = TextField('additional information', blank=True) stakeholder_participation = BooleanField('stakeholder participation', default=False) additional_information_stakeholder = TextField( 'additional information about stakeholder involvement', blank=True) environmental_themes = ManyToManyField('common.EnvironmentalTheme', verbose_name='topics', blank=True) geographical_scope = ForeignKey('common.GeographicalScope', verbose_name='geographical scope', null=True, blank=True) countries = ManyToManyField('common.Country', verbose_name='countries', blank=True) def __unicode__(self): return self.title
class Partie(AutoriteModel, UniqueSlugModel): """ Partie de l’œuvre, c’est-à-dire typiquement un rôle ou un instrument pour une œuvre musicale. Pour plus de compréhensibilité, on affiche « rôle ou instrument » au lieu de « partie ». """ nom = CharField(_('nom'), max_length=200, db_index=True, help_text=_('Le nom d’une partie de la partition, ' 'instrumentale ou vocale.')) nom_pluriel = CharField(_('nom (au pluriel)'), max_length=230, blank=True, help_text=PLURAL_MSG) INSTRUMENT = 1 ROLE = 2 TYPES = ( (INSTRUMENT, _('instrument')), (ROLE, _('rôle')), ) type = PositiveSmallIntegerField(_('type'), choices=TYPES, db_index=True) # TODO: Ajouter automatiquement le rôle à l’effectif. oeuvre = ForeignKey('Oeuvre', verbose_name=_('œuvre'), blank=True, null=True, related_name='parties', help_text=_('Ne remplir que pour les rôles.'), on_delete=CASCADE) # TODO: Changer le verbose_name en un genre de "types de voix" # pour les rôles, mais en plus générique (ou un help_text). professions = ManyToManyField('Profession', related_name='parties', verbose_name=_('professions'), blank=True) parent = ForeignKey('self', related_name='enfants', blank=True, null=True, verbose_name=_('rôle ou instrument parent'), on_delete=CASCADE) classement = SmallIntegerField(_('classement'), default=1, db_index=True) premier_interprete = ForeignKey( 'Individu', related_name='parties_creees', on_delete=PROTECT, null=True, blank=True, verbose_name=_('premier(ère) interprète'), ) class Meta(object): unique_together = ('nom', 'parent', 'oeuvre') verbose_name = _('rôle ou instrument') verbose_name_plural = _('rôles et instruments') ordering = ( 'type', 'classement', 'nom', ) permissions = (('can_change_status', _('Peut changer l’état')), ) def clean(self): if self.premier_interprete and (self.type == self.INSTRUMENT or self.type == self.ROLE and not self.oeuvre): raise ValidationError({ 'premier_interprete': _('Le premier interprète ne peut être rempli que pour ' 'un rôle d’une œuvre donnée.'), }) @staticmethod def invalidated_relations_when_saved(all_relations=False): return ('pupitres', ) def interpretes(self): return self.elements_de_distribution.individus() def interpretes_html(self): return str_list([i.html() for i in self.interpretes()]) interpretes_html.short_description = _('interprètes') def evenements(self): return self.elements_de_distribution.evenements() def repertoire(self): return self.pupitres.oeuvres() def get_children(self): return self.enfants.all() def is_leaf_node(self): return not self.enfants.exists() def pluriel(self): return calc_pluriel(self) @permalink def get_absolute_url(self): return 'partie_detail', (self.slug, ) @permalink def permalien(self): return 'partie_permanent_detail', (self.pk, ) def link(self): return self.html() def html(self, pluriel=False, oeuvre=True, tags=True): url = '' if not tags else self.get_absolute_url() if pluriel: out = self.pluriel() else: out = self.nom if oeuvre and self.oeuvre: out = f'{out} ({self.oeuvre})' return href(url, out, tags=tags) def __str__(self): return self.html(tags=False) def short_html(self, pluriel=False, tags=True): return self.html(pluriel=pluriel, oeuvre=False, tags=tags) @staticmethod def autocomplete_search_fields(): return ( 'nom__unaccent__icontains', 'nom_pluriel__unaccent__icontains', 'professions__nom__unaccent__icontains', 'professions__nom_pluriel__unaccent__icontains', 'oeuvre__prefixe_titre__unaccent__icontains', 'oeuvre__titre__unaccent__icontains', 'oeuvre__coordination__unaccent__icontains', 'oeuvre__prefixe_titre_secondaire__unaccent__icontains', 'oeuvre__titre_secondaire__unaccent__icontains', )