class BTCWallet(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) provider = models.CharField(max_length=30, choices=BTC_WALLET_PROVIDER_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in BTC_WALLET_PROVIDER_CHOICES ])) token = models.TextField( verbose_name='token', help_text='"oauth_token" (OAuth1) or access token (OAuth2)') token_secret = models.TextField( blank=True, verbose_name='token secret', help_text='"oauth_token_secret" (OAuth1) or refresh token (OAuth2)') expires_at = models.DateTimeField(blank=True, null=True, verbose_name='expires at') updated_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('user', 'provider') verbose_name = 'bitcoin wallet' def __unicode__(self): return '%s - %s' % (self.user.get_short_name(), self.get_provider_display())
class AppIntegration(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) provider = models.CharField( max_length=30, choices=APP_INTEGRATION_PROVIDER_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in APP_INTEGRATION_PROVIDER_CHOICES ])) token = models.TextField( verbose_name='token', help_text='"oauth_token" (OAuth1) or access token (OAuth2)') token_secret = models.TextField( blank=True, verbose_name='token secret', help_text='"oauth_token_secret" (OAuth1) or refresh token (OAuth2)') extra = models.TextField(blank=True, null=True) # JSON formatted extra details expires_at = models.DateTimeField(blank=True, null=True, verbose_name='expires at') updated_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('user', 'provider') verbose_name = 'app integration' verbose_name_plural = 'app integrations' def __unicode__(self): return '%s - %s' % (self.user.get_short_name(), self.get_provider_display())
class DeveloperApplication(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField(unique=True, validators=[validate_email]) phone_number = models.CharField(max_length=15) country = CountryField() city = models.CharField(max_length=50) stack = models.TextField() experience = models.TextField() discovery_story = models.TextField() status = models.PositiveSmallIntegerField( choices=APPLICATION_STATUS_CHOICES, help_text=','.join(['%s - %s' % (item[0], item[1]) for item in APPLICATION_STATUS_CHOICES]), default=REQUEST_STATUS_INITIAL ) created_at = models.DateTimeField(auto_now_add=True) confirmation_key = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) confirmation_sent_at = models.DateTimeField(blank=True, null=True, editable=False) used = models.BooleanField(default=False) used_at = models.DateTimeField(blank=True, null=True, editable=False) def __str__(self): return self.display_name @property def display_name(self): return '%s %s' % (self.first_name, self.last_name) @property def country_name(self): return self.country.name country_name.fget.short_description = 'country'
class Release(models.Model): types = [ ('release', 'release'), ('event', 'event'), ] release_date = models.DateField() release_type = models.CharField(max_length=999, choices=types) title = models.CharField(max_length=999) important_tags = tagulous.models.TagField( to="general.customtag", related_name="release_important_tags", default=None, blank=True) other_tags = tagulous.models.TagField(to="general.customtag", related_name="release_other_tags", default=None, blank=True) snippet = models.CharField(max_length=999, blank=True, null=True) description = models.TextField( blank=True, null=True, ) video = models.CharField(max_length=999, blank=True, null=True) external_link = models.CharField(max_length=999, blank=True, null=True) review_link = models.ForeignKey(Publication, blank=True, null=True, related_name="review_link", on_delete=models.CASCADE) def __str__(self): return self.title
class UserProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) bio = models.TextField(max_length=500, blank=True, null=True) country = CountryField(blank=True, null=True) city = tagulous.models.SingleTagField(to=City, blank=True, null=True) street = models.CharField(max_length=100, blank=True, null=True) plot_number = models.CharField(max_length=100, blank=True, null=True) postal_code = models.IntegerField(blank=True, null=True) postal_address = models.CharField(max_length=100, blank=True, null=True) phone_number = models.CharField(max_length=15, blank=True, null=True) company = models.CharField(max_length=200, blank=True, null=True) skills = tagulous.models.TagField(to=Skill, blank=True) website = models.URLField(blank=True, null=True) def __unicode__(self): return self.user.get_short_name() @property def city_name(self): return str(self.city) @property def country_name(self): return self.country.name @allow_staff_or_superuser def has_object_read_permission(self, request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user
class ContactForm(models.Model): """ Contact form """ submission_types = {("tecnico", "tecnico"), ("particular", "particular"), ("empresa", "empresa"), ("inapropiado", "inapropiado"), ("patreon_link", "patreon_link"), ("otros", "otros")} content = models.TextField() subject = models.TextField() username = models.CharField(max_length=100) email = models.CharField(max_length=100) submission_type = models.CharField(max_length=100, choices=submission_types) timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return str(self.content)
class Document(models.Model): project = models.ForeignKey(Project) type = models.CharField(choices=PROJECT_DOCUMENT_CHOICES, max_length=30, default=DOC_OTHER) url = models.URLField(blank=True, null=True) file = models.FileField(verbose_name='Upload', upload_to='documents/%Y/%m/%d', blank=True, null=True) title = models.CharField(max_length=100, blank=True, null=True) description = models.TextField(blank=True, null=True) created_by = models.ForeignKey(settings.AUTH_USER_MODEL) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) legacy_id = models.PositiveIntegerField(blank=True, null=True) migrated_at = models.DateTimeField(blank=True, null=True) def __str__(self): return '{} | {}'.format(self.type, self.project) class Meta: ordering = ['-created_at'] @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return self.project.is_participant(request.user) @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.is_project_manager or request.user.is_project_owner @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.created_by @property def download_url(self): if self.file: return '{}{}'.format( not re.match(r'://', self.file.url) and TUNGA_URL or '', self.file.url) elif self.url: return self.url return None
class ProgressReport(models.Model): event = models.OneToOneField(ProgressEvent, on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING) status = models.PositiveSmallIntegerField( choices=PROGRESS_REPORT_STATUS_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in PROGRESS_REPORT_STATUS_CHOICES ])) percentage = models.PositiveIntegerField( validators=[MinValueValidator(0), MaxValueValidator(100)]) accomplished = models.TextField() next_steps = models.TextField(blank=True, null=True) remarks = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) uploads = GenericRelation(Upload, related_query_name='progress_reports') def __unicode__(self): return '{0} - {1}%'.format(self.event, self.percentage) @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return self.event.task.has_object_read_permission(request) @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.type == USER_TYPE_DEVELOPER @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user
class Application(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) task = models.ForeignKey(Task, on_delete=models.CASCADE) accepted = models.BooleanField(default=False) responded = models.BooleanField(default=False) pitch = models.CharField(max_length=1000, blank=True, null=True) hours_needed = models.PositiveIntegerField(blank=True, null=True) hours_available = models.PositiveIntegerField(blank=True, null=True) remarks = models.TextField( blank=True, null=True) # These will also be delivered as messages to the client deliver_at = models.DateTimeField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) channels = GenericRelation(Channel, related_query_name='task_applications') def __unicode__(self): return '%s - %s' % (self.user.get_short_name() or self.user.username, self.task.summary) class Meta: unique_together = ('user', 'task') @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return self.has_object_update_permission(request) @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.type == USER_TYPE_DEVELOPER @staticmethod @allow_staff_or_superuser def has_update_permission(request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user @allow_staff_or_superuser def has_object_update_permission(self, request): # Task owner can update applications return request.user == self.user or request.user == self.task.user
class Subject(models.Model): """Model to store information about a Subject""" name = models.CharField(max_length=255, unique=True, verbose_name="Name") slug = models.SlugField(max_length=150, unique=True, editable=False, verbose_name="Slug") description = models.TextField(blank=True, verbose_name="Description") class Meta: ordering = ['name'] def __str__(self): return self.name def save(self, *args, **kwargs): self.slug = slugify(self.name, allow_unicode=True) super().save(*args, **kwargs)
class Employee(models.Model): company = models.ForeignKey('business.Company') profile = models.ForeignKey('accounts.Profile') primary = models.BooleanField(default=False) title = models.CharField(max_length=255, blank=True, null=True) start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) current = models.BooleanField(default=True) city = models.CharField(max_length=255, blank=True, null=True) state = models.CharField(max_length=255, blank=True, null=True) country = models.CharField(max_length=255, blank=True, null=True) description = models.TextField(blank=True, null=True) class Meta: ordering = ['-current', '-end_date', '-start_date']
class ProjectMeta(models.Model): project = models.ForeignKey(Project, on_delete=models.CASCADE) meta_key = models.CharField(max_length=30) meta_value = models.TextField() created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='project_meta_created', blank=True, null=True, on_delete=models.DO_NOTHING) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return '{} | {} - {}'.format(self.project, self.meta_key, self.meta_value) class Meta: ordering = ['created_at'] unique_together = ('project', 'meta_key')
class Project(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='projects_created', on_delete=models.DO_NOTHING) title = models.CharField(max_length=200) description = models.TextField(blank=True, null=True) deadline = models.DateTimeField(blank=True, null=True) closed = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) closed_at = models.DateTimeField(blank=True, null=True) def __unicode__(self): return self.title class Meta: ordering = ['-created_at'] unique_together = ('user', 'title') @staticmethod @allow_staff_or_superuser def has_read_permission(request): return request.user.type == USER_TYPE_PROJECT_OWNER @allow_staff_or_superuser def has_object_read_permission(self, request): return request.user == self.user @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.type == USER_TYPE_PROJECT_OWNER @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user @property def excerpt(self): try: return strip_tags(self.description).strip() except: return None
class Content(models.Model): """Model to store content related to a subject""" subject = models.ManyToManyField(Subject, blank=True, related_name='content') title = models.CharField(max_length=255, verbose_name="Title") slug = models.SlugField(max_length=150, unique=True, editable=False, verbose_name="Slug") url = models.URLField(blank=True, null=True, default='', verbose_name="URL") content_id = models.CharField(max_length=255) type = models.CharField(max_length=150, choices=media_types, default='other', verbose_name="Type") image = models.ImageField(upload_to='content_images', blank=True, null=True, verbose_name="Image") image_thumbnail = ImageSpecField(source='image', processors=[ResizeToFill(100, 150)], options={'quality': 100}) description = models.TextField(blank=True, verbose_name="Description") tags = tagulous.models.TagField(to=Tag, related_name='content_tag') topics = tagulous.models.TagField(to=Topic, related_name='content_topic') class Meta: ordering = ['title'] def __str__(self): return self.title def save(self, *args, **kwargs): self.slug = str(self.content_id) + '-' + slugify(self.title) super().save(*args, **kwargs) def url_text(self): if self.url and '//' not in self.url: self.url = '%s%s' % ('https://', self.url) parsed_url = urlparse(self.url) if parsed_url.hostname: return parsed_url.hostname.replace("www.", "") + "/..." else: return ""
class List(models.Model): """Content lists which can be created for each user.""" user = models.ForeignKey(BucketUser, on_delete=models.CASCADE) date_created = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=255, verbose_name="Name") slug = models.SlugField(max_length=150, unique=True, editable=False, verbose_name="Slug") description = models.TextField(blank=True, verbose_name="Description") image = models.ImageField(upload_to='list_images', blank=True, null=True, verbose_name="Image") image_thumbnail = ImageSpecField(source='image', processors=[ResizeToFill(100, 150)], options={'quality': 100}) content = models.ManyToManyField(Content, blank=True, related_name='content', verbose_name='Content') visibility = models.CharField(max_length=150, choices=visibility, default='public', verbose_name="Visibility") topics = tagulous.models.TagField(to=Topic, related_name='list_topic') list_bookmarked_by = models.ManyToManyField(BucketUser, related_name='list_bookmark') class Meta: ordering = ['name'] def __str__(self): return "{0} by {1}".format(self.name, self.user) def save(self, *args, **kwargs): self.slug = add_slug(self) super().save(*args, **kwargs)
class IntegrationActivity(models.Model): integration = models.ForeignKey(Integration, on_delete=models.CASCADE, related_name='activities') event = models.ForeignKey(IntegrationEvent, related_name='integration_activities') action = models.CharField(max_length=30, blank=True, null=True) url = models.URLField(blank=True, null=True) ref = models.CharField(max_length=30, blank=True, null=True) ref_name = models.CharField(max_length=50, blank=True, null=True) username = models.CharField(max_length=30, blank=True, null=True) fullname = models.CharField(max_length=50, blank=True, null=True) avatar_url = models.URLField(blank=True, null=True) title = models.CharField(max_length=200, blank=True, null=True) body = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __unicode__(self): return '%s | ' % (self.integration, ) class Meta: ordering = ['created_at']
class ProgressEvent(models.Model): project = models.ForeignKey(Project, on_delete=models.CASCADE) type = models.CharField(max_length=50, choices=PROGRESS_EVENT_TYPE_CHOICES, default=PROGRESS_EVENT_DEVELOPER, help_text=','.join([ '{} - {}'.format(item[0], item[1]) for item in PROGRESS_EVENT_TYPE_CHOICES ])) due_at = models.DateTimeField() title = models.CharField(max_length=200, blank=True, null=True) description = models.TextField(blank=True, null=True) last_reminder_at = models.DateTimeField(blank=True, null=True) missed_notification_at = models.DateTimeField(blank=True, null=True) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='progress_events_created', blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) legacy_id = models.PositiveIntegerField(blank=True, null=True) migrated_at = models.DateTimeField(blank=True, null=True) activity_objects = GenericRelation( Action, object_id_field='target_object_id', content_type_field='target_content_type', related_query_name='progress_events') def __str__(self): return '{} | {} - {}'.format(self.type, self.title, self.due_at) class Meta: unique_together = ('project', 'type', 'due_at') ordering = ['-due_at'] @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return self.project.is_participant(request.user) @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.is_project_owner or request.user.is_project_manager or request.user.is_project_owner @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.project.user or request.user == self.project.owner @property def participants(self): participants = [] if self.type in [PROGRESS_EVENT_CLIENT, PROGRESS_EVENT_MILESTONE]: if self.project.owner: participants.append(self.project.owner) else: participants.append(self.project.user) if self.type in [ PROGRESS_EVENT_PM, PROGRESS_EVENT_MILESTONE, PROGRESS_EVENT_INTERNAL ] and self.project.pm: participants.append(self.project.pm) if self.type in [PROGRESS_EVENT_DEVELOPER, PROGRESS_EVENT_MILESTONE]: participants.extend([ participant.user for participant in self.project.participation_set.filter( status=STATUS_ACCEPTED, updates_enabled=True) ]) return participants @property def status(self): if self.progressreport_set.count() > 0: return 'completed' past_by_24_hours = datetime.datetime.utcnow() - relativedelta(hours=24) if self.due_at > past_by_24_hours: return 'upcoming' return 'missed'
class Task(models.Model): project = models.ForeignKey(Project, related_name='tasks', on_delete=models.DO_NOTHING, blank=True, null=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tasks_created', on_delete=models.DO_NOTHING) title = models.CharField(max_length=200) description = models.TextField(blank=True, null=True) remarks = models.TextField(blank=True, null=True) url = models.URLField(blank=True, null=True) fee = models.BigIntegerField() currency = models.CharField(max_length=5, choices=CURRENCY_CHOICES, default=CURRENCY_CHOICES[0][0]) deadline = models.DateTimeField(blank=True, null=True) skills = tagulous.models.TagField(Skill, blank=True) visibility = models.PositiveSmallIntegerField( choices=VISIBILITY_CHOICES, default=VISIBILITY_CHOICES[0][0]) update_interval = models.PositiveIntegerField(blank=True, null=True) update_interval_units = models.PositiveSmallIntegerField( choices=UPDATE_SCHEDULE_CHOICES, blank=True, null=True) apply = models.BooleanField(default=True) closed = models.BooleanField(default=False) paid = models.BooleanField(default=False) applicants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Application', through_fields=('task', 'user'), related_name='task_applications', blank=True) participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation', through_fields=('task', 'user'), related_name='task_participants', blank=True) satisfaction = models.SmallIntegerField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) apply_closed_at = models.DateTimeField(blank=True, null=True) closed_at = models.DateTimeField(blank=True, null=True) paid_at = models.DateTimeField(blank=True, null=True) comments = GenericRelation(Comment, related_query_name='tasks') uploads = GenericRelation(Upload, related_query_name='tasks') ratings = GenericRelation(Rating, related_query_name='tasks') def __unicode__(self): return self.summary class Meta: ordering = ['-created_at'] unique_together = ('user', 'title', 'fee') @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): if self.visibility == VISIBILITY_DEVELOPER: return request.user.type == USER_TYPE_DEVELOPER elif self.visibility == VISIBILITY_MY_TEAM: return bool( Connection.objects.exclude(accepted=False).filter( Q(from_user=self.user, to_user=request.user) | Q(from_user=request.user, to_user=self.user)).count()) elif self.visibility == VISIBILITY_CUSTOM: return self.participation_set.filter( (Q(accepted=True) | Q(responded=False)), user=request.user).count() return False @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.type == USER_TYPE_PROJECT_OWNER @staticmethod @allow_staff_or_superuser def has_update_permission(request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user @allow_staff_or_superuser def has_object_update_permission(self, request): if self.has_object_write_permission(request): return True # Participants can edit participation info directly on task object if request.method in ['PUT', 'PATCH']: allowed_keys = [ 'assignee', 'participants', 'confirmed_participants', 'rejected_participants' ] if not [x for x in request.data.keys() if not x in allowed_keys]: return self.participation_set.filter( (Q(accepted=True) | Q(responded=False)), user=request.user).count() return False def display_fee(self, amount=None): if amount is None: amount = self.fee if self.currency in CURRENCY_SYMBOLS: return '%s%s' % (CURRENCY_SYMBOLS[self.currency], floatformat(amount, arg=-2)) return amount @property def summary(self): return '%s - Fee: %s' % (self.title, self.display_fee()) @property def excerpt(self): try: return strip_tags(self.description).strip() except: return None @property def skills_list(self): return str(self.skills) @property def milestones(self): return self.progressevent_set.filter(type__in=[ PROGRESS_EVENT_TYPE_MILESTONE, PROGRESS_EVENT_TYPE_SUBMIT ]) @property def progress_events(self): return self.progressevent_set.all() @property def participation(self): return self.participation_set.filter( Q(accepted=True) | Q(responded=False)) @property def assignee(self): try: return self.participation_set.get( (Q(accepted=True) | Q(responded=False)), assignee=True) except: return None @property def update_schedule_display(self): if self.update_interval and self.update_interval_units: if self.update_interval == 1 and self.update_interval_units == UPDATE_SCHEDULE_DAILY: return 'Daily' interval_units = str( self.get_update_interval_units_display()).lower() if self.update_interval == 1: return 'Every %s' % interval_units return 'Every %s %ss' % (self.update_interval, interval_units) return None @property def applications(self): return self.application_set.filter(responded=False) @property def all_uploads(self): return Upload.objects.filter( Q(tasks=self) | Q(comments__tasks=self) | Q(progress_reports__event__task=self)) @property def meta_payment(self): return { 'task_url': '/task/%s/' % self.id, 'amount': self.fee, 'currency': self.currency } def get_default_participation(self): tags = ['tunga.io', 'tunga'] if self.skills: tags.extend(str(self.skills).split(',')) tunga_share = '{share}%'.format(**{'share': TUNGA_SHARE_PERCENTAGE}) return { 'type': 'payment', 'language': 'EN', 'title': self.summary, 'description': self.excerpt or self.summary, 'keywords': tags, 'participants': [{ 'id': 'mailto:%s' % TUNGA_SHARE_EMAIL, 'role': 'owner', 'share': tunga_share }] } def mobbr_participation(self, check_only=False): participation_meta = self.get_default_participation() if not self.url: return participation_meta, False mobbr_info_url = '%s?url=%s' % ( 'https://api.mobbr.com/api_v1/uris/info', urllib.quote_plus(self.url)) r = requests.get(mobbr_info_url, **{'headers': { 'Accept': 'application/json' }}) has_script = False if r.status_code == 200: response = r.json() task_script = response['result']['script'] for meta_key in participation_meta: if meta_key == 'keywords': if isinstance(task_script[meta_key], list): participation_meta[meta_key].extend( task_script[meta_key]) elif meta_key == 'participants': if isinstance(task_script[meta_key], list): absolute_shares = [] relative_shares = [] absolute_participants = [] relative_participants = [] for key, participant in enumerate( task_script[meta_key]): if re.match(r'\d+%$', participant['share']): share = int(participant['share'].replace( "%", "")) if share > 0: absolute_shares.append(share) new_participant = participant new_participant['share'] = share absolute_participants.append( new_participant) else: share = int(participant['share']) if share > 0: relative_shares.append(share) new_participant = participant new_participant['share'] = share relative_participants.append( new_participant) additional_participants = [] total_absolutes = sum(absolute_shares) total_relatives = sum(relative_shares) if total_absolutes >= 100 or total_relatives == 0: additional_participants = absolute_participants elif total_absolutes == 0: additional_participants = relative_participants else: additional_participants = absolute_participants for participant in relative_participants: share = int( round(((participant['share'] * (100 - total_absolutes)) / total_relatives), 0)) if share > 0: new_participant = participant new_participant['share'] = share additional_participants.append( new_participant) if len(additional_participants): participation_meta[meta_key].extend( additional_participants) has_script = True elif meta_key in task_script: participation_meta[meta_key] = task_script[meta_key] return participation_meta, has_script @property def meta_participation(self): participation_meta, has_script = self.mobbr_participation() # TODO: Update local participation script to use defined shares if not has_script: participants = self.participation_set.filter( accepted=True).order_by('share') total_shares = 100 - TUNGA_SHARE_PERCENTAGE num_participants = participants.count() for participant in participants: participation_meta['participants'].append({ 'id': 'mailto:%s' % participant.user.email, 'role': participant.role, 'share': int(total_shares / num_participants) }) return participation_meta
class Project(models.Model): company = models.ForeignKey(Company, blank=True, null=True) project_manager = models.ForeignKey('accounts.Profile') title = models.CharField(max_length=255, unique=True) slug = models.SlugField(max_length=255) type = models.CharField(max_length=100, choices=PROJECT_TYPES, blank=True, null=True) category = models.CharField(max_length=100, blank=True, null=True) start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) expire_date = models.DateField(blank=True, null=True) skills = tagulous.models.TagField(to='accounts.Skills', blank=True, null=True) deleted = models.BooleanField(default=False) estimated_hours = models.IntegerField(blank=True, null=True) estimated_cash = models.IntegerField(blank=True, null=True) estimated_equity_percentage = models.DecimalField(blank=True, null=True, max_digits=4, decimal_places=2) estimated_equity_shares = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=2) date_created = models.DateTimeField(auto_now_add=True) city = models.CharField(max_length=255, blank=True, null=True) state = models.CharField(max_length=255, blank=True, null=True) country = models.CharField(max_length=100, blank=True, null=True) country_code = models.CharField(max_length=100, blank=True, null=True) location = models.CharField(max_length=150, blank=True, null=True) status = models.CharField(max_length=100, blank=True, null=True) remote = models.BooleanField(default=False) featured = models.BooleanField(default=False) mix = models.BooleanField(default=False) background = models.TextField(blank=True, null=True) progress = models.TextField(blank=True, null=True) scope = models.TextField(blank=True, null=True) specs = models.TextField(blank=True, null=True) private_info = models.TextField(blank=True, null=True) published = models.BooleanField(default=False) approved = models.BooleanField(default=False) role = models.CharField(max_length=100, blank=True, null=True) years = models.IntegerField(blank=True, null=True) employment_type = models.CharField(max_length=100, default='freelance') autorenew = models.BooleanField(default=False) sku = models.CharField(max_length=50, blank=True, null=True) views = models.IntegerField(default=0) objects = ProjectManager() def __str__(self): return self.title def __unicode__(self): return self.title class Meta: ordering = ['-date_created'] def save(self, *args, **kwargs): self.slug = slugify(self.title) super(Project, self).save(*args, **kwargs) def preauth(self, promo=None): product = Product.objects.get(sku=self.sku) try: order = Order.objects.get(content_type__pk=self.content_type.id, object_id=self.id, status='preauth') except Order.DoesNotExist: if promo: promo = Promo.objects.get(code=promo.lower()) order = Order(content_object=self, product=product, user=self.project_manager, status='preauth', promo=promo) order.save() order.charge() return order def activate(self): today = datetime.now().date() if not self.sku: self.sku = 'free' product = Product.objects.get(sku=self.sku) self.expire_date = today + timedelta(days=product.interval) self.status = 'active' self.save() return self def subscribe(self, promo=None): try: order = Order.objects.get(content_type__pk=self.content_type.id, object_id=self.id, status='preauth') except Order.DoesNotExist: order = self.preauth(promo=promo) if order.product.sku != self.sku: product = Product.objects.get(sku=self.sku) order.product = product order.save() order.capture() if self.project_manager.referral_code: response = vl_conversion(self.project_manager) context = { 'name': self.project_manager.name, 'title': self.title, 'email': self.project_manager.email, 'location': self.project_manager.location, 'referral_code': self.project_manager.referral_code, 'url': settings.VL_CAMPAIGN_URL } send_to_emails('referral-conversion', settings.ADMINS, context) self.activate() return self def deactivate(self): today = datetime.now().date() if self.expire_date and self.expire_date <= today: if self.sku != 'free': order = Order.objects.get( content_type__pk=self.content_type.id, object_id=self.id, status='active') order.status = 'expired' order.save() self.status = 'expired' self.published = False self.save() if not self.deleted: template = 'project-expired-free' if self.sku == 'free' else 'project-expired' url = '{0}/project/upgrade/{1}/'.format( settings.BASE_URL, self.slug ) if self.sku == 'free' else '{0}/project/renew/{1}/'.format( settings.BASE_URL, self.slug) send_mail( template, [self.project_manager], { 'fname': self.project_manager.first_name, 'title': self.title, 'url': url }) return self @property def content_type(self): return ContentType.objects.get_for_model(self) @property def location(self): return self.city if self.city else self.project_manager.city @property def skills_str(self): return self.skills.get_tag_string() @property def nda_list(self): ndas = NDA.objects.filter(proposal__project=self) nda_list = [nda.receiver.id for nda in ndas] nda_list.append(self.project_manager.id) return nda_list
class UserProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) bio = models.TextField(blank=True, null=True) country = CountryField(blank=True, null=True) city = tagulous.models.SingleTagField(to=City, blank=True, null=True) street = models.CharField(max_length=100, blank=True, null=True) plot_number = models.CharField(max_length=100, blank=True, null=True) postal_code = models.CharField(max_length=20, blank=True, null=True) postal_address = models.CharField(max_length=100, blank=True, null=True) phone_number = models.CharField(max_length=15, blank=True, null=True) id_document = models.ImageField(upload_to='ids/%Y/%m/%d', blank=True, null=True) skills = tagulous.models.TagField(to=Skill, blank=True) company = models.CharField(max_length=200, blank=True, null=True) website = models.URLField(blank=True, null=True) company_profile = models.TextField(blank=True, null=True) company_bio = models.TextField(blank=True, null=True) vat_number = models.CharField(max_length=50, blank=True, null=True) company_reg_no = models.CharField(max_length=50, blank=True, null=True) payment_method = models.CharField(max_length=30, choices=PAYMENT_METHOD_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in PAYMENT_METHOD_CHOICES ]), blank=True, null=True) btc_wallet = models.ForeignKey(BTCWallet, blank=True, null=True, on_delete=models.SET_NULL) btc_address = models.CharField(max_length=40, blank=True, null=True, validators=[validate_btc_address]) mobile_money_cc = models.CharField(max_length=5, choices=MOBILE_MONEY_CC_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in MOBILE_MONEY_CC_CHOICES ]), blank=True, null=True) mobile_money_number = models.CharField(max_length=15, blank=True, null=True) def __unicode__(self): return self.user.get_short_name() @property def city_name(self): return self.city and str(self.city) or "" @property def country_name(self): return str(self.country.name) @allow_staff_or_superuser def has_object_read_permission(self, request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user
class Company(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_company') # Description name = models.CharField(max_length=100, blank=True, null=True) bio = models.TextField(blank=True, null=True) website = models.URLField(blank=True, null=True) # Contact Info country = CountryField(blank=True, null=True) city = tagulous.models.SingleTagField(to=City, blank=True, null=True) street = models.CharField(max_length=100, blank=True, null=True) plot_number = models.CharField(max_length=100, blank=True, null=True) postal_code = models.CharField(max_length=20, blank=True, null=True) postal_address = models.CharField(max_length=100, blank=True, null=True) tel_number = models.CharField(max_length=15, blank=True, null=True) # Stack skills = tagulous.models.TagField(to=Skill, blank=True) # KYC vat_number = models.CharField(max_length=50, blank=True, null=True) reg_no = models.CharField(max_length=50, blank=True, null=True) ref_no = models.CharField(max_length=50, blank=True, null=True) class Meta: verbose_name = 'company' verbose_name_plural = 'companies' def __str__(self): return self.name or 'Company #{}'.format(self.id) @property def city_name(self): return self.city and str(self.city) or "" @property def country_name(self): return str(self.country.name) @property def location(self): location = self.city if self.country_name: location = '{}{}{}'.format(location, location and ', ' or '', self.country.name) return location or '' @allow_staff_or_superuser def has_object_read_permission(self, request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user def get_category_skills(self, skill_type): return self.skills.filter(type=skill_type) @property def skills_details(self): return dict( language=self.get_category_skills(SKILL_TYPE_LANGUAGE), framework=self.get_category_skills(SKILL_TYPE_FRAMEWORK), platform=self.get_category_skills(SKILL_TYPE_PLATFORM), library=self.get_category_skills(SKILL_TYPE_LIBRARY), storage=self.get_category_skills(SKILL_TYPE_STORAGE), api=self.get_category_skills(SKILL_TYPE_API), other=self.get_category_skills(SKILL_TYPE_OTHER), )
class UserProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # Personal Info bio = models.TextField(blank=True, null=True) # Contact Info country = CountryField(blank=True, null=True) city = tagulous.models.SingleTagField(to=City, blank=True, null=True) street = models.CharField(max_length=100, blank=True, null=True) plot_number = models.CharField(max_length=100, blank=True, null=True) postal_code = models.CharField(max_length=20, blank=True, null=True) postal_address = models.CharField(max_length=100, blank=True, null=True) phone_number = models.CharField(max_length=15, blank=True, null=True) # Professional Info skills = tagulous.models.TagField(to=Skill, blank=True) # KYC id_document = models.ImageField(upload_to='ids/%Y/%m/%d', blank=True, null=True) company = models.CharField(max_length=200, blank=True, null=True) website = models.URLField(blank=True, null=True) company_profile = models.TextField(blank=True, null=True) company_bio = models.TextField(blank=True, null=True) vat_number = models.CharField(max_length=50, blank=True, null=True) company_reg_no = models.CharField(max_length=50, blank=True, null=True) reference_number = models.CharField(max_length=50, blank=True, null=True) company_details = models.CharField(max_length=50, blank=True, null=True) # Payment Information payment_method = models.CharField( max_length=30, choices=PAYMENT_METHOD_CHOICES, help_text=','.join(['%s - %s' % (item[0], item[1]) for item in PAYMENT_METHOD_CHOICES]), blank=True, null=True ) btc_wallet = models.ForeignKey(BTCWallet, blank=True, null=True, on_delete=models.SET_NULL) btc_address = models.CharField(max_length=40, blank=True, null=True, validators=[validate_btc_address]) mobile_money_cc = models.CharField( max_length=5, choices=MOBILE_MONEY_CC_CHOICES, help_text=','.join(['%s - %s' % (item[0], item[1]) for item in MOBILE_MONEY_CC_CHOICES]), blank=True, null=True) mobile_money_number = models.CharField(max_length=15, blank=True, null=True) # Tax Information tax_name = models.CharField(max_length=200, blank=True, null=True) tax_percentage = models.FloatField(blank=True, null=True) def __str__(self): return self.user.get_short_name() @property def city_name(self): return self.city and str(self.city) or "" @property def country_name(self): return str(self.country.name) @property def location(self): location = self.city if self.country_name: location = '{}{}{}'.format(location, location and ', ' or '', self.country.name) return location or '' @allow_staff_or_superuser def has_object_read_permission(self, request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user def get_category_skills(self, skill_type): return self.skills.filter(type=skill_type) @property def skills_details(self): return dict( language=self.get_category_skills(SKILL_TYPE_LANGUAGE), framework=self.get_category_skills(SKILL_TYPE_FRAMEWORK), platform=self.get_category_skills(SKILL_TYPE_PLATFORM), library=self.get_category_skills(SKILL_TYPE_LIBRARY), storage=self.get_category_skills(SKILL_TYPE_STORAGE), api=self.get_category_skills(SKILL_TYPE_API), other=self.get_category_skills(SKILL_TYPE_OTHER), )
class Profile(AbstractUser): address = models.CharField(max_length=255, blank=True, null=True) address2 = models.CharField(max_length=255, blank=True, null=True) city = models.CharField(max_length=255, blank=True, null=True) state = models.CharField(max_length=255, blank=True, null=True) country = models.CharField(max_length=100, blank=True, null=True) country_code = models.CharField(max_length=100, blank=True, null=True) zipcode = models.IntegerField(blank=True, null=True) location = models.CharField(max_length=100, blank=True, null=True) capacity = models.IntegerField(blank=True, null=True) notes = models.TextField(blank=True, null=True) photo = models.ImageField(blank=True, null=True, upload_to=path_and_rename) skills = tagulous.models.TagField(to=Skills, blank=True) signup_code = models.CharField(max_length=25, blank=True, null=True) title = models.CharField(max_length=100, blank=True, null=True) roles = models.ManyToManyField(Role, blank=True) stripe = models.CharField(max_length=255, blank=True, null=True) stripe_connect = models.CharField(max_length=255, blank=True, null=True) verification = models.CharField(max_length=255, default='unverified') payouts_enabled = models.BooleanField(default=False) biography = models.TextField(blank=True, null=True) long_description = models.TextField(blank=True, null=True) objects = CustomUserManager() email_notifications = models.BooleanField(default=True) email_confirmed = models.BooleanField(default=False) featured = models.BooleanField(default=False) tos = models.BooleanField(default=False) work_examples = GenericRelation('generics.Attachment') score = models.DecimalField(blank=True, null=True, max_digits=5, decimal_places=2) referral_code = models.CharField(max_length=50, blank=True, null=True) full_time = models.BooleanField(default=True) contract_to_hire = models.BooleanField(default=True) freelance = models.BooleanField(default=True) @property def name(self): return '{0} {1}'.format(self.first_name, self.last_name) @property def get_photo(self): if self.photo: return '{0}{1}'.format(settings.MEDIA_URL, self.photo) else: try: return self.social_auth.get( provider='linkedin-oauth2' ).extra_data['picture_urls']['values'][0] except: return '' @property def linkedin(self): try: return self.social_auth.get(provider='linkedin-oauth2') except UserSocialAuth.DoesNotExist: return None @property def company(self): """ TODO Needs to support multiple primary companies """ try: return Employee.objects.get(profile=self, primary=True).company except Employee.DoesNotExist: return None @property def contact_details(self): details, created = ContactDetails.objects.get_or_create(profile=self) if (created): details.email = self.email details.email_confirmed = self.email_confirmed details.save() return details def get_skills(self): return self.skills.all() @property def skilltests(self): return SkillTest.objects.filter(profile=self) @property def taken_tests(self): return [t.expertratings_test for t in self.skilltests] def get_default_payment(self): if self.stripe: stripe.api_key = settings.STRIPE_KEY stripe_customer = stripe.Customer.retrieve(self.stripe) for card in stripe_customer['sources']['data']: if card['id'] == stripe_customer['default_source']: # TODO Manually serialize card, circular import error if using api serializer return card return None @property def subscribed(self): active_projects = Project.objects.filter(project_manager=self, status='active') if len(active_projects) > 0: return True return False def invite(self, sender): invite, created = Invite.objects.get_or_create(recipient=self, sender=sender) if created: return True return False
class Thesis(models.Model): """ This class defines the field that a thesis can have. The following fields are optional: additional, pdf A thesis can be promoted for either BSc, MSc, BEd, MEd, or as being part of a project. Each thesis is referenced with the chair that is providing it via a ForeignKey field. """ THESIS_CHOICES = ( ('BSC', 'Bachelor of Science'), ('MSC', 'Master of Science'), ('BED', 'Bachelor of Education'), ('MED', 'Master of Education'), ('PRO', 'Forschungsprojekt'), ('ETC', 'nach Absprache') ) title = models.CharField('Titel der Arbeit', blank=False, max_length=200) description = models.TextField('Beschreibung', blank=False) date_added = models.DateTimeField('Erstellungsdatum', default=timezone.now, editable=False) additional = models.TextField('weitere Beschreibung', blank=True, max_length=1000) contact = models.EmailField('E-Mail der Kontaktperson:', blank=False) chair = models.ForeignKey(Chair, on_delete=models.CASCADE, related_name="provided_by", verbose_name='angeboten durch Lehrstuhl') start_date = models.DateField('frühester Beginn', blank=False, default=timezone.now) is_active = models.BooleanField('aktiv', default=True) pdf = models.FileField('PDF mit Ausschreibung', validators=[FileExtensionValidator(allowed_extensions=['pdf'])], blank=True) type = MultiSelectField('Art der Arbeit', choices=THESIS_CHOICES, blank=False) tags = tagulous.models.TagField(get_absolute_url=lambda tag: reverse( 'by_tag', args=parse_tags(tag.slug))) user = models.ForeignKey(AAIUser, null=True, on_delete=models.DO_NOTHING, related_name="uploaded_by", verbose_name="hochgeladen von") def __str__(self): return self.title def get_absolute_url(self): return "/%i/" % self.id class Meta: verbose_name = "Abschlussarbeit" verbose_name_plural = "Abschlussarbeiten"
class UserProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # Personal Info bio = models.TextField(blank=True, null=True) # Contact Info country = CountryField(blank=True, null=True) city = tagulous.models.SingleTagField(to=City, blank=True, null=True) street = models.CharField(max_length=100, blank=True, null=True) plot_number = models.CharField(max_length=100, blank=True, null=True) postal_code = models.CharField(max_length=20, blank=True, null=True) postal_address = models.CharField(max_length=100, blank=True, null=True) phone_number = models.CharField(max_length=15, blank=True, null=True) # Professional Info skills = tagulous.models.TagField(to=Skill, blank=True) # KYC id_document = models.ImageField(upload_to='ids/%Y/%m/%d', blank=True, null=True) company = models.CharField(max_length=200, blank=True, null=True) website = models.URLField(blank=True, null=True) company_profile = models.TextField(blank=True, null=True) company_bio = models.TextField(blank=True, null=True) vat_number = models.CharField(max_length=50, blank=True, null=True) company_reg_no = models.CharField(max_length=50, blank=True, null=True) reference_number = models.CharField(max_length=50, blank=True, null=True) company_details = models.CharField(max_length=50, blank=True, null=True) # Payment Information payment_method = models.CharField( max_length=30, choices=PAYMENT_METHOD_CHOICES, help_text=','.join(['%s - %s' % (item[0], item[1]) for item in PAYMENT_METHOD_CHOICES]), blank=True, null=True ) btc_wallet = models.ForeignKey(BTCWallet, blank=True, null=True, on_delete=models.SET_NULL) btc_address = models.CharField(max_length=40, blank=True, null=True, validators=[validate_btc_address]) mobile_money_cc = models.CharField( max_length=5, choices=MOBILE_MONEY_CC_CHOICES, help_text=','.join(['%s - %s' % (item[0], item[1]) for item in MOBILE_MONEY_CC_CHOICES]), blank=True, null=True) mobile_money_number = models.CharField(max_length=15, blank=True, null=True) # Tax Information tax_name = models.CharField(max_length=200, blank=True, null=True) tax_percentage = models.FloatField(blank=True, null=True) def __str__(self): return self.user.get_short_name() @property def city_name(self): return self.city and str(self.city) or "" @property def country_name(self): return str(self.country.name) @property def location(self): location = self.city if self.country_name: location = '{}{}{}'.format(location, location and ', ' or '', self.country.name) return location or '' @allow_staff_or_superuser def has_object_read_permission(self, request): return True @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user def get_category_skills(self, skill_type): return self.skills.filter(type=skill_type) @property def skills_details(self): return dict( language=self.get_category_skills(SKILL_TYPE_LANGUAGE), framework=self.get_category_skills(SKILL_TYPE_FRAMEWORK), platform=self.get_category_skills(SKILL_TYPE_PLATFORM), library=self.get_category_skills(SKILL_TYPE_LIBRARY), storage=self.get_category_skills(SKILL_TYPE_STORAGE), api=self.get_category_skills(SKILL_TYPE_API), other=self.get_category_skills(SKILL_TYPE_OTHER), ) @property def tunga_badge(self): badge = TUNGA_DEVELOPER_BADGE user_projects = self.user.projects total_projects = len(user_projects) user_dedicated_months = self.get_months_of_participation() if total_projects in range(1, 4) or user_dedicated_months in range(1, 7): badge = TUNGA_TALENT_BADGE if total_projects in range(4, 9) or user_dedicated_months in range(7, 19): badge = TUNGA_VETERAN_BADGE if total_projects > 8 or user_dedicated_months > 18: badge = TUNGA_GURU_BADGE return badge def get_months_of_participation(self): total_months = 0 user_participations = self.user.project_participation.filter(status=STATUS_ACCEPTED) for participation in user_participations: start_date = participation.created_at end_or_current_date = participation.project.closed_at or datetime.datetime.now() period = relativedelta.relativedelta(end_or_current_date, start_date) total_months += period.months return total_months
class Project(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='projects_created', on_delete=models.DO_NOTHING) title = models.CharField(max_length=200) description = models.TextField() owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='projects_owned', on_delete=models.DO_NOTHING, blank=True, null=True) pm = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='projects_managed', on_delete=models.DO_NOTHING, blank=True, null=True) skills = tagulous.models.TagField(Skill, blank=True) budget = models.DecimalField(max_digits=17, decimal_places=2, blank=True, null=True, default=None) currency = models.CharField(max_length=5, choices=CURRENCY_CHOICES_EUR_ONLY, default=CURRENCY_EUR) type = models.ManyToManyField(ProjectType, blank=True) expected_duration = models.CharField( max_length=20, choices=PROJECT_EXPECTED_DURATION_CHOICES, blank=True, null=True) stage = models.CharField(max_length=20, choices=PROJECT_STAGE_CHOICES, default=PROJECT_STAGE_ACTIVE) # State identifiers client_survey_enabled = models.BooleanField(default=True) pm_updates_enabled = models.BooleanField(default=True) closed = models.BooleanField(default=False) archived = models.BooleanField(default=False) # Significant event dates start_date = models.DateTimeField(blank=True, null=True) deadline = models.DateTimeField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) closed_at = models.DateTimeField(blank=True, null=True) archived_at = models.DateTimeField(blank=True, null=True) participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Participation', through_fields=('project', 'user'), related_name='project_participants', blank=True) legacy_id = models.PositiveIntegerField(blank=True, null=True) migrated_at = models.DateTimeField(blank=True, null=True) hubspot_deal_id = models.CharField(editable=False, null=True, max_length=12) activity_objects = GenericRelation( Action, object_id_field='target_object_id', content_type_field='target_content_type', related_query_name='projects') def __str__(self): return self.title class Meta: ordering = ['-created_at'] @allow_staff_or_superuser def is_participant(self, user, active=True): if user == self.user or user == self.owner: return True elif user.is_project_manager and self.pm == user: return True elif user.is_developer and self.participation_set.filter( user=user, status__in=active and [STATUS_ACCEPTED] or [STATUS_ACCEPTED, STATUS_INITIAL]).count() > 0: return True else: return False @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return True @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.is_project_owner or request.user.is_project_manager @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user or request.user == self.owner or request.user == self.pm @property def margin(self): from tunga_payments.models import Invoice sales_amount = Invoice.objects.filter( project=self, type=INVOICE_TYPE_SALE).aggregate(Sum('amount')) project_amount = Invoice.objects.filter( project=self, type=INVOICE_TYPE_PURCHASE).aggregate(Sum('amount')) sales_amount = sales_amount['amount__sum'] or 0 project_amount = project_amount['amount__sum'] or 0 return sales_amount - project_amount
class ProgressReport(models.Model): event = models.ForeignKey(ProgressEvent, on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING) # Status details status = models.CharField(max_length=50, choices=PROGRESS_REPORT_STATUS_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in PROGRESS_REPORT_STATUS_CHOICES ]), blank=True, null=True) percentage = models.PositiveIntegerField( validators=[MinValueValidator(0), MaxValueValidator(100)], blank=True, null=True) accomplished = models.TextField(blank=True, null=True) todo = models.TextField(blank=True, null=True) obstacles = models.TextField(blank=True, null=True) obstacles_prevention = models.TextField(blank=True, null=True) remarks = models.TextField(blank=True, null=True) stuck_reason = models.CharField( max_length=50, choices=PROGRESS_REPORT_STUCK_REASON_CHOICES, help_text=','.join([ '%s - %s' % (item[0], item[1]) for item in PROGRESS_REPORT_STUCK_REASON_CHOICES ]), blank=True, null=True) stuck_details = models.TextField(blank=True, null=True) # Deliverables rate_deliverables = models.PositiveIntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)], blank=True, null=True) # Deadline Info started_at = models.DateTimeField(blank=True, null=True) last_deadline_met = models.NullBooleanField(blank=True, null=True) deadline_miss_communicated = models.NullBooleanField(blank=True, null=True) deadline_report = models.TextField(blank=True, null=True) next_deadline = models.DateTimeField(blank=True, null=True) next_deadline_meet = models.NullBooleanField(blank=True, null=True) next_deadline_fail_reason = models.TextField(blank=True, null=True) # PMs only team_appraisal = models.TextField(blank=True, null=True) # Clients only deliverable_satisfaction = models.NullBooleanField(blank=True, null=True) rate_communication = models.PositiveIntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)], blank=True, null=True) pm_communication = models.NullBooleanField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) legacy_id = models.PositiveIntegerField(blank=True, null=True) migrated_at = models.DateTimeField(blank=True, null=True) def __str__(self): return '{0} - {1}%'.format(self.event, self.percentage) class Meta: ordering = ['-created_at'] @staticmethod @allow_staff_or_superuser def has_read_permission(request): return True @allow_staff_or_superuser def has_object_read_permission(self, request): return self.event.project.is_participant(request) @staticmethod @allow_staff_or_superuser def has_write_permission(request): return request.user.is_developer or request.user.is_project_manager or request.user.is_project_owner @allow_staff_or_superuser def has_object_write_permission(self, request): return request.user == self.user
class Company(models.Model): name = models.CharField(max_length=255, unique=True) slug = models.SlugField() legal_entity_name = models.CharField(max_length=255, blank=True, null=True) phone = models.CharField(max_length=255, blank=True, null=True) email = models.CharField(max_length=255, blank=True, null=True) stripe = models.CharField(max_length=255, blank=True, null=True) address = models.CharField(max_length=255, blank=True, null=True) address2 = models.CharField(max_length=255, blank=True, null=True) city = models.CharField(max_length=255, blank=True, null=True) state = models.CharField(max_length=255, blank=True, null=True) zipcode = models.CharField(max_length=15, blank=True, null=True) country = models.CharField(max_length=255, blank=True, null=True) ein = models.CharField(max_length=50, verbose_name='EIN', blank=True, null=True) logo = models.ImageField(blank=True, null=True, upload_to='provider/logo') description = models.TextField(blank=True, null=True) long_description = models.TextField(blank=True, null=True) type = models.CharField(max_length=100, choices=COMPANY_TYPES, blank=True, null=True) filing_location = models.CharField(max_length=100, blank=True, null=True) incorporation_date = models.DateField(blank=True, null=True) @property def get_logo(self): if self.logo: return '{0}{1}'.format(settings.MEDIA_URL, self.logo) else: return '{0}{1}'.format(settings.STATIC_URL, 'images/home-hero-2g.jpg') @property def primary_contact(self): return Employee.objects.get(company=self, primary=True) @property def employees(self): return Employee.objects.filter(company=self) @property def tags(self): projects = Project.objects.filter(company=self) tags = [] for project in projects: # TODO List comprehension here bro if project.type not in tags: tags.append(project.type) return tags def save(self, *args, **kwargs): self.slug = slugify(self.name) super(Company, self).save(*args, **kwargs) def __str__(self): return self.name def __unicode__(self): return self.name class Meta: verbose_name_plural = 'companies'