def test_invalid_alphabets(self): with self.assertRaises(exceptions.ImproperlyConfigured): HashidField(alphabet="") # blank with self.assertRaises(exceptions.ImproperlyConfigured): HashidField(alphabet="abcdef") # too short with self.assertRaises(exceptions.ImproperlyConfigured): HashidField(alphabet="abcdefghijklmno") # too short by one with self.assertRaises(exceptions.ImproperlyConfigured): HashidField(alphabet="aaaaaaaaaaaaaaaaaaaaa") # not unique with self.assertRaises(exceptions.ImproperlyConfigured): HashidField(alphabet="aabcdefghijklmno") # not unique by one
class Record(models.Model): name = models.CharField(max_length=40) artist = models.ForeignKey(Artist, null=True, blank=True) reference_id = HashidField() alternate_id = HashidField(salt="a different salt", null=True, blank=True) key = HashidField(min_length=10, alphabet="abcdlmnotuvwxyz123789", null=True, blank=True) def __str__(self): return "{} ({})".format(self.name, self.reference_id)
class Book(models.Model): name = models.CharField(max_length=40) author = models.ForeignKey(Author, on_delete=models.CASCADE, null=True, blank=True, related_name='books') reference_id = HashidField(salt="alternative salt", allow_int_lookup=True) key = HashidField(min_length=10, alphabet="abcdlmnotuvwxyz0123789", null=True, blank=True) some_number = models.IntegerField(null=True, blank=True) editors = models.ManyToManyField(Editor, blank=True) def get_absolute_url(self): return reverse("library:book-detail", kwargs={'pk': self.pk}) def __str__(self): return "{} ({})".format(self.name, self.reference_id)
class Book(models.Model): name = models.CharField(max_length=40) author = models.ForeignKey(Author, null=True, blank=True, related_name='books') reference_id = HashidField(salt="alternative salt") key = HashidField(min_length=10, alphabet="abcdlmnotuvwxyz0123789", null=True, blank=True) some_number = models.IntegerField(null=True, blank=True) editors = models.ManyToManyField(Editor, blank=True) def __str__(self): return "{} ({})".format(self.name, self.reference_id)
class CandidateBookTimeSlot(models.Model): candidate = models.ForeignKey(Candidate, related_name='candidate', on_delete=models.CASCADE) exam = models.ForeignKey(Exam, on_delete=models.CASCADE, related_name='exam_book_slot', default=None, blank=True, null=True) time_slot = models.ForeignKey(ExamTimeSlot, on_delete=models.CASCADE, related_name='time_slot', default=None, blank=True, null=True) exam_room = models.ForeignKey(ExamRoom, on_delete=models.CASCADE, related_name='exam_room', default=None, blank=True, null=True) hall_ticket_number = HashidField(salt=settings.HASHID_FIELD_SALT + "Hall ticket", allow_int_lookup=True, editable=False, alphabet="0123456789ABCDEF", null=True) hall_ticket_downloaded = models.BooleanField(default=False) def save(self, *args, **kwargs): super(CandidateBookTimeSlot, self).save(*args, **kwargs) if not self.hall_ticket_number: self.hall_ticket_number = self.pk super(CandidateBookTimeSlot, self).save(*args, **kwargs)
class Speaker(models.Model): email = models.EmailField(unique=True, max_length=200, db_index=True) name = models.CharField(max_length=100, db_index=True) location = models.CharField(blank=True, null=True, max_length=100, db_index=True) facebook = models.CharField(blank=True, null=True, max_length=50) twitter = models.CharField(blank=True, null=True, max_length=50) github = models.CharField(blank=True, null=True, max_length=50) linkedin = models.CharField(blank=True, null=True, max_length=50) behance = models.CharField(blank=True, null=True, max_length=50) medium = models.CharField(blank=True, null=True, max_length=50) image_url = models.URLField(blank=True, null=True, max_length=300) site = models.URLField(blank=True, null=True, max_length=300) published = models.BooleanField(default=False, db_index=True) updated_at = models.DateTimeField(auto_now=True) interests = models.ManyToManyField("Interest") user = models.OneToOneField("User", blank=True, null=True) confirmation_key = HashidField(blank=True, null=True) active = models.BooleanField(default=False) def __str__(self): return "{}, {}".format(self.name, self.email) @property def photo(self): return self.image_url or get_gravatar_url(self.email, size=80) @property def facebook_url(self): return ("{}{}".format(settings.FACEBOOK_URL, self.facebook) if self.facebook else None) @property def twitter_url(self): return ("{}{}".format(settings.TWITTER_URL, self.twitter) if self.twitter else None) @property def linkedin_url(self): return ("{}{}".format(settings.LINKEDIN_URL, self.linkedin) if self.linkedin else None) @property def github_url(self): return "{}{}".format(settings.GITHUB_URL, self.github) if self.github else None @property def behance_url(self): return ("{}{}".format(settings.BEHANCE_URL, self.behance) if self.behance else None) @property def medium_url(self): return "{}{}".format(settings.MEDIUM_URL, self.medium) if self.medium else None
class ApiToken(TimestampMixin): token = HashidField(min_length=32, alphabet='0123456789abcdef', default=timestamp_seconds, unique=True, editable=True) owner = models.OneToOneField(User, related_name="token") def __str__(self): return self.token.hashid
def test_custom_hashids_settings(self): SALT = "abcd" ALPHABET = "abcdefghijklmnop" MIN_LENGTH = 10 field = HashidField(salt=SALT, alphabet=ALPHABET, min_length=MIN_LENGTH) hashids = field._hashids self.assertEqual(hashids._salt, SALT) self.assertEqual(hashids._min_length, MIN_LENGTH) self.assertEqual( "".join( sorted(hashids._alphabet + hashids._guards + hashids._separators)), ALPHABET) # Make sure all characters in 100 hashids are in the ALPHABET and are at least MIN_LENGTH for i in range(1, 100): hashid = str(field.to_python(i)) self.assertGreaterEqual(len(hashid), MIN_LENGTH) for c in hashid: self.assertIn(c, ALPHABET)
class Serving(TimestampMixin): """Model representing already served menu.""" public_id = HashidField(alphabet='0123456789abcdefghijklmnopqrstuvwxyz', default=timestamp_seconds, unique=True) menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE) vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE) date_served = models.DateField() def __str__(self): return '{} served on {}'.format(self.menu_item, self.date_served) class Meta: unique_together = ('menu_item', 'vendor', 'date_served')
def test_encode_with_prefix(self): SALT = "abcd" ALPHABET = "abcdefghijklmnop" field_without_prefix = HashidField(min_length=5) field_with_prefix = HashidField(min_length=5, prefix=1) hashed_id_without_prefix = field_without_prefix.encode_id(1) hashed_id_with_prefix = field_with_prefix.encode_id(1) self.assertNotEqual(hashed_id_without_prefix, hashed_id_with_prefix)
class TakeExam(models.Model): take_exam_id = HashidField(salt=settings.HASHID_FIELD_SALT + "Exam", allow_int_lookup=True, null=True, editable=False, alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", default=None) student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='exam_student', null=True) exam = models.ForeignKey('instructor.Exam', on_delete=models.CASCADE, related_name='exam_taken') current_question = models.PositiveSmallIntegerField(default=0) answers_given = models.ManyToManyField('instructor.Answer') completed = models.BooleanField(default=False) completion_time = models.DateTimeField(default=None, null=True) def save(self, *args, **kwargs): super(TakeExam, self).save(*args, **kwargs) if not self.take_exam_id: self.take_exam_id = self.pk super(TakeExam, self).save(*args, **kwargs) def score(self): take_exam = self exam = self.exam answers_given = self.answers_given.all() question_queryset = exam.questions.all() score = 0 for question in question_queryset: count_answer = 0 answer_queryset = question.answers() for answer in answer_queryset: answer_key = 0 if answer.correct: answer_key = answer_key + 1 if answer in answers_given: score = score + (4.0 / 3.0) * answer_key - (1.0 / 3.0) answer_key = answer_key + 2 count_answer = count_answer + 1 return score
class ApiKey(TimestampMixin): """Model representing clients' api keys.""" token = HashidField(min_length=32, alphabet='0123456789abcdef', default=timestamp_seconds, unique=True, editable=False) revoked = models.BooleanField(default=False) owner = models.ForeignKey(User, on_delete=models.CASCADE) def clean(self): if not self.owner.is_superuser: raise ValidationError(_('Ensure the owner is a superuser.')) super().clean() def save(self, *args, **kwargs): self.full_clean() return super().save(*args, **kwargs) def __str__(self): return self.token.hashid
class CtfChallenge(models.Model): REQUIRED_FIELDS = ['oyu_user'] # Relationships author = models.ForeignKey(OyuUser, verbose_name='Нэмсэн', on_delete=models.DO_NOTHING, null=True) # General title = models.CharField("Гарчиг", max_length=30, unique=True) description = MartorField() value = models.PositiveIntegerField("Бодлогын оноо", default=500, null=True) category = models.CharField("Төрөл", max_length=100, choices=CTF_CHALLENGE_CATEGORY_CHOICES, null=True) state = models.CharField("Төлөв", max_length=100, null=True, default='active') flag = models.CharField("Flag", max_length=100, null=False) # Additional solved_users_count = models.PositiveIntegerField( "Бодсон хэрэглэгчдийн тоо", null=True, default=0) # Security reference_id = HashidField(allow_int_lookup=True, null=True, salt='CHANGEMEINFUTURE', min_length=15) def __str__(self): return "%s | %s" % (self.title, self.category)
class Record(models.Model): id = HashidAutoField(primary_key=True) name = models.CharField(max_length=40) artist = models.ForeignKey(Artist, on_delete=models.CASCADE, null=True, blank=True, related_name="records") reference_id = HashidField() prefixed_id = HashidField(null=True, blank=True, prefix="prefix_") string_id = HashidField(null=True, blank=True, enable_hashid_object=False) plain_hashid = HashidField(null=True, blank=True, enable_descriptor=False) plain_id = HashidField(null=True, blank=True, enable_descriptor=False, enable_hashid_object=False) alternate_id = HashidField(salt="a different salt", null=True, blank=True) key = BigHashidField(min_length=10, alphabet="abcdlmnotuvwxyz123789", null=True, blank=True) def __str__(self): return "{} ({})".format(self.name, self.reference_id)
class Candidate(models.Model): GENDER_CHOICES = ( ('Male', 'Male'), ('Female', 'Female'), ) MARITAL_STATUS_CHOICES = ( ('Married', 'Married'), ('Single', 'Single'), ) DISTRICT_CHOICES = ( ('District1', 'District1'), ('District2', 'District2'), ) STATE_NURSING_COUNCIL_CHOICES = ( ('SNC1', 'SNC1'), ('SNC2', 'SNC2'), ('SNC3', 'SNC3'), ) PREFERENCE_OF_WORK_CHOICES = ( ('India', 'India'), ('Foreign', 'Foreign'), ('Both', 'Both'), ) YEAR_CHOICES = [] for r in range(1970, (timezone.datetime.now().year + 20)): YEAR_CHOICES.append((r, r)) def media_path(instance, filename): return 'candidate/{0}/personal/{1}'.format( instance.candidate_username.username, filename) # Tab 1: Personal details candidate_username = models.ForeignKey(User, on_delete=models.CASCADE, related_name='candidate_username', default=None, blank=True, null=True) photograph = models.FileField( max_length=500, default=None, blank=True, null=True, upload_to=media_path, validators=[ValidateFileExtension.validate_image]) curriculum_vitae = models.FileField( max_length=500, default=None, blank=True, null=True, upload_to=media_path, validators=[ValidateFileExtension.validate_file_doc_or_pdf]) name = models.CharField(max_length=200, default="Name") fathers_name = models.CharField(max_length=200, default="Father's Name") date_of_birth = models.DateField(default=timezone.now, blank=False, null=False) gender = models.CharField(max_length=200, choices=GENDER_CHOICES, default="Male") marital_status = models.CharField(max_length=200, choices=MARITAL_STATUS_CHOICES, default="Single") # email = models.CharField(max_length=200, default="*****@*****.**") phone_number = models.CharField(max_length=200, default="9810117638") house_number = models.CharField(max_length=200, default="Address Line 1") area_locality = models.CharField(max_length=200, default="Locality") street_name = models.CharField(max_length=200, default="Street Nae") village_PS_PO = models.CharField(max_length=200, default="PS/PO/Village") country = models.CharField(max_length=200, default="India") state = models.CharField(max_length=200, default="Delhi") city = models.CharField(max_length=200, default="Delhi") district = models.CharField(max_length=200, default="Delhi") pin_code = models.CharField(max_length=200, default="110078") # Tab 2: SNC details # degree_recognized_by_INC = models.BooleanField(default=False) # state_nursing_council_name = models.CharField(max_length=200, choices=STATE_NURSING_COUNCIL_CHOICES, default="SNC1", blank=True) # state_nursing_council_registration_number = models.CharField(max_length=200, default="", blank=True) # state_nursing_council_registration_date = models.DateField(default=timezone.now, blank=True) # state_nursing_council_proof = models.FileField(max_length = 500, default=None, blank=True, null=True) # Tab 3: Educational qualifications # See model below # Tab 4: Eligibility tests # See model below # Tab 5: Passport details and misc. passport_number = models.CharField(max_length=50, blank=True) passport_valid_from = models.DateField(blank=True, null=True, default=None) passport_valid_to = models.DateField(blank=True, null=True, default=None) # passport_valid_from = models.PositiveSmallIntegerField(choices=YEAR_CHOICES, blank=True, null=True) # passport_valid_to = models.PositiveSmallIntegerField(choices=YEAR_CHOICES, blank=True, null=True) passport_place_of_issue = models.CharField(max_length=200, blank=True) # Tab 6: Miscellaneous details TNAI_number = models.CharField(max_length=200, default="", blank=True) preference_of_work = models.CharField(max_length=10, choices=PREFERENCE_OF_WORK_CHOICES, blank=True) registration_number = HashidField( allow_int_lookup=True, null=True, editable=False, alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", default=None) is_provisional_registration_number = models.BooleanField(default=True, editable=False) sent_email_notification_provisional_registration_number = models.BooleanField( default=False, editable=False) is_active = models.BooleanField(default=True, editable=False) def __str__(self): return "Name: " + self.name + ", Username: "******"TNAI/REC/PROV/" + str( self.registration_number) else: displayed_registration_number = "TNAI/REC/PERM/" + str( self.registration_number) return displayed_registration_number def save(self, *args, **kwargs): super(Candidate, self).save(*args, **kwargs) if not self.registration_number: self.registration_number = self.pk super(Candidate, self).save(*args, **kwargs) def is_candidate_verified(self): return not self.is_provisional_registration_number
class Employer(models.Model): COUNTRY_CHOICES = ( ('Indian', 'Indian'), ('Foreign', 'Foreign'), ) COMPANY_CHOICES = ( ('Government', 'Government'), ('Private', 'Private'), ) SECTOR_CHOICES = ( ('Health', 'Health'), ('Construction', 'Construction'), ('IT', 'IT'), ('Shipping', 'Shipping'), ) employer_username = models.ForeignKey(User, on_delete=models.CASCADE, related_name='employer_username', default=None, blank=True, null=True) country = models.CharField(max_length=200, choices=COUNTRY_CHOICES, default="Indian") name = models.CharField(max_length=200, default="Employer Name") registration = models.CharField(max_length=200, default="ABC-1234") type = models.CharField(max_length=200, choices=COMPANY_CHOICES, default="Private") authorized_signatory = models.CharField(max_length=200, default="Auth 123") sector = models.CharField(max_length=200, choices=SECTOR_CHOICES, default="Health") address = models.CharField(max_length=500, default="abcdef") website = models.URLField(max_length=200, default="http://example.com") phone = models.CharField(max_length=200, default="9810117638") # registration_certification = models.CharField(max_length=200, default="") registration_certification = models.FileField( max_length=500, default="", validators=[ValidateFileExtension.validate_file], blank=True, null=True) authorized_signatory_id_proof = models.FileField( max_length=500, default=None, validators=[ValidateFileExtension.validate_file], blank=True, null=True) total_employees = models.IntegerField(default=0, blank=True, null=True) annual_recruitment_of_indians = models.IntegerField(default=0, blank=True, null=True) nurses_degree = models.IntegerField(default=0, blank=True, null=True) nurses_diploma = models.IntegerField(default=0, blank=True, null=True) doctors = models.IntegerField(default=0, blank=True, null=True) lab_technicians = models.IntegerField(default=0, blank=True, null=True) pathologists = models.IntegerField(default=0, blank=True, null=True) registration_number = HashidField( salt=settings.HASHID_FIELD_SALT + "Employer", allow_int_lookup=True, null=True, editable=False, alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", default=None) is_provisional_registration_number = models.BooleanField(default=True, editable=False) sent_email_notification_provisional_registration_number = models.BooleanField( default=False, editable=False) def __str__(self): return self.name + " (Employer)" def registration_number_display(self): if self.is_provisional_registration_number: displayed_registration_number = "TNAI/REC/PROV/" + str( self.registration_number) else: displayed_registration_number = "TNAI/REC/PERM/" + str( self.registration_number) return displayed_registration_number def save(self, *args, **kwargs): # pdb.set_trace() super(Employer, self).save(*args, **kwargs) if not self.registration_number: self.registration_number = self.pk super(Employer, self).save(*args, **kwargs) def is_employer_verified(self): return not self.is_provisional_registration_number
class Post(InheritanceManagerMixin, CreatedUpdatedMixin, LikesMixin, MentionsMixin, models.Model): """A post created by a User. Features a title, some media, tags and mentions. """ STATUSES = ( ('draft', 'Draft'), ('processing', 'Processing'), ('published', 'Published'), ) VISIBILITIES = ( ('public', 'Public'), ('unlisted', 'Unlisted'), ('private', 'Private'), ) # This value is set by the publish() method upon publishing published_at = models.DateTimeField('date published', null=True, blank=True) hash_id = HashidField(null=True) user = models.ForeignKey(User, on_delete=models.CASCADE) community = models.ForeignKey(Community, on_delete=models.CASCADE, null=True, blank=True) # TODO(fsiddi) rename to description title = models.TextField(null=True) content = models.TextField(null=True) status = models.CharField(max_length=20, choices=STATUSES, default='draft') visibility = models.CharField(max_length=20, choices=VISIBILITIES, default='public') is_hidden_by_moderator = models.BooleanField(default=False) tags = TaggableManager() def get_absolute_url(self): return reverse('post_detail', kwargs={'hash_id': str(self.hash_id)}) @property def absolute_url(self) -> str: return 'http://%s%s' % (Site.objects.get_current().domain, self.get_absolute_url()) @property def comments_count(self) -> int: return Comment.objects.filter(post=self).count() @property def videos(self) -> typing.List['PostMediaVideo']: """Returns a list of PostMediaVideo objects.""" media_video_type = ContentType.objects.get(app_label='dillo', model='postmediavideo') media_videos = [] for video in self.postmedia_set.filter(content_type=media_video_type): media_videos.append(video.content_object) return media_videos @property def thumbnail(self): """An image preview for the Post, generated from the first media item. If the first media is an image, return the image. If the first media is a video, return the thumbnail. """ first_media = self.postmedia_set.first() if not first_media: log.error('No thumbnail available for post %i' % self.id) return None if first_media.content_type.name == 'post media image': return first_media.content_object.image elif first_media.content_type.name == 'post media video': return first_media.content_object.thumbnail else: log.error('No thumbnail available for post %i' % self.id) return None @property def may_i_publish(self): """Investigate the status of attached videos. If video.encoding_job_status is 'job.complete' for all videos, we set the post status to 'draft' and we are ready for publishing. """ if not self.videos: return True is_processing_videos = False for video in self.videos: log.debug('Checking processing status of encoded videos') if video.encoding_job_status != 'job.completed': is_processing_videos = True break log.debug('Found a processing video') if is_processing_videos: return False log.debug('All videos are encoded') self.status = 'draft' self.save() log.info('Post %i has status %s and is ready for publishing' % (self.id, self.status)) return True def process_videos(self) -> int: """Look at attached media, and if videos are present, start processing. Returns an int, used in the admin to show how many video are processed. """ videos_processing_count = 0 for video in self.videos: # Create encoding job for the video self.process_video(video) videos_processing_count += 1 return videos_processing_count def process_video(self, video: 'PostMediaVideo'): """Set Post as 'processing' and start video encoding.""" # Set status as processing, without triggering Post save signals Post.objects.filter(pk=self.id).update(status='processing') # Create a background job, using only hashable arguments create_job(str(self.hash_id), video.id, video.source.name) def update_video_processing_status(self, video_id: int, job: dict): """Update the processing status of a video. Called via the post_update_video_processing view, updates the the encoding_job_status and if the job status is 'job.completed' check if the Post itself is ready for publishing. If it is, kick off the publishing pipeline. """ video = PostMediaVideo.objects.get(pk=video_id) if video.encoding_job_id != job['id']: # If the job id changed, we likely restarted the job (manually) video.encoding_job_status = None video.encoding_job_id = job['id'] video.save() # If the current status of the video job is completed, we quit. # This prevents status updates sent after the completion of the job # from altering the video status. if video.encoding_job_status == 'job.completed': log.info('Encoding job was already completed') return # Update encoding job status job_status = job['event'] job_progress = None if 'progress' not in job else job['progress'] video.encoding_job_status = job_status video.encoding_job_progress = job_progress # Assign video thumbnail if 'format' in job and job['format'].startswith('jpg'): log.debug('Assigning thumbnail to video %i' % video_id) url = pathlib.Path(video.source.name) video.thumbnail = str(url.with_suffix('.thumbnail.jpg')) video.save() if job_status != 'job.completed': return if not self.may_i_publish: return log.debug('Video has been processed, attempting to publish') self.publish() def publish(self): """If the Post is in 'draft', kick-off the publishing pipeline.""" if self.status != 'draft': log.info('Attempting to publish a %s Post, no action taken' % self.status) return # Set the Post as 'published' self.status = 'published' self.published_at = timezone.now() self.save() log.info('Post %i has been published' % self.id) # Send signal to generate Tags activity post_published.send(sender=self.__class__, instance=self) def is_bookmarked(self, user: User): if user.is_anonymous: return False return user.profile.bookmarks.filter(pk=self.pk).exists() def __str__(self): return f'{self.title[:50]}' if self.title else str(self.hash_id) class Meta: ordering = ['-created_at']
class Advertisement(models.Model): DURATION_UNITS_CHOICES = ( ("Days", "Days"), ("Weeks", "Weeks"), ("Months", "Months"), ("Years", "Years"), ) MEDICAL_FACILITIES_CHOICES = ( ("Self", "Self"), ("Family", "Family"), ("None", "None"), ) GENDER_CHOICES = (("Any", "Any"), ("Male", "Male"), ("Female", "Female")) employer_advert = models.ForeignKey(Employer, on_delete=models.CASCADE, related_name='employer_advert', blank=True, null=True) dummy_employer_name = models.CharField(max_length=200, blank=True, default="") job_role = models.CharField(max_length=200, blank=True, default="Nurse") closing_date = models.DateField(blank=True, default=timezone.now) gender = models.CharField(max_length=200, choices=GENDER_CHOICES, default="Any", blank=True) number_of_vacancies = models.IntegerField(blank=True, default=5) educational_qualifications = models.CharField(max_length=500, blank=True, default="ANM required") eligibility_tests = models.CharField(max_length=500, blank=True, default="HAAD") experience = models.CharField(max_length=500, blank=True, default="2 years") name_of_hospital = models.CharField(max_length=200, blank=True, default="Hospital1") city = models.CharField(max_length=200, blank=True, default="Delhi") country = models.CharField(max_length=200, blank=True, default="India") start_date = models.DateField(blank=True, default=timezone.now) duration_of_assignment = models.IntegerField(blank=True, default=5) duration_of_assignment_units = models.CharField( max_length=15, blank=True, choices=DURATION_UNITS_CHOICES, default="Months") salary_number = models.IntegerField(blank=True, default=1200) salary_currency = models.CharField(max_length=20, blank=True, default="AED") allowances = models.CharField(max_length=500, blank=True, default="Travel") personal_accomodation = models.BooleanField(blank=True, default=False) family_accomodation = models.BooleanField(blank=True, default=False) accomodation_allowance = models.IntegerField(blank=True, default=0) accomodation_allowance_currency = models.CharField(max_length=20, blank=True, default="AED") maternity_leave_number_of_days = models.IntegerField(blank=True, default=0) medical_facilities = models.CharField(max_length=15, blank=True, choices=MEDICAL_FACILITIES_CHOICES, default="Self") visa_cost = models.BooleanField(blank=True, default=False) air_ticket_for_joining = models.BooleanField(blank=True, default=False) air_ticket_for_vacation = models.BooleanField(blank=True, default=False) annual_leave_days = models.IntegerField(blank=True, default=14) other_notes = models.CharField( max_length=500, blank=True, ) life_insurance = models.IntegerField(blank=True, default=0) life_insurance_currency = models.CharField(max_length=20, blank=True, default="AED") obfuscated_id = HashidField(salt=settings.HASHID_FIELD_SALT + "Advertisement", allow_int_lookup=True, editable=False, alphabet="0123456789ABCDEF", null=True) def save(self, *args, **kwargs): super(Advertisement, self).save(*args, **kwargs) if not self.obfuscated_id: self.obfuscated_id = self.pk super(Advertisement, self).save(*args, **kwargs) def __str__(self): return "Advertisement ID: " + str(self.obfuscated_id)
class Transaction(models.Model): user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, null=True) amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True) authority = models.CharField(max_length=100, blank=True, null=True) ref_id = models.IntegerField(null=True, blank=True) description = models.TextField() callback_url = models.CharField(max_length=100, null=True) first_name = models.CharField(max_length=225, blank=True, null=True) last_name = models.CharField(max_length=225, blank=True, null=True) email = models.CharField(max_length=225, blank=True, null=True) mobile = models.CharField(max_length=225, blank=True, null=True) order_number = HashidField(allow_int_lookup=True, blank=True, null=True, salt=getattr(settings, "HASHID_FIELD_SALT", None)) address = models.CharField(max_length=225, blank=True, null=True) country = models.CharField(max_length=225, blank=True, null=True) postal_code = models.CharField(max_length=225, blank=True, null=True) city = models.CharField(max_length=100, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) successful_payment_date_time = models.DateTimeField(blank=True, null=True) # status = models.CharField(max_length=100, choices=TRANSACTION_STATUS_CHOICES) status = models.CharField(max_length=100) failure_reason = models.CharField(max_length=100, blank=True, null=True) simulation = models.BooleanField(default=False) objects = TransactionManager() def __repr__(self): return "<zarinpal id:{0}>".format(self.order_number) def __str__(self): return "zarinpal: {0}".format(self.order_number) def success(self, ref_id): self.ref_id = ref_id self.status = "SUCCESS" self.successful_payment_date_time = timezone.now() self.save( update_fields=["status", "successful_payment_date_time", "ref_id"]) def fail(self, failure_reason=None): self.status = "FAILED" if failure_reason: self.failure_reason = failure_reason self.save(update_fields=["status", "failure_reason"]) def is_successful(self): return self.status == "SUCCESS" def get_transaction_start_url(self, request=None): if self.simulation is False: return ZARINPAL_START_GATEWAY + self.authority else: relative_start_url = reverse( "zarinpal:sandbox-payment", kwargs={"authority_start": self.authority}) if request: return request.build_absolute_uri(relative_start_url) else: return relative_start_url def get_client_callback_url(self): if self.callback_url: return self.callback_url + f"?orderNumber={self.order_number}" else: raise CallbackUrlNotProvided( f"Callback url is not set in transaction with order number {self.order_number.hashid}" )
class InternalUser(AbstractBaseUser, PermissionsMixin): class Meta: ordering = [ 'last_name', 'first_name', 'middle_name', 'email' ] GENDER_CHOICES = [ ('M', 'Male'), ('F', 'Female'), ('', 'Not Set'), ] email = models.EmailField('email', null=False, blank=False, max_length=64, unique=True, db_index=True) first_name = models.CharField('first_name', null=False, blank=False, max_length=50) last_name = models.CharField('last_name', null=False, blank=False, max_length=50) middle_name = models.CharField('middle_name', null=False, blank=True, max_length=50) bio = models.CharField('bio', null=False, blank=True, max_length=500, default="") phone = models.CharField('phone', null=False, blank=True, max_length=100, default="") is_confirmed = models.BooleanField('is_confirmed', default=False) dob = models.DateField('dob', null=False, blank=True, default="1970-01-01") gender = models.CharField('gender', max_length=1, choices=GENDER_CHOICES, null=False, blank=True, default='') timezone = TimeZoneField('timezone', default='Europe/Moscow') is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_admin = models.BooleanField(default=False) two_factor_auth = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True, blank=True, null=False) updated_at = models.DateTimeField(auto_now_add=True, blank=True, null=False) username = models.CharField(max_length=100, null=False, blank=True) EMAIL_FIELD = 'email' USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['first_name', 'last_name'] unique_user_token = HashidField(min_length=50, null=True) objects = UserModelManager() @property def full_name(self) -> str: """Constructs user's full name. """ name = f'{self.last_name} {self.first_name}' if self.middle_name: name += ' ' + self.middle_name return name def get_full_name(self) -> str: """Constructs user's full name. """ name = f'{self.last_name} {self.first_name}' if self.middle_name: name += ' ' + self.middle_name return name @property def short_name(self) -> str: """Constructs user's short name (without patronymic). """ return f'{self.last_name} {self.first_name}' def __str__(self): return "{}".format(self.email)
class Transaction(models.Model): user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, null=True) amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True) authority_start = models.CharField(max_length=100, blank=True, null=True) # by module authority_verify = models.CharField(max_length=100, blank=True, null=True) # by module description = models.TextField() callback_url = models.CharField(max_length=1000) from_currency_code = models.CharField(max_length=4, choices=CURRENCY_CHOICES, default="EUR") to_currency_code = models.CharField(max_length=4, choices=CURRENCY_CHOICES, default="EUR") first_name = models.CharField(max_length=225) last_name = models.CharField(max_length=225) email = models.CharField(max_length=225) mobile = models.CharField(max_length=225) order_number = HashidField(allow_int_lookup=True, blank=True, null=True) address = models.CharField(max_length=225) country = models.CharField(max_length=225) postal_code = models.CharField(max_length=225) city = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) # by module successful_payment_date_time = models.DateTimeField(blank=True, null=True) # by module status = models.CharField(max_length=100, choices=TRANSACTION_STATUS_CHOICES) # by module failure_reason = models.CharField(max_length=100, blank=True, null=True) # by module simulation = models.BooleanField(default=False) objects = TransactionManager() class Meta: ordering = ['-created_at'] def __repr__(self): return "<yekpay id:{0}>".format(self.order_number) def __str__(self): return "yekpay: {0}".format(self.order_number) def success(self): self.status = "SUCCESS" self.successful_payment_date_time = timezone.now() self.save(update_fields=["status", "successful_payment_date_time"]) def fail(self, failure_reason=None): self.status = "FAILED" if failure_reason: self.failure_reason = failure_reason self.save(update_fields=["status", "failure_reason"]) def is_successful(self): return self.status == "SUCCESS" def get_transaction_start_url(self, request=None): if self.simulation is False: return YEKPAY_START_GATEWAY + self.authority_start else: relative_start_url = reverse( "yekpay:sandbox-payment", kwargs={"authority_start": self.authority_start}, ) if request: return request.build_absolute_uri(relative_start_url) else: return relative_start_url def get_verify_url(self): return (reverse( "yekpay:verify_transaction", kwargs={"transaction_order_number": self.order_number.hashid}, ) + f"?authority={self.authority_verify}") def get_client_callback_url(self): if self.callback_url: return self.callback_url + f"?orderNumber={self.order_number}" else: raise CallbackUrlNotProvided( f"Callback url is not set in transaction with order number {self.order_number.hashid}" )