class Session(models.Model): title = models.CharField(max_length=102) slug = models.SlugField(db_index=True) description = MarkupField(blank=True, markup_type='markdown', help_text="Markdown is supported.") speakers = JSONField(help_text='An array of objects. Each must contain a "name" attribute', blank=True, default='[]', db_index=True) extra_data = JSONField(blank=True, default='{}') tags = TaggableManager(blank=True, help_text="Help us schedule your session so that it doesn't conflict with other sessions around the same topics. Some example tags: Open data, International, Federal, State, Parliamentary Monitoring, Social Media, Design, Lobbying.") auto_tags = TaggableManager(blank=True, through=AutoTags) auto_tags.rel.related_name = '+' user_notes = models.TextField(blank=True, default='', help_text='Note in this space if you need to request a specific timeslot, or make sure you have a projector, etc. We can\'t make guarantees about anything, but we\'ll do our best.') hashtag = models.CharField(max_length=140, blank=True, null=True, help_text="Help others find and share info about your session! Include the '#'.") is_public = models.BooleanField(default=False, db_index=True) has_notes = models.BooleanField(default=True) notes_slug = models.SlugField(blank=True, help_text="Set this to override the default slug (i.e., If you want more than one session to have the same pad.") event = models.ForeignKey(Event, related_name='sessions') location = models.ForeignKey(Location, blank=True, null=True, related_name='sessions') start_time = models.DateTimeField(blank=True, null=True, db_index=True) end_time = models.DateTimeField(blank=True, null=True, db_index=True) admin_notes = models.TextField(blank=True, default='') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published_by = models.ForeignKey(User, blank=True, null=True, related_name="approved_sked_sessions") objects = SessionManager() class Meta: unique_together = (('event', 'slug'), ) ordering = ('-event__start_date', 'start_time', ) def __unicode__(self): return u"%s at %s" % (self.title, self.event) def get_absolute_url(self): url = reverse('sked:session_detail', kwargs={ 'event_slug': self.event.slug, 'slug': self.slug, }) return url def get_edit_url(self): url = reverse('sked:edit_session', kwargs={ 'event_slug': self.event.slug, 'slug': self.slug, }) return url def save(self, *args, **kwargs): # get the old instance for dirty tracking try: old = Session.objects.get(pk=self.id) except Session.DoesNotExist, AttributeError: old = None # create a slug if doesn't exist if not self.slug: self.slug = slugify(self.title)[:50].rstrip('-') normalized_slug = re.sub(r'[\d]+$', '', self.slug) count = 0 while Session.objects.filter(slug=self.slug, event=self.event).count(): count += 1 self.slug = '%s%s' % (normalized_slug, count) # set the right end time if it should be set if self.start_time and (not self.end_time or (old.start_time != self.start_time and old.end_time == self.end_time)): self.end_time = self.start_time + self.event.session_length super(Session, self).save(*args, **kwargs)
class Profile(TimeStampedModel): """ Stores extra information about a user or DDL member. """ user = models.OneToOneField('auth.User', editable=False) email_hash = models.CharField(max_length=32, editable=False) organization = models.CharField(max_length=255, **nullable) location = models.CharField(max_length=255, **nullable) biography = MarkupField(markup_type='markdown', help_text='Edit in Markdown', **nullable) twitter = models.CharField(max_length=100, **nullable) linkedin = models.URLField(**nullable) class Meta: db_table = 'member_profiles' @property def full_name(self): return self.user.get_full_name() @property def full_email(self): email = u"{} <{}>".format(self.full_name, self.user.email) return email.strip() @property def gravatar(self): return self.get_gravatar_url() @property def gravatar_icon(self): return self.get_gravatar_url(size=settings.GRAVATAR_ICON_SIZE) @property def gravatar_badge(self): return self.get_gravatar_url(size=64) def get_gravatar_url(self, size=None, default=None): """ Comptues the gravatar url from an email address """ size = size or settings.GRAVATAR_DEFAULT_SIZE default = default or settings.GRAVATAR_DEFAULT_IMAGE params = urllib.parse.urlencode({'d': default, 's': str(size)}) return "http://www.gravatar.com/avatar/{}?{}".format( self.email_hash, params) def get_api_detail_url(self): """ Returns the API detail endpoint for the object """ return reverse('api:user-detail', args=(self.user.pk, )) def get_absolute_url(self): """ Returns the detail view url for the object """ return reverse('member:detail', args=(self.user.username, )) def __str__(self): return self.full_email
class Package(BaseModel): title = models.CharField(_("Title"), max_length="100") slug = models.SlugField( _("Slug"), help_text= "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens. Values will be converted to lowercase.", unique=True) category = models.ManyToManyField(Category, verbose_name="Installation", blank=True) repo_description = models.TextField(_("Repo Description"), blank=True) repo_url = models.URLField(_("repo URL"), help_text=repo_url_help_text, blank=True, unique=True) repo_watchers = models.IntegerField(_("Stars"), default=0) repo_forks = models.IntegerField(_("repo forks"), default=0) pypi_url = models.CharField(_("PyPI slug"), max_length=255, help_text=pypi_url_help_text, blank=True, default='') pypi_downloads = models.IntegerField(_("Pypi downloads"), default=0) pypi_description = MarkupField(_('text'), blank=True, default='', default_markup_type='restructuredtext') participants = models.TextField( _("Participants"), help_text="List of collaborats/participants on the project", blank=True) usage = models.ManyToManyField(User, blank=True) created_by = models.ForeignKey(User, blank=True, null=True, related_name="creator", on_delete=models.SET_NULL) last_modified_by = models.ForeignKey(User, blank=True, null=True, related_name="modifier", on_delete=models.SET_NULL) last_fetched = models.DateTimeField(blank=True, null=True, default=timezone.now) documentation_url = models.URLField(_("Documentation URL"), blank=True, null=True, default="") commit_list = models.TextField(_("Commit List"), blank=True) show_pypi = models.BooleanField(_("Show pypi stats & version"), default=True) @property def description(self): from markupfield.markup import render_rest if self.repo_description: return render_rest(self.repo_description) return self.pypi_description @property def pypi_name(self): """ return the pypi name of a package""" if not self.pypi_url.strip(): return "" name = self.pypi_url.replace("http://pypi.python.org/pypi/", "") if "/" in name: return name[:name.index("/")] return name def last_updated(self): cache_name = self.cache_namer(self.last_updated) last_commit = cache.get(cache_name) if last_commit is not None: return last_commit try: last_commit = self.commit_set.latest('commit_date').commit_date if last_commit: cache.set(cache_name, last_commit) return last_commit except ObjectDoesNotExist: last_commit = None return last_commit @property def repo(self): return get_repo_for_repo_url(self.repo_url) @property def active_examples(self): return self.packageexample_set.filter(active=True) @property def license_latest(self): try: return self.version_set.latest().license except Version.DoesNotExist: return "UNKNOWN" def repo_name(self): return re.sub(self.repo.url_regex, '', self.repo_url) def repo_info(self): return dict( username=self.repo_name().split('/')[0], repo_name=self.repo_name().split('/')[1], ) def participant_list(self): return self.participants.split(',') def get_usage_count(self): return self.usage.count() def commits_over_52(self): cache_name = self.cache_namer(self.commits_over_52) value = cache.get(cache_name) if value is not None: return value now = timezone.now() commits = self.commit_set.filter(commit_date__gt=now - timedelta(weeks=52), ).values_list( 'commit_date', flat=True) weeks = [0] * 52 for cdate in commits: age_weeks = (now - cdate).days // 7 if age_weeks < 52: weeks[age_weeks] += 1 value = ','.join(map(str, reversed(weeks))) cache.set(cache_name, value) return value def fetch_pypi_data(self, *args, **kwargs): # Get the releases from pypi if self.pypi_url.strip( ) and self.pypi_url != "http://pypi.python.org/pypi/": url = "https://pypi.python.org/pypi/{0}/json".format( self.pypi_name) response = requests.get(url) if settings.DEBUG: if response.status_code not in (200, 404): print("BOOM!") print(self, response.status_code) if response.status_code == 404: if settings.DEBUG: print("BOOM!") print(self, response.status_code) return False release = json.loads(response.content) info = release['info'] version, created = Version.objects.get_or_create( package=self, number=info['version']) # add to versions license = info['license'] if not info['license'] or not license.strip( ) or 'UNKNOWN' == license.upper(): for classifier in info['classifiers']: if classifier.strip().startswith('License'): # Do it this way to cover people not quite following the spec # at # http://docs.python.org/distutils/setupscript.html#additional-meta-data license = classifier.strip().replace('License ::', '') license = license.replace('OSI Approved :: ', '') break if license and len(license) > 100: license = "Other (see http://pypi.python.org/pypi/%s)" % self.pypi_name version.license = license # version stuff try: url_data = release['urls'][0] version.downloads = url_data['downloads'] version.upload_time = url_data['upload_time'] except IndexError: # Not a real release so we just guess the upload_time. version.upload_time = version.created version.hidden = info['_pypi_hidden'] for classifier in info['classifiers']: if classifier.startswith('Development Status'): version.development_status = status_choices_switch( classifier) break for classifier in info['classifiers']: if classifier.startswith( 'Programming Language :: Python :: 3'): version.supports_python3 = True break version.save() self.pypi_description = info['description'] self.save() # Calculate total downloads return True return False @property def total_downloads(self): total = 0 for dl in [v.downloads for v in self.version_set.all()]: total += dl return total def fetch_metadata(self, fetch_pypi=True, fetch_repo=True): if fetch_pypi: self.fetch_pypi_data() if fetch_repo: self.repo.fetch_metadata(self) self.save() def save(self, *args, **kwargs): if not self.repo_description: self.repo_description = "" super(Package, self).save(*args, **kwargs) def fetch_commits(self): self.repo.fetch_commits(self) def pypi_version(self): version = get_pypi_version(self) return version def last_released(self): cache_name = self.cache_namer(self.last_released) version = cache.get(cache_name) if version is not None: return version version = get_version(self) cache.set(cache_name, version) return version @property def development_status(self): """ Gets data needed in API v2 calls """ return self.last_released().pretty_status @property def pypi_ancient(self): release = self.last_released() if release: return release.upload_time < datetime.now() - timedelta(365) return None @property def no_development(self): commit_date = self.last_updated() if commit_date is not None: return commit_date < datetime.now() - timedelta(365) return None class Meta: ordering = ['title'] get_latest_by = 'id' def __unicode__(self): return self.title def get_absolute_url(self): return urlresolvers.reverse_lazy('package_index:package_detail', kwargs={'object_slug': self.slug}) @property def last_commit(self): return self.commit_set.latest()
class Abstract(models.Model): content = MarkupField() class Meta: abstract = True
class Deployment(models.Model): title = models.CharField(max_length=250) latitude = models.DecimalField(max_digits=5, decimal_places=3) longitude = models.DecimalField(max_digits=6, decimal_places=3) user_story = MarkupField()
class Event(ContentManageable): uid = models.CharField(max_length=200, null=True, blank=True) title = models.CharField(max_length=200) calendar = models.ForeignKey(Calendar, related_name='events') description = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, escape_html=False) venue = models.ForeignKey(EventLocation, null=True, blank=True, related_name='events') categories = models.ManyToManyField(EventCategory, related_name='events', blank=True, null=True) featured = models.BooleanField(default=False, db_index=True) objects = EventManager() class Meta: ordering = ('-occurring_rule__dt_start', ) def __str__(self): return self.title def get_absolute_url(self): return reverse('events:event_detail', kwargs={ 'calendar_slug': self.calendar.slug, 'pk': self.pk }) @cached_property def previous_event(self): dt = self.next_time.dt_end try: return Event.objects.until_datetime(dt).filter( calendar=self.calendar)[0] except IndexError: return None @cached_property def next_event(self): dt = self.next_time.dt_start try: return Event.objects.for_datetime(dt).filter( calendar=self.calendar)[0] except IndexError: return None @property def next_time(self): """ Return the OccurringRule or RecurringRule with the closest `dt_start` from now. """ now = timezone.now() recurring_start = occurring_start = None try: occurring_rule = self.occurring_rule except OccurringRule.DoesNotExist: pass else: if occurring_rule and occurring_rule.dt_start > now: occurring_start = (occurring_rule.dt_start, occurring_rule) rrules = self.recurring_rules.filter(finish__gt=now) recurring_starts = [(rule.dt_start, rule) for rule in rrules if rule.dt_start is not None] recurring_starts.sort(key=itemgetter(0)) try: recurring_start = recurring_starts[0] except IndexError: pass starts = [ i for i in (recurring_start, occurring_start) if i is not None ] starts.sort(key=itemgetter(0)) try: return starts[0][1] except IndexError: return None @property def previous_time(self): now = timezone.now() recurring_end = occurring_end = None try: occurring_rule = self.occurring_rule except OccurringRule.DoesNotExist: pass else: if occurring_rule and occurring_rule.dt_end < now: occurring_end = (occurring_rule.dt_end, occurring_rule) rrules = self.recurring_rules.filter(begin__lt=now) recurring_ends = [(rule.dt_end, rule) for rule in rrules if rule.dt_end is not None] recurring_ends.sort(key=itemgetter(0), reverse=True) try: recurring_end = recurring_ends[0] except IndexError: pass ends = [i for i in (recurring_end, occurring_end) if i is not None] ends.sort(key=itemgetter(0), reverse=True) try: return ends[0][1] except IndexError: return None @property def next_or_previous_time(self): return self.next_time or self.previous_time @property def is_past(self): return self.next_time is None
class Proposal(TimeStampedModel): audience_level = models.CharField( verbose_name=_("Nivel de la audiencia"), choices=PROPOSAL_LEVELS, null=True, default=BASIC_LEVEL, max_length=32, ) language = models.CharField(verbose_name=_("Idioma"), max_length=2, choices=PROPOSAL_LANGUAGES, default="es") duration = models.PositiveIntegerField( verbose_name=_("Duración"), choices=PROPOSAL_DURATIONS, default=30, null=True, blank=True, ) tags = TaggableManager( verbose_name=_("Etiquetas"), help_text=_("Lista de etiquetas separadas por comas."), blank=True, ) is_beginners_friendly = models.BooleanField( verbose_name=_("¿Es apta para principiantes?"), default=False) kind = models.ForeignKey( "proposals.ProposalKind", verbose_name=_("Tipo de propuesta"), on_delete=models.CASCADE, ) title = models.CharField(max_length=100, verbose_name=_("Título")) description = models.TextField( _("Breve descripción"), max_length=500, help_text=_( "Si tu propuesta se acepta esto se hará público, y se incluirá en el programa. " "Debería ser un párrafo, con un máximo de 500 caracteres."), ) abstract = MarkupField( _("Resumen detallado"), blank=True, default="", default_markup_type="markdown", help_text= _("Resumen detallado. Se hará pública si la propuesta se acepta. Edita " "usando <a href='http://daringfireball.net/projects/markdown/basics' " "target='_blank'>Markdown</a>."), ) additional_notes = MarkupField( _("Notas adicionales"), blank=True, default="", default_markup_type="markdown", help_text= _("Cualquier cosa que te gustaría hacer saber a los revisores para que la tengan en " "cuenta al ahora de hacer la selección. Esto no se hará público. Edita usando " "<a href='http://daringfireball.net/projects/markdown/basics' " "target='_blank'>Markdown</a>."), ) speakers = models.ManyToManyField("speakers.Speaker", related_name="proposals", blank=False) cancelled = models.BooleanField(default=False) notified = models.BooleanField(default=False) accepted = models.NullBooleanField(verbose_name=_("Aceptada"), default=None) accepted_notified = models.BooleanField( verbose_name=_("Notificación de aceptación enviada"), default=False) code = models.CharField(max_length=64, null=True, blank=True) def __str__(self): return self.title @property def translated_abstract(self): return get_translated_markdown_field(self, "abstract") @property def translated_additional_notes(self): return get_translated_markdown_field(self, "additional_notes") @property def avg(self): data = [ review.score for review in self.reviews.filter(finished=True) if review.score is not None ] if data: return sum(data) / len(data) return None @property def completed_reviews(self): return self.reviews.filter(finished=True).count() @property def assigned_reviews(self): return self.reviews.count() @property def tag_list(self): return ", ".join(tag.name for tag in self.tags.all()) @property def speakers_list(self): return ", ".join([ "%s <%s>" % (speaker.name, speaker.email) for speaker in self.speakers.all() ]) @property def renormalization_o0(self): """Renormalization with order 0. Average value of 0""" score = [] for review in self.reviews.all(): reviewer = Reviewer.objects.get(user=review.user) mean = reviewer.mean() if reviewer.num_reviews() <= 1: continue score.append((review.score or 0) - mean) if score: return np.mean(score) return None @property def renormalization_o1(self): """Renormalization with order 1. Expand the value to get the same standard deviation for everyone""" score = [] for review in self.reviews.all(): reviewer = Reviewer.objects.get(user=review.user) std = reviewer.std() if reviewer.num_reviews() <= 1 or std < 0.75: continue score.append((review.score or 0) - std) if score: return np.mean(score) return None def notification_email_context(self, speaker): site = Site.objects.get_current() return { "title": self.title, "speaker": speaker, "kind": self.kind.name, "code": self.code, "site": site, } def notify(self): """Sends an email to the creator of the proposal with a confirmation email. The emails has a link to edit the proposal. """ if not self.code: self.code = random_string(64) for speaker in self.speakers.all(): context = self.notification_email_context(speaker=speaker) send_email( context=context, template="emails/proposals/confirmation.html", subject=_("[%s] Confirmación de propuesta de charla") % settings.CONFERENCE_TITLE, to=speaker.email, from_email=settings.CONTACT_EMAIL, ) self.notified = True self.save() def notify_acceptance(self): """Sends an email to the creator of the proposal with an email with the resolution of the acceptance or not of his proposal. """ for speaker in self.speakers.all(): context = self.notification_email_context(speaker=speaker) if self.accepted is None: return template = ("emails/proposals/accepted.html" if self.accepted else "emails/proposals/rejected.html") send_email( context=context, template=template, subject=_("[%s] Notificación de propuesta de charla") % settings.CONFERENCE_TITLE, to=self.speaker.email, from_email=settings.CONTACT_EMAIL, ) self.accepted_notified = True self.save()
class Contract(models.Model): """ Contract model to oficialize a Sponsorship """ DRAFT = "draft" OUTDATED = "outdated" AWAITING_SIGNATURE = "awaiting signature" EXECUTED = "executed" NULLIFIED = "nullified" STATUS_CHOICES = [ (DRAFT, "Draft"), (OUTDATED, "Outdated"), (AWAITING_SIGNATURE, "Awaiting signature"), (EXECUTED, "Executed"), (NULLIFIED, "Nullified"), ] FINAL_VERSION_PDF_DIR = "sponsors/contracts/" FINAL_VERSION_DOCX_DIR = FINAL_VERSION_PDF_DIR + "docx/" SIGNED_PDF_DIR = FINAL_VERSION_PDF_DIR + "signed/" status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=DRAFT, db_index=True) revision = models.PositiveIntegerField(default=0, verbose_name="Revision nº") document = models.FileField( upload_to=FINAL_VERSION_PDF_DIR, blank=True, verbose_name="Unsigned PDF", ) document_docx = models.FileField( upload_to=FINAL_VERSION_DOCX_DIR, blank=True, verbose_name="Unsigned Docx", ) signed_document = models.FileField( upload_to=signed_contract_random_path, blank=True, verbose_name="Signed PDF", ) # Contract information gets populated during object's creation. # The sponsorship FK ís just a reference to keep track of related objects. # It shouldn't be used to fetch for any of the sponsorship's data. sponsorship = models.OneToOneField( "sponsors.Sponsorship", null=True, on_delete=models.SET_NULL, related_name="contract", ) sponsor_info = models.TextField(verbose_name="Sponsor information") sponsor_contact = models.TextField(verbose_name="Sponsor contact") # benefits_list = """ # - Foundation - Promotion of Python case study [^1] # - PyCon - PyCon website Listing [^1][^2] # - PyPI - Social media promotion of your sponsorship # """ benefits_list = MarkupField(markup_type="markdown") # legal_clauses = """ # [^1]: Here's one with multiple paragraphs and code. # Indent paragraphs to include them in the footnote. # `{ my code }` # Add as many paragraphs as you like. # [^2]: Here's one with multiple paragraphs and code. # Indent paragraphs to include them in the footnote. # `{ my code }` # Add as many paragraphs as you like. # """ legal_clauses = MarkupField(markup_type="markdown", default="", blank=True) # Activity control fields created_on = models.DateField(auto_now_add=True) last_update = models.DateField(auto_now=True) sent_on = models.DateField(null=True) class Meta: verbose_name = "Contract" verbose_name_plural = "Contracts" def __str__(self): return f"Contract: {self.sponsorship}" @classmethod def new(cls, sponsorship): """ Factory method to create a new Contract from a Sponsorship """ sponsor = sponsorship.sponsor primary_contact = sponsor.primary_contact sponsor_info = f"{sponsor.name}, {sponsor.description}" sponsor_contact = "" if primary_contact: sponsor_contact = f"{primary_contact.name} - {primary_contact.phone} | {primary_contact.email}" benefits = sponsorship.benefits.all() # must query for Legal Clauses again to respect model's ordering clauses_ids = [ c.id for c in chain(*(b.legal_clauses for b in benefits)) ] legal_clauses = list(LegalClause.objects.filter(id__in=clauses_ids)) benefits_list = [] for benefit in benefits: item = f"- {benefit.program_name} - {benefit.name_for_display}" index_str = "" for legal_clause in benefit.legal_clauses: index = legal_clauses.index(legal_clause) + 1 index_str += f"[^{index}]" if index_str: item += f" {index_str}" benefits_list.append(item) legal_clauses_text = "\n".join([ f"[^{i}]: {c.clause}" for i, c in enumerate(legal_clauses, start=1) ]) return cls.objects.create( sponsorship=sponsorship, sponsor_info=sponsor_info, sponsor_contact=sponsor_contact, benefits_list="\n".join([b for b in benefits_list]), legal_clauses=legal_clauses_text, ) @property def is_draft(self): return self.status == self.DRAFT @property def preview_url(self): return reverse("admin:sponsors_contract_preview", args=[self.pk]) @property def awaiting_signature(self): return self.status == self.AWAITING_SIGNATURE @property def next_status(self): states_map = { self.DRAFT: [self.AWAITING_SIGNATURE, self.EXECUTED], self.OUTDATED: [], self.AWAITING_SIGNATURE: [self.EXECUTED, self.NULLIFIED], self.EXECUTED: [], self.NULLIFIED: [self.DRAFT], } return states_map[self.status] def save(self, **kwargs): if all([self.pk, self.is_draft]): self.revision += 1 return super().save(**kwargs) def set_final_version(self, pdf_file, docx_file=None): if self.AWAITING_SIGNATURE not in self.next_status: msg = f"Can't send a {self.get_status_display()} contract." raise InvalidStatusException(msg) sponsor = self.sponsorship.sponsor.name.upper() # save contract as PDF file path = f"{self.FINAL_VERSION_PDF_DIR}" pdf_filename = f"{path}SoW: {sponsor}.pdf" file = file_from_storage(pdf_filename, mode="wb") file.write(pdf_file) file.close() self.document = pdf_filename # save contract as docx file if docx_file: path = f"{self.FINAL_VERSION_DOCX_DIR}" docx_filename = f"{path}SoW: {sponsor}.docx" file = file_from_storage(docx_filename, mode="wb") file.write(docx_file) file.close() self.document_docx = docx_filename self.status = self.AWAITING_SIGNATURE self.save() def execute(self, commit=True, force=False): if not force and self.EXECUTED not in self.next_status: msg = f"Can't execute a {self.get_status_display()} contract." raise InvalidStatusException(msg) self.status = self.EXECUTED self.sponsorship.status = Sponsorship.FINALIZED self.sponsorship.finalized_on = timezone.now().date() if commit: self.sponsorship.save() self.save() def nullify(self, commit=True): if self.NULLIFIED not in self.next_status: msg = f"Can't nullify a {self.get_status_display()} contract." raise InvalidStatusException(msg) self.status = self.NULLIFIED if commit: self.sponsorship.save() self.save()
class Post(models.Model): title = models.CharField(max_length=50) body = MarkupField('body of post') def __unicode__(self): return self.title
class Job(ContentManageable): NEW_THRESHOLD = datetime.timedelta(days=30) category = models.ForeignKey(JobCategory, related_name='jobs') job_types = models.ManyToManyField(JobType, related_name='jobs', blank=True) company = models.ForeignKey('companies.Company', related_name='jobs') city = models.CharField(max_length=100) region = models.CharField(max_length=100) country = models.CharField(max_length=100, db_index=True) location_slug = models.SlugField(max_length=350, editable=False) description = MarkupField(blank=True, default_markup_type=DEFAULT_MARKUP_TYPE) requirements = MarkupField(blank=True, default_markup_type=DEFAULT_MARKUP_TYPE) contact = models.CharField(null=True, blank=True, max_length=100) email = models.EmailField() url = models.URLField('URL', null=True, blank=True) STATUS_DRAFT = 'draft' STATUS_REVIEW = 'review' STATUS_APPROVED = 'approved' STATUS_REJECTED = 'rejected' STATUS_ARCHIVED = 'archived' STATUS_REMOVED = 'removed' STATUS_EXPIRED = 'expired' STATUS_CHOICES = ( (STATUS_DRAFT, 'draft'), (STATUS_REVIEW, 'review'), (STATUS_APPROVED, 'approved'), (STATUS_REJECTED, 'rejected'), (STATUS_ARCHIVED, 'archived'), (STATUS_REMOVED, 'removed'), (STATUS_EXPIRED, 'expired'), ) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_DRAFT, db_index=True) dt_start = models.DateTimeField('Job start date', blank=True, null=True) dt_end = models.DateTimeField('Job end date', blank=True, null=True) telecommuting = models.BooleanField(default=True) agencies = models.BooleanField(default=True) is_featured = models.BooleanField(default=False, db_index=True) objects = JobManager() class Meta: ordering = ('-created', ) get_latest_by = 'created' verbose_name = 'job' verbose_name_plural = 'jobs' permissions = [('can_moderate_jobs', 'Can moderate Job listings')] def __str__(self): return 'Job Listing #{0}'.format(self.pk) def save(self, **kwargs): self.location_slug = slugify('%s %s %s' % (self.city, self.region, self.country)) if not self.dt_start and self.status == self.STATUS_APPROVED: self.dt_start = timezone.now() self.dt_end = timezone.now() + self.NEW_THRESHOLD return super().save(**kwargs) def get_absolute_url(self): return reverse('jobs:job_detail', kwargs={'pk': self.pk}) @property def is_new(self): return self.created > (timezone.now() - self.NEW_THRESHOLD) @property def editable(self): return self.status in (self.STATUS_DRAFT, self.STATUS_REVIEW, self.STATUS_REJECTED)
class Speaker(TimeStampedModel): user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name="speaker") name = models.CharField( verbose_name=_("Nombre"), max_length=100, help_text=_( "Tal como quieres que apareza en el programa de la conferencia.")) biography = MarkupField(verbose_name=_("Biografía"), blank=True, default="", default_markup_type='markdown', help_text=_( "Unas palabras sobre ti. Edita usando " "<a href='http://warpedvisions.org/projects/" "markdown-cheat-sheet/target='_blank'>" "Markdown</a>.")) photo = models.ImageField(verbose_name=_("Foto"), upload_to="speakers", blank=True, null=True) annotation = models.TextField(default="", blank=True) is_keynoter = models.BooleanField(default=False) class Meta: ordering = ['name'] @property def photo_url(self): try: return self.photo.url except ValueError: return static("img/default-avatar.png") @property def email(self): if self.user is not None: return self.user.email else: return self.invite_email @property def all_presentations(self): presentations = [] if self.presentations: for p in self.presentations.all(): presentations.append(p) for p in self.copresentations.all(): presentations.append(p) return presentations def __str__(self): if self.user: return self.name else: return "?" def has_biography(self): return bool(self.biography.raw) def get_api_id(self): return "S{:05d}".format(self.pk) def save(self, **kwargs): """Save user full name by default for speaker.""" if not self.name: self.name = self.user.get_full_name() return super(Speaker, self).save(**kwargs)
class DescribeAble(models.Model): description = MarkupField(default="No Description Provided", default_markup_type='markdown') class Meta: abstract = True
class LocateAble(models.Model): phone = models.CharField(max_length=10, null=True, blank=True) address = MarkupField(default="No Address Provided") class Meta: abstract = True
class PMASA(models.Model): year = models.IntegerField(choices=YEAR_CHOICES, default=YEAR_TODAY) sequence = models.IntegerField( help_text='Sequence number of PMASA in given year') date = models.DateTimeField(db_index=True, default=timezone.now) updated = models.DateTimeField( null=True, blank=True, help_text='Set this in case of major update to the entry') summary = models.CharField(max_length=200) description = MarkupField(default_markup_type='markdown') severity = models.TextField() mitigation = models.TextField(blank=True) affected = models.TextField() unaffected = models.TextField(blank=True) solution = models.TextField( max_length=200, default='Upgrade to phpMyAdmin ? or newer or apply patch listed below.' ) references = models.TextField( help_text='Links to reporter etc.', blank=True, ) cve = models.CharField( max_length=200, help_text= 'Space separated list of related CVE entries, enter CVE-2017- in case none is assigned yet' ) cwe = models.CharField( max_length=200, default='661', help_text='Space separated list of CWE classifications') commits = models.TextField(help_text=( 'Space separated list of commits, commits for different branches ' 'should be placed on separate line prefixed with version prefix. ' 'For example: 3.5: 01d35b3558e47fba947719857bd71f6fd9e5dce8')) draft = models.BooleanField( default=True, help_text='Draft entries are not shown in website listings') class Meta(object): unique_together = ('year', 'sequence') ordering = ('-year', '-sequence') verbose_name = 'PMASA' verbose_name_plural = 'PMASAs' def __unicode__(self): return 'PMASA-{0}-{1}'.format(self.year, self.sequence) @models.permalink def get_absolute_url(self): if self.draft: page = 'security-issue-draft' else: page = 'security-issue' return (page, (), {'year': self.year, 'sequence': self.sequence}) def get_cves(self): for cve in self.cve.split(): # Incomplete reference as CVE-2016- if cve[-1] == '-': yield '', 'Not yet assigned' else: yield 'https://cve.mitre.org/cgi-bin/cvename.cgi?name={0}'.format( cve), cve def get_cwes(self): return self.cwe.split() def get_commits(self): lines = self.commits.strip().split('\n') result = [] for line in lines: if ':' in line: branch, line = line.split(':') else: branch = '' result.append({ 'branch': branch, 'commits': line.strip().split(), }) return result
class Slot(models.Model): day = models.ForeignKey(Day) kind = models.ForeignKey(SlotKind) start = models.TimeField() end = models.TimeField() content_override = MarkupField(blank=True, default_markup_type='markdown') default_room = models.ForeignKey(Room, null=True, blank=True) video_url = models.URLField(_("video URL"), blank=True, null=True) keynote_url = models.URLField(_("keynote URL"), blank=True, null=True) keynote = models.FileField(_("keynote file"), blank=True, null=True, upload_to="keynotes") def assign(self, content): """ Assign the given content to this slot and if a previous slot content was given we need to unlink it to avoid integrity errors. """ self.unassign() content.slot = self content.save() def unassign(self): """ Unassign the associated content with this slot. """ content = self.content if content and content.slot_id: content.slot = None content.save() @property def content(self): """ Return the content this slot represents. @@@ hard-coded for presentation for now """ try: return self.content_ptr except ObjectDoesNotExist: return None def get_video_url(self): if self.video_url: return self.video_url if not self.content_ptr.video_url: return "" return self.content_ptr.video_url def get_keynote_url(self): if self.keynote and not self.keynote_url: return self.keynote elif self.keynote_url: return self.keynote_url if self.content_ptr.keynote and not self.content_ptr.keynote_url: return self.content_ptr.keynote.url elif self.content_ptr.keynote_url: return self.content_ptr.keynote_url return "" @property def start_datetime(self): return datetime.datetime( self.day.date.year, self.day.date.month, self.day.date.day, self.start.hour, self.start.minute) @property def end_datetime(self): return datetime.datetime( self.day.date.year, self.day.date.month, self.day.date.day, self.end.hour, self.end.minute) @property def length_in_minutes(self): return int( (self.end_datetime - self.start_datetime).total_seconds() / 60) @property def rooms(self): return Room.objects.filter(pk__in=self.slotroom_set.values("room")) def __str__(self): if not self.rooms: return "%s %s (%s - %s)" % (self.day, self.kind, self.start, self.end) rooms = ", ".join(map(lambda room: room.name, self.rooms)) return "%s %s (%s - %s, %s)" % (self.day, self.kind, self.start, self.end, rooms) class Meta: ordering = ["day", "start", "end", "default_room__order"]
class CustomArticle(models.Model): text = MarkupField(markup_choices=CUSTOM_MARKUP_TYPES, default_markup_type='text/x-rst')
class Job(ContentManageable): NEW_THRESHOLD = datetime.timedelta(days=30) category = models.ForeignKey( JobCategory, related_name='jobs', limit_choices_to={'active': True}, on_delete=models.CASCADE, ) job_types = models.ManyToManyField( JobType, related_name='jobs', blank=True, verbose_name='Job technologies', limit_choices_to={'active': True}, ) other_job_type = models.CharField( verbose_name='Other job technologies', max_length=100, blank=True, ) company_name = models.CharField(max_length=100, null=True) company_description = MarkupField(blank=True, default_markup_type=DEFAULT_MARKUP_TYPE) job_title = models.CharField(max_length=100) city = models.CharField(max_length=100) region = models.CharField(verbose_name='State, Province or Region', blank=True, max_length=100) country = models.CharField(max_length=100, db_index=True) location_slug = models.SlugField(max_length=350, editable=False) country_slug = models.SlugField(max_length=100, editable=False) description = MarkupField(verbose_name='Job description', default_markup_type=DEFAULT_MARKUP_TYPE) requirements = MarkupField(verbose_name='Job requirements', default_markup_type=DEFAULT_MARKUP_TYPE) contact = models.CharField(verbose_name='Contact name', null=True, blank=True, max_length=100) email = models.EmailField(verbose_name='Contact email') url = models.URLField(verbose_name='URL', null=True, blank=True) STATUS_DRAFT = 'draft' STATUS_REVIEW = 'review' STATUS_APPROVED = 'approved' STATUS_REJECTED = 'rejected' STATUS_ARCHIVED = 'archived' STATUS_REMOVED = 'removed' STATUS_EXPIRED = 'expired' STATUS_CHOICES = ( (STATUS_DRAFT, 'draft'), (STATUS_REVIEW, 'review'), (STATUS_APPROVED, 'approved'), (STATUS_REJECTED, 'rejected'), (STATUS_ARCHIVED, 'archived'), (STATUS_REMOVED, 'removed'), (STATUS_EXPIRED, 'expired'), ) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_REVIEW, db_index=True) expires = models.DateTimeField(verbose_name='Job Listing Expiration Date', blank=True, null=True) telecommuting = models.BooleanField(verbose_name='Telecommuting allowed?', default=False) agencies = models.BooleanField(verbose_name='Agencies are OK to contact?', default=True) is_featured = models.BooleanField(default=False, db_index=True) objects = JobQuerySet.as_manager() class Meta: ordering = ('-created', ) get_latest_by = 'created' verbose_name = 'job' verbose_name_plural = 'jobs' permissions = [('can_moderate_jobs', 'Can moderate Job listings')] def __str__(self): return 'Job Listing #{}'.format(self.pk) def save(self, **kwargs): location_parts = (self.city, self.region, self.country) location_str = '' for location_part in location_parts: if location_part is not None: location_str = ' '.join([location_str, location_part]) self.location_slug = slugify(location_str) self.country_slug = slugify(self.country) if not self.expires and self.status == self.STATUS_APPROVED: delta = datetime.timedelta(days=settings.JOB_THRESHOLD_DAYS) self.expires = timezone.now() + delta return super().save(**kwargs) def review(self): """Updates job status to Job.STATUS_REVIEW after preview was done by user. """ old_status = self.status self.status = Job.STATUS_REVIEW self.save() if old_status != self.status: job_was_submitted.send(sender=self.__class__, job=self) def approve(self, approving_user): """Updates job status to Job.STATUS_APPROVED after approval was issued by approving_user. """ self.status = Job.STATUS_APPROVED self.save() job_was_approved.send(sender=self.__class__, job=self, approving_user=approving_user) def reject(self, rejecting_user): """Updates job status to Job.STATUS_REJECTED after rejection was issued by rejecing_user. """ self.status = Job.STATUS_REJECTED self.save() job_was_rejected.send(sender=self.__class__, job=self, rejecting_user=rejecting_user) def get_absolute_url(self): return reverse('jobs:job_detail', kwargs={'pk': self.pk}) @property def display_name(self): return "%s, %s" % (self.job_title, self.company_name) @property def display_description(self): return self.company_description @property def display_location(self): location_parts = [ part for part in (self.city, self.region, self.country) if part ] location_str = ', '.join(location_parts) return location_str @property def is_new(self): return self.created > (timezone.now() - self.NEW_THRESHOLD) @property def editable(self): return self.status in (self.STATUS_DRAFT, self.STATUS_REVIEW, self.STATUS_REJECTED) def get_previous_listing(self): return self.get_previous_by_created(status=self.STATUS_APPROVED) def get_next_listing(self): return self.get_next_by_created(status=self.STATUS_APPROVED)
class Presentation(models.Model): slot = models.OneToOneField(Slot, null=True, blank=True, related_name="presentation", on_delete=SET_NULL) title = models.CharField(max_length=100, default="", blank=True) slug = models.SlugField(max_length=100, null=True, blank=True, allow_unicode=True) description = MarkupField(default="", blank=True, default_markup_type='markdown') abstract = MarkupField(default="", blank=True, default_markup_type='markdown') language = models.CharField(verbose_name=_("Idioma"), max_length=2, choices=PROPOSAL_LANGUAGES, null=True, blank=True) speakers = models.ManyToManyField("speakers.Speaker", related_name="presentations", blank=True) proposal = models.OneToOneField("proposals.Proposal", related_name="presentation", null=True, blank=True) cancelled = models.BooleanField(default=False) video_url = models.URLField(_("video URL"), blank=True, null=True) keynote_url = models.URLField(_("URL de la presentación o del código"), blank=True, null=True) keynote = models.FileField(_("Fichero de presentación"), blank=True, null=True, upload_to="keynotes") class Meta: ordering = ["slot"] def __str__(self): return "#%s %s (%s)" % (self.pk, self.get_title(), ",".join( map(lambda s: six.text_type(s), self.get_speakers()))) def get_title(self): if self.title: return self.title if self.proposal: return self.proposal.title return None def get_description(self): if self.description.raw: return self.description if self.proposal: return self.proposal.description return None def get_abstract(self): if self.abstract.raw: return self.abstract if self.proposal: return self.proposal.translated_abstract return None def get_additional_notes(self): if self.additional_notes.raw: return self.additional_notes if self.proposal: return self.proposal.translated_additional_notes return None def get_language(self): if self.language: return self.language if self.proposal: return self.proposal.language return None def get_speakers(self): if self.speakers.exists(): return self.speakers.all() if self.proposal: return self.proposal.speakers.all() return self.speakers.all() def has_speakers(self): return self.get_speakers().exists() def get_audience_level(self): if self.proposal: return self.proposal.audience_level return BASIC_LEVEL def get_video_url(self): if not self.video_url: return "" return self.video_url def get_keynote_url(self): if self.keynote and not self.keynote_url: return self.keynote.url elif self.keynote_url: return self.keynote_url return "" def get_api_id(self): return "T{:04d}".format(self.pk) def save(self, *args, **kwargs): title = self.get_title() if title and not self.slug: self.slug = slugify(title) super(Presentation, self).save(*args, **kwargs)
class Authority(models.Model): ref = models.CharField(max_length=20, primary_key=True) name = models.CharField(max_length=60, unique=True) url = models.URLField(blank=True, null=True) notes = MarkupField(markup_type='markdown', blank=True, null=True)
class Slot(models.Model): day = models.ForeignKey(Day) kind = models.ForeignKey(SlotKind) start = models.TimeField() end = models.TimeField() order = models.PositiveIntegerField(default=0) content_override = MarkupField(blank=True, default_markup_type='markdown') room = models.ForeignKey(Room, related_name="slots", null=True, blank=True) track = models.ForeignKey(Track, related_name="slots", null=True, blank=True) video_url = models.URLField(_("video URL"), blank=True, null=True) keynote_url = models.URLField(_("keynote URL"), blank=True, null=True) keynote = models.FileField(_("keynote file"), blank=True, null=True, upload_to="keynotes") class Meta: ordering = ["day", "start", "end", "track__order", "order"] @property def content(self): """ Return the content this slot represents.""" try: return self.presentation except ObjectDoesNotExist: return None @property def start_datetime(self): return make_aware( datetime.datetime(self.day.date.year, self.day.date.month, self.day.date.day, self.start.hour, self.start.minute)) @property def end_datetime(self): return make_aware( datetime.datetime(self.day.date.year, self.day.date.month, self.day.date.day, self.end.hour, self.end.minute)) def __str__(self): return "%s %s (%s - %s, %s)" % (self.day, self.kind, self.start, self.end, self.room) def assign(self, content): """Assign the given content to this slot and if a previous slot content was given we need to unlink it to avoid integrity errors. """ self.unassign() content.slot = self content.save() def unassign(self): """Unassign the associated content with this slot.""" content = self.presentation if content and content.slot_id: content.slot = None content.save() def get_video_url(self): if self.video_url: return self.video_url try: return self.presentation.get_video_url() except ObjectDoesNotExist: pass return "" def get_keynote_url(self): if self.keynote and not self.keynote_url: return self.keynote elif self.keynote_url: return self.keynote_url try: return self.presentation.get_keynote_url() except ObjectDoesNotExist: pass return "" def get_absolute_url(self): if self.content and self.content.slug: return reverse("schedule:slot", kwargs={"slot": self.content.slug}) return reverse("schedule:slot", kwargs={"slot": self.pk})
class Release(ContentManageable, NameSlugModel): """ A particular version release. Name field should be version number for example: 3.3.4 or 2.7.6 """ PYTHON1 = 1 PYTHON2 = 2 PYTHON3 = 3 PYTHON_VERSION_CHOICES = ( (PYTHON3, 'Python 3.x.x'), (PYTHON2, 'Python 2.x.x'), (PYTHON1, 'Python 1.x.x'), ) version = models.IntegerField(default=PYTHON3, choices=PYTHON_VERSION_CHOICES) is_latest = models.BooleanField( verbose_name='Is this the latest release?', default=False, db_index=True, help_text="Set this if this should be considered the latest release " "for the major version. Previous 'latest' versions will " "automatically have this flag turned off.", ) is_published = models.BooleanField( verbose_name='Is Published?', default=False, db_index=True, help_text= "Whether or not this should be considered a released/published version", ) pre_release = models.BooleanField( verbose_name='Pre-release', default=False, db_index=True, help_text="Boolean to denote pre-release/beta/RC versions", ) show_on_download_page = models.BooleanField( default=True, db_index=True, help_text= "Whether or not to show this release on the main /downloads/ page", ) release_date = models.DateTimeField(default=timezone.now) release_page = models.ForeignKey( Page, related_name='release', blank=True, null=True, on_delete=models.CASCADE, ) release_notes_url = models.URLField('Release Notes URL', blank=True) content = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, default='') objects = ReleaseManager() class Meta: verbose_name = 'Release' verbose_name_plural = 'Releases' ordering = ('name', ) get_latest_by = 'release_date' def __str__(self): return self.name def get_absolute_url(self): if not self.content.raw and self.release_page: return self.release_page.get_absolute_url() else: return reverse('download:download_release_detail', kwargs={'release_slug': self.slug}) def download_file_for_os(self, os_slug): """ Given an OS slug return the appropriate download file """ try: file = self.files.get(os__slug=os_slug, download_button=True) except ReleaseFile.DoesNotExist: file = None return file def files_for_os(self, os_slug): """ Return all files for this release for a given OS """ files = self.files.filter(os__slug=os_slug).order_by('-name') return files def get_version(self): version = re.match(r'Python\s([\d.]+)', self.name) if version is not None: return version.group(1) return None
class Question(KnowledgeBase): is_question = True _requesting_user = None title = models.CharField(max_length=255, verbose_name=_('Question'), help_text=_('Enter your question or suggestion.')) body = MarkupField(blank=True, null=True, verbose_name=_('Description'), default_markup_type="markdown", help_text=_('Please offer details. Markdown enabled.')) status = models.CharField(verbose_name=_('Status'), max_length=32, choices=STATUSES, default='private', db_index=True) locked = models.BooleanField(default=False) categories = models.ManyToManyField('knowledge.Category', blank=True) objects = QuestionManager() class Meta: ordering = ['-added'] verbose_name = _('Question') verbose_name_plural = _('Questions') def __unicode__(self): return self.title def get_absolute_url(self): from django.template.defaultfilters import slugify if settings.SLUG_URLS: return reverse('knowledge_thread', kwargs={ 'question_id': self.id, 'slug': slugify(self.title) }) else: return reverse('knowledge_thread_no_slug', kwargs={'question_id': self.id}) def inherit(self): pass def internal(self): pass def lock(self, save=True): self.locked = not self.locked if save: self.save() lock.alters_data = True ################### #### RESPONSES #### ################### def get_responses(self, user=None): user = user or self._requesting_user if user: return [ r for r in self.responses.all().select_related('user') if r.can_view(user) ] else: return self.responses.all().select_related('user') def answered(self): """ Returns a boolean indictating whether there any questions. """ return bool(self.get_responses()) def accepted(self): """ Returns a boolean indictating whether there is a accepted answer or not. """ return any([r.accepted for r in self.get_responses()]) def clear_accepted(self): self.get_responses().update(accepted=False) clear_accepted.alters_data = True def accept(self, response=None): """ Given a response, make that the one and only accepted answer. Similar to StackOverflow. """ self.clear_accepted() if response and response.question == self: response.accepted = True response.save() return True else: return False accept.alters_data = True def states(self): """ Handy for checking for mod bar button state. """ return [self.status, 'lock' if self.locked else None] @property def url(self): return self.get_absolute_url()
def test_markuptextarea_used(self): self.assertTrue(isinstance(MarkupField().formfield().widget, MarkupTextarea)) self.assertTrue(isinstance(ArticleForm()['normal_field'].field.widget, MarkupTextarea))
class AdminAlert(models.Model): """An un-dismissable alert from a superuser to site users. Not dependent on project. Will expire after a set time.""" #: Alert message to be shown for users message = models.CharField( max_length=255, unique=False, help_text='Alert message to be shown for users', ) #: Superuser who has set the alert user = models.ForeignKey( AUTH_USER_MODEL, related_name='alerts', help_text='Superuser who has set the alert', on_delete=models.CASCADE, ) #: Full description (optional, will be shown on a separate page) description = MarkupField( unique=False, blank=True, null=True, markup_type='markdown', help_text='Full description of alert ' '(optional, will be shown on a separate page)', ) #: Alert creation timestamp date_created = models.DateTimeField(auto_now_add=True, help_text='Alert creation timestamp') #: Alert expiration timestamp date_expire = models.DateTimeField(blank=False, null=False, help_text='Alert expiration timestamp') #: Alert status (for disabling the alert before expiration) active = models.BooleanField( default=True, help_text='Alert status (for disabling the alert before expiration)', ) #: Require authorization to view alert require_auth = models.BooleanField( default=True, help_text='Require authorization to view alert') #: Adminalerts SODAR UUID sodar_uuid = models.UUIDField(default=uuid.uuid4, unique=True, help_text='Adminalerts SODAR UUID') def __str__(self): return '{}{}'.format( self.message, ' [ACTIVE]' if (self.active and self.date_expire > timezone.now()) else '', ) def __repr__(self): values = (self.message, self.user.username, self.active) return 'AdminAlert({})'.format(', '.join(repr(v) for v in values)) def is_active(self): """Return True if alert is active and has not expired""" return (True if (self.date_expire > timezone.now() and self.active) else False) def is_expired(self): """Return True if alert has expired""" return True if self.date_expire < timezone.now() else False
class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Entry', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, verbose_name='modified', editable=False)), ('title', models.CharField(max_length=500)), ('slug', models.SlugField(unique=True, editable=False)), ('content', MarkupField(default_markup_type='markdown')), ('is_published', models.BooleanField(default=False)), ('published_timestamp', models.DateTimeField(null=True, editable=False, blank=True)), ('_content_rendered', models.TextField(editable=False, blank=True)), ('author', models.ForeignKey(editable=False, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), ], options={ 'verbose_name_plural': 'entries', }, bases=(models.Model, ), ), migrations.CreateModel( name='EntryImage', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, verbose_name='created', editable=False)), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, verbose_name='modified', editable=False)), ('image', models.ImageField(upload_to=b'andablog/images', blank=True)), ('entry', models.ForeignKey(to='andablog.Entry', on_delete=models.CASCADE)), ], options={ 'abstract': False, }, bases=(models.Model, ), ), ]
class BlogEntry(models.Model): """Each blog entry. Title: Post title. Slug: Post slug. These two if not given are inferred directly from entry text. text = The main data for the post. summary = The summary for the text. probably can be derived from text, but we dont want do do that each time main page is displayed. created_on = The date this entry was created. Defaults to now. Created by: The user who wrote this. is_page: Is this a page or a post? Pages are the more important posts, which might be displayed differently. Defaults to false. is_published: Is this page published. If yes then we would display this on site, otherwise no. Defaults to true. comments_allowed: Are comments allowed on this post? Defaults to True is_rte: Was this post done using a Rich text editor?""" title = models.CharField(max_length=100) slug = models.SlugField() text = MarkupField(default_markup_type=getattr(settings, 'DEFAULT_MARKUP_TYPE', 'plain'), markup_choices=getattr(settings, "MARKUP_RENDERERS", DEFAULT_MARKUP_TYPES)) summary = models.TextField() created_on = models.DateTimeField(default=datetime.max, editable=False) created_by = models.ForeignKey(User, unique=False) is_page = models.BooleanField(default=False) is_published = models.BooleanField(default=True) publish_date = models.DateTimeField() comments_allowed = models.BooleanField(default=True) is_rte = models.BooleanField(default=False) meta_keywords = models.TextField(blank=True, null=True) meta_description = models.TextField(blank=True, null=True) tags = TaggableManager() default = models.Manager() objects = BlogPublishedManager() class Meta: ordering = ['-created_on'] verbose_name_plural = 'Blog entries' def __unicode__(self): return self.title def save(self, *args, **kwargs): if self.title is None or self.title == '': self.title = _infer_title_or_slug(self.text.raw) if self.slug is None or self.slug == '': self.slug = slugify(self.title) i = 1 while True: created_slug = self.create_slug(self.slug, i) slug_count = BlogEntry.objects.filter(slug__exact=created_slug).exclude(pk=self.pk) if not slug_count: break i += 1 self.slug = created_slug if not self.summary: self.summary = _generate_summary(self.text.raw) if not self.meta_keywords: self.meta_keywords = self.summary if not self.meta_description: self.meta_description = self.summary if self.is_published: #default value for created_on is datetime.max whose year is 9999 if self.created_on.year == 9999: self.created_on = self.publish_date # Call the "real" save() method. super(BlogEntry, self).save(*args, **kwargs) def create_slug(self, initial_slug, i=1): if not i == 1: initial_slug += "-%s" % (i,) return initial_slug def get_absolute_url(self): return reverse('blogango_details', kwargs={'year': self.created_on.strftime('%Y'), 'month': self.created_on.strftime('%m'), 'slug': self.slug}) def get_edit_url(self): return reverse('blogango_admin_entry_edit', args=[self.id]) def get_num_comments(self): cmnt_count = Comment.objects.filter(comment_for=self, is_spam=False).count() return cmnt_count def get_num_reactions(self): reaction_count = Reaction.objects.filter(comment_for=self).count() return reaction_count
class Release(models.Model): version = models.CharField(max_length=50, unique=True) version_num = models.IntegerField(default=0, unique=True) release_notes = MarkupField(default_markup_type='markdown') stable = models.BooleanField(default=False, db_index=True) snapshot = models.BooleanField(default=False, db_index=True) date = models.DateTimeField(db_index=True, default=timezone.now) purged = False class Meta(object): ordering = ['-version_num'] def __unicode__(self): return self.version def get_absolute_url(self): if self.snapshot: return reverse('downloads') return reverse('release', kwargs={'version': self.version}) def simpledownload(self): try: return self.download_set.get( filename__endswith='-all-languages.zip') except Download.DoesNotExist: try: return self.download_set.all()[0] except IndexError: return None @staticmethod def parse_version(version): if '+' in version: # Snapshots, eg. 4.7+snapshot parts = [int(x) for x in version.split('+')[0].split('.')] assert len(parts) == 2 return (100000000 * parts[0] + 1000000 * parts[1]) if '-' in version: version, suffix = version.split('-') if suffix.startswith('alpha'): suffix_num = int(suffix[5:]) elif suffix.startswith('beta'): suffix_num = 10 + int(suffix[4:]) elif suffix.startswith('rc'): suffix_num = 50 + int(suffix[2:]) else: raise ValueError(version) else: suffix_num = 99 version = version parts = [int(x) for x in version.split('.')] if len(parts) == 2: parts.append(0) if len(parts) == 3: parts.append(0) assert len(parts) == 4 return (100000000 * parts[0] + 1000000 * parts[1] + 10000 * parts[2] + 100 * parts[3] + suffix_num) def save(self, *args, **kwargs): self.version_num = self.parse_version(self.version) self.stable = self.version_num % 100 == 99 super(Release, self).save(*args, **kwargs) def get_version_suffix(self): ''' Returns suffix for a version. ''' for match, result in VERSION_INFO: if self.version.find(match) != -1: return result return '' def get_php_versions(self): if self.version[:3] == '5.1': return '>=7.1,<7.3' elif self.version[:3] == '5.0': return '>=7.1,<7.3' elif self.version[:3] == '4.9': return '>=5.5,<7.3' elif self.version[:3] == '4.8': return '>=5.5,<7.3' elif self.version[:3] == '4.7': return '>=5.5,<7.3' elif self.version[:3] == '4.6': return '>=5.5,<7.2' elif self.version[:3] == '4.5': return '>=5.5,<7.1' elif self.version[:3] == '4.4': return '>=5.3,<7.1' elif self.version[:3] == '4.3': return '>=5.3,<7.0' elif self.version[:3] == '4.2': return '>=5.3,<7.0' elif self.version[:3] == '4.1': return '>=5.3,<7.0' elif self.version[:3] == '4.0': return '>=5.2,<5.3' def get_mysql_versions(self): if self.version[:3] == '5.1': return '>=5.5' elif self.version[:3] == '5.0': return '>=5.5' elif self.version[:3] == '4.9': return '>=5.5' elif self.version[:3] == '4.8': return '>=5.5' elif self.version[:3] == '4.7': return '>=5.5' elif self.version[:3] == '4.6': return '>=5.5' elif self.version[:3] == '4.5': return '>=5.5' elif self.version[:3] == '4.4': return '>=5.5' elif self.version[:3] == '4.3': return '>=5.5' elif self.version[:3] == '4.2': return '>=5.5' elif self.version[:3] == '4.1': return '>=5.5' elif self.version[:3] == '4.0': return '>=5.0' def get_version_info(self): ''' Returns description to the phpMyAdmin version. ''' text = '' if self.version[:2] == '0.': text = 'Historical release.' elif self.version[:2] == '1.': text = 'Historical release.' elif self.version[:2] == '2.': text = 'Version compatible with PHP 4+ and MySQL 3+.' elif self.version[:2] == '3.': text = ('Frames version not requiring Javascript. ' + 'Requires PHP 5.2 and MySQL 5. ' + 'Supported for security fixes only, until Jan 1, 2014.') elif self.version[:3] == '5.1': text = ( 'Future version compatible with PHP 7.1 and newer and MySQL 5.5 and newer. ' ) elif self.version[:3] == '5.0': text = ( 'Future version compatible with PHP 7.1 and newer and MySQL 5.5 and newer. ' ) elif self.version[:3] == '4.9': text = ( 'Current version compatible with PHP 5.5 to 7.2 and MySQL 5.5 and newer. ' ) elif self.version[:3] == '4.8': text = ( 'Older version compatible with PHP 5.5 to 7.2 and MySQL 5.5 and newer. ' + 'Was supported until June 4, 2019.') elif self.version in ('4.7.0', '4.7.1', '4.7.2', '4.7.3', '4.7.0-rc1', '4.7.0-beta1'): text = ( 'Older version compatible with PHP 5.5 to 7.1 and MySQL 5.5 and newer. ' + 'Was supported until April 7, 2018.') elif self.version[:3] == '4.7': text = ( 'Older version compatible with PHP 5.5 to 7.2 and MySQL 5.5 and newer. ' + 'Was supported until April 7, 2018.') elif self.version[:3] == '4.6': text = ( 'Older version compatible with PHP 5.5 to 7.1 and MySQL 5.5 and newer. ' + 'Was supported until April 1, 2017.') elif self.version[:3] == '4.5': text = ( 'Older version compatible with PHP 5.5 to 7.0 and MySQL 5.5. ' + 'Was supported until April 1, 2016.') elif self.version[:3] == '4.4': text = ( 'Older version compatible with PHP 5.3.7 to 7.0 and MySQL 5.5. ' + 'Was supported until October 1, 2016.') elif self.version[:3] == '4.3': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Was supported until October 1, 2015.') elif self.version[:3] == '4.2': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Was supported until July 1, 2015.') elif self.version[:3] == '4.1': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Was supported until January 1, 2015.') elif self.version[:3] == '4.0': text = ('Older version compatible with PHP 5.2 and MySQL 5. ' + 'Does not support PHP 5.5 or newer. ' + 'Was supported until April 1, 2017.') text += self.get_version_suffix() return text def get_downloads(self): """Lists downloads, making all-languages.zip first""" dlset = self.download_set return (list(dlset.filter(filename__endswith='all-languages.zip')) + list(dlset.exclude(filename__endswith='all-languages.zip')))
class Presentation(models.Model): slot = models.OneToOneField(Slot, null=True, blank=True, related_name="content_ptr", on_delete=SET_NULL) title = models.CharField(max_length=100, default="", blank=True) description = MarkupField(default="", blank=True, default_markup_type='markdown') abstract = MarkupField(default="", blank=True, default_markup_type='markdown') speaker = models.ForeignKey("speakers.Speaker", related_name="presentations") additional_speakers = models.ManyToManyField("speakers.Speaker", related_name="copresentations", blank=True) cancelled = models.BooleanField(default=False) proposal_base = models.OneToOneField("proposals.ProposalBase", related_name="presentation") section = models.ForeignKey("conference.Section", related_name="presentations") video_url = models.URLField(_("video URL"), blank=True, null=True) keynote_url = models.URLField(_("keynote URL"), blank=True, null=True) keynote = models.FileField(_("keynote file"), blank=True, null=True, upload_to="keynotes") @property def number(self): return self.proposal.number @property def proposal(self): if self.proposal_base_id is None: return None return ProposalBase.objects.get_subclass(pk=self.proposal_base_id) def speakers(self): yield self.speaker for speaker in self.additional_speakers.all(): if speaker.user: yield speaker def get_title(self): if self.title: return self.title return self.proposal_base.title def get_description(self): if self.description.raw: return self.description return self.proposal_base.description def get_abstract(self): if self.abstract.raw: return self.abstract return self.proposal_base.abstract def get_video_url(self): if not self.video_url: return "" return self.video_url def get_keynote_url(self): if self.keynote and not self.keynote_url: return self.keynote.url elif self.keynote_url: return self.keynote_url return "" def __str__(self): return "#%s %s (%s)" % (self.number, self.get_title(), self.speaker) class Meta: ordering = ["slot"]
class Nomination(models.Model): def __str__(self): return f"{self.name} <{self.email}>" election = models.ForeignKey(Election, on_delete=models.CASCADE) name = models.CharField(max_length=1024, blank=False, null=True) email = models.CharField(max_length=1024, blank=False, null=True) previous_board_service = models.CharField(max_length=1024, blank=False, null=True) employer = models.CharField(max_length=1024, blank=False, null=True) other_affiliations = models.CharField(max_length=2048, blank=True, null=True) nomination_statement = MarkupField(escape_html=True, markup_type="markdown", blank=False, null=True) nominator = models.ForeignKey(User, related_name="nominations_made", on_delete=models.CASCADE) nominee = models.ForeignKey( Nominee, related_name="nominations", null=True, on_delete=models.CASCADE, blank=True, ) accepted = models.BooleanField(null=False, default=False) approved = models.BooleanField(null=False, default=False) def get_absolute_url(self): return reverse( "nominations:nomination_detail", kwargs={ "election": self.election.slug, "pk": self.pk }, ) def get_edit_url(self): return reverse( "nominations:nomination_edit", kwargs={ "election": self.election.slug, "pk": self.pk }, ) def editable(self, user=None): if (self.nominee and user == self.nominee.user and self.election.nominations_open): return True if (user == self.nominator and not (self.accepted or self.approved) and self.election.nominations_open): return True return False def visible(self, user=None): if self.accepted and self.approved and not self.election.nominations_open_at: return True if user is None: return False if user.is_staff: return True if user == self.nominator: return True if self.nominee and user == self.nominee.user: return True return False
class Release(models.Model): version = models.CharField(max_length=50, unique=True) version_num = models.IntegerField(default=0, unique=True) release_notes = MarkupField(default_markup_type='markdown') stable = models.BooleanField(default=False, db_index=True) date = models.DateTimeField(db_index=True, default=timezone.now) class Meta(object): ordering = ['-version_num'] def __unicode__(self): return self.version @models.permalink def get_absolute_url(self): return ('release', (), {'version': self.version}) def simpledownload(self): try: return self.download_set.get( filename__endswith='-all-languages.zip') except Download.DoesNotExist: return self.download_set.all()[0] @staticmethod def parse_version(version): if '-' in version: version, suffix = version.split('-') if suffix.startswith('alpha'): suffix_num = int(suffix[5:]) elif suffix.startswith('beta'): suffix_num = 10 + int(suffix[4:]) elif suffix.startswith('rc'): suffix_num = 50 + int(suffix[2:]) else: raise ValueError(version) else: suffix_num = 99 version = version parts = [int(x) for x in version.split('.')] if len(parts) == 2: parts.append(0) if len(parts) == 3: parts.append(0) assert len(parts) == 4 return (100000000 * parts[0] + 1000000 * parts[1] + 10000 * parts[2] + 100 * parts[3] + suffix_num) def save(self, *args, **kwargs): self.version_num = self.parse_version(self.version) self.stable = self.version_num % 100 == 99 super(Release, self).save(*args, **kwargs) def get_version_suffix(self): ''' Returns suffix for a version. ''' for match, result in VERSION_INFO: if self.version.find(match) != -1: return result return '' def get_php_versions(self): if self.version[:3] == '4.5': return '>=5.5,<7.1' elif self.version[:3] == '4.4': return '>=5.3,<7.1' elif self.version[:3] == '4.3': return '>=5.3,<7.0' elif self.version[:3] == '4.2': return '>=5.3,<7.0' elif self.version[:3] == '4.1': return '>=5.3,<7.0' elif self.version[:3] == '4.0': return '>=5.2,<5.3' def get_mysql_versions(self): if self.version[:3] == '4.5': return '>=5.5' elif self.version[:3] == '4.4': return '>=5.5' elif self.version[:3] == '4.3': return '>=5.5' elif self.version[:3] == '4.2': return '>=5.5' elif self.version[:3] == '4.1': return '>=5.5' elif self.version[:3] == '4.0': return '>=5.0' def get_version_info(self): ''' Returns description to the phpMyAdmin version. ''' if self.version[:2] == '1.': text = 'Historical release.' elif self.version[:2] == '2.': text = 'Version compatible with PHP 4+ and MySQL 3+.' elif self.version[:2] == '3.': text = ('Frames version not requiring Javascript. ' + 'Requires PHP 5.2 and MySQL 5. ' + 'Supported for security fixes only, until Jan 1, 2014.') elif self.version[:3] == '4.5': text = 'Current version compatible with PHP 5.5 to 7.0 and MySQL 5.5.' elif self.version[:3] == '4.4': text = ( 'Older version compatible with PHP 5.3.7 to 7.0 and MySQL 5.5. ' + 'Supported for security fixes only, until April 1, 2016') elif self.version[:3] == '4.3': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Supported for security fixes only, until Oct 1, 2015.') elif self.version[:3] == '4.2': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Supported for security fixes only, until Jul 1, 2015.') elif self.version[:3] == '4.1': text = ('Older version compatible with PHP 5.3 and MySQL 5.5. ' + 'Supported for security fixes only, until Jan 1, 2015.') elif self.version[:3] == '4.0': text = ('Older version compatible with PHP 5.2 and MySQL 5. ' + 'Supported for security fixes only, until Apr 1, 2017.') text += self.get_version_suffix() return text