diff --git a/resume_manager_core/fixtures/resume_manager/resume_sections.json b/resume_manager_core/fixtures/resume_manager/resume_sections.json index f1357358a1fbcdac2d97a033aefec0fb641c5725..ef7871888284669705ee682da42b0c2f017cf23d 100644 --- a/resume_manager_core/fixtures/resume_manager/resume_sections.json +++ b/resume_manager_core/fixtures/resume_manager/resume_sections.json @@ -3,8 +3,10 @@ "model": "resume_manager_core.resumesection", "pk": 1, "fields": { - "name": "Page", - "css_classes": "resume-page full-size", + "resume": 1, + "parent_section": null, + "name": "Basic Page", + "css": [1], "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } @@ -13,8 +15,10 @@ "model": "resume_manager_core.resumesection", "pk": 2, "fields": { - "name": "Row: Full", - "css_classes": "resume-section-row full-size", + "resume": 1, + "parent_section": 1, + "name": "Header Section", + "css": [2, 4, 18, 19], "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } @@ -23,8 +27,10 @@ "model": "resume_manager_core.resumesection", "pk": 3, "fields": { - "name": "Row: Half", - "css_classes": "resume-section-row full-size", + "resume": 1, + "parent_section": 1, + "name": "Body Section", + "css": [3, 11, 19], "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } @@ -33,8 +39,10 @@ "model": "resume_manager_core.resumesection", "pk": 4, "fields": { - "name": "Row: One-Third", - "css_classes": "resume-section-row one-third-size", + "resume": 1, + "parent_section": 1, + "name": "Footer Section", + "css": [2, 4, 18, 19], "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } @@ -43,8 +51,11 @@ "model": "resume_manager_core.resumesection", "pk": 5, "fields": { - "name": "Row: Two-Thirds", - "css_classes": "resume-section-row two-thirds-size", + "resume": 1, + "parent_section": 2, + "name": "Header Left", + "css": [20], + "display_category": 1, "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } @@ -53,138 +64,11 @@ "model": "resume_manager_core.resumesection", "pk": 6, "fields": { - "name": "Row: One-Fourth", - "css_classes": "resume-section-row one-fourth-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 7, - "fields": { - "name": "Row: Three-Fourths", - "css_classes": "resume-section-row three-fourths-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 8, - "fields": { - "name": "Column: Full", - "css_classes": "resume-section-column full-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 9, - "fields": { - "name": "Column: Half", - "css_classes": "resume-section-column half-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 10, - "fields": { - "name": "Column: One-Third", - "css_classes": "resume-section-column one-third-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 11, - "fields": { - "name": "Column: Two-Thirds", - "css_classes": "resume-section-column two-thirds-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 12, - "fields": { - "name": "Column: One-Fourth", - "css_classes": "resume-section-column one-fourth-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 13, - "fields": { - "name": "Column: Three-Fourths", - "css_classes": "resume-section-column three-fourths-size", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 14, - "fields": { - "name": "Text: Left-Align", - "css_classes": "resume-section-text align-left", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 15, - "fields": { - "name": "Text: Center-Align", - "css_classes": "resume-section-text align-center", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 16, - "fields": { - "name": "Text: Right-Align", - "css_classes": "resume-section-text align-right", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 17, - "fields": { - "name": "Style: Full Color", - "css_classes": "resume-section-style full-color", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 18, - "fields": { - "name": "Row: Minimal", - "css_classes": "resume-section-row minimal", - "date_created": "2023-01-01T08:00:00.000Z", - "date_modified": "2023-01-01T08:00:00.000Z" - } -}, -{ - "model": "resume_manager_core.resumesection", - "pk": 19, - "fields": { - "name": "Column: Minimal", - "css_classes": "resume-section-column minimal", + "resume": 1, + "parent_section": 2, + "name": "Header Right", + "css": [22], + "display_category": 2, "date_created": "2023-01-01T08:00:00.000Z", "date_modified": "2023-01-01T08:00:00.000Z" } diff --git a/resume_manager_core/fixtures/resume_manager/resumes.json b/resume_manager_core/fixtures/resume_manager/resumes.json new file mode 100644 index 0000000000000000000000000000000000000000..c082936402ec8451a264e9188f2a644e88668ca4 --- /dev/null +++ b/resume_manager_core/fixtures/resume_manager/resumes.json @@ -0,0 +1,15 @@ +[ +{ + "model": "resume_manager_core.resume", + "pk": 1, + "fields": { + "name": "Example Blocky-Purple Resume", + "user": 1, + "theme": 1, + "profession": "Example <br> Profession", + "last_activity": "2023-01-01", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +} +] diff --git a/resume_manager_core/fixtures/resume_manager/section_css.json b/resume_manager_core/fixtures/resume_manager/section_css.json new file mode 100644 index 0000000000000000000000000000000000000000..ebac4180a6e08b50232471d9c67fcfd362be78cf --- /dev/null +++ b/resume_manager_core/fixtures/resume_manager/section_css.json @@ -0,0 +1,222 @@ +[ +{ + "model": "resume_manager_core.sectioncss", + "pk": 1, + "fields": { + "name": "page", + "css_classes": "page full-size", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 2, + "fields": { + "name": "flex-horizontal", + "css_classes": "flex flex-horizontal", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 3, + "fields": { + "name": "flex-vertical", + "css_classes": "flex flex-vertical", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 4, + "fields": { + "name": "flex-ratio 10%", + "css_classes": "flex flex-ratio-10", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 5, + "fields": { + "name": "flex-ratio 20%", + "css_classes": "flex flex-ratio-20", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 6, + "fields": { + "name": "flex-ratio 30%", + "css_classes": "flex flex-ratio-30", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 7, + "fields": { + "name": "flex-ratio 40%", + "css_classes": "flex flex-ratio-40", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 8, + "fields": { + "name": "flex-ratio 50", + "css_classes": "flex flex-ratio-50%", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 9, + "fields": { + "name": "flex-ratio 60%", + "css_classes": "flex flex-ratio-60", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 10, + "fields": { + "name": "flex-ratio 70%", + "css_classes": "flex flex-ratio-70", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 11, + "fields": { + "name": "flex-ratio 80%", + "css_classes": "flex flex-ratio-80", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 12, + "fields": { + "name": "flex-ratio 90%", + "css_classes": "flex flex-ratio-90", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 13, + "fields": { + "name": "flex-ratio 100%", + "css_classes": "flex flex-ratio-100", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 14, + "fields": { + "name": "flex-align-start", + "css_classes": "flex flex-align-start", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 15, + "fields": { + "name": "flex-align-space-between", + "css_classes": "flex flex-align-space-between", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 16, + "fields": { + "name": "flex-align-space-around", + "css_classes": "flex flex-align-space-around", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 17, + "fields": { + "name": "flex-align-end", + "css_classes": "flex flex-align-end", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 18, + "fields": { + "name": "background-color", + "css_classes": "color-section", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 19, + "fields": { + "name": "full-border", + "css_classes": "full-border", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 20, + "fields": { + "name": "text-align-left", + "css_classes": "align-left", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 21, + "fields": { + "name": "text-align-center", + "css_classes": "align-center", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +}, +{ + "model": "resume_manager_core.sectioncss", + "pk": 22, + "fields": { + "name": "text-align-right", + "css_classes": "align-right", + "date_created": "2023-01-01T08:00:00.000Z", + "date_modified": "2023-01-01T08:00:00.000Z" + } +} +] diff --git a/resume_manager_core/management/commands/resume_manager_core__loadfixtures.py b/resume_manager_core/management/commands/resume_manager_core__loadfixtures.py index 89b4d3c4b1102d57a924142b3947471fae2dd474..156d0fd3b4c967e80566f484402b31d9de59bfda 100644 --- a/resume_manager_core/management/commands/resume_manager_core__loadfixtures.py +++ b/resume_manager_core/management/commands/resume_manager_core__loadfixtures.py @@ -12,10 +12,10 @@ from apps.Resume_Manager.resume_manager_core.models import ( Resume, ResumeTheme, ResumeSection, - ResumeToSectionIntermediary, - DataSectionHeader, - DataItem, - DataKeyValue, + # ResumeToSectionIntermediary, + # DataSectionHeader, + # DataItem, + # DataKeyValue, ) from workspace_core.settings.reusable_settings import ConsoleColors @@ -35,12 +35,14 @@ class Command(BaseCommand): )) # Run fixture imports. - self.populate_resume_sections() self.populate_resume_themes() self.populate_resumes() - self.populate_resume_section_intermediaries() - self.populate_data_section_headers() - self.populate_data_section_items() + self.populate_section_css() + self.populate_resume_sections() + + # self.populate_resume_section_intermediaries() + # self.populate_data_section_headers() + # self.populate_data_section_items() print('{0}RESUME_MANAGER_CORE{1}: {2}Fixture Loading{3} complete.\n'.format( ConsoleColors.purple, @@ -49,12 +51,6 @@ class Command(BaseCommand): ConsoleColors.reset, )) - def populate_resume_sections(self): - """ - Loads fixtures for ResumeSection models. - """ - call_command('loaddata', 'resume_manager/resume_sections.json') - def populate_resume_themes(self): """ Loads fixtures for ResumeTheme models. @@ -65,8 +61,19 @@ class Command(BaseCommand): """ Loads fixtures for Resume models. """ - # No fixtures yet. - pass + call_command('loaddata', 'resume_manager/resumes.json') + + def populate_section_css(self): + """ + Loads fixtures for SectionCss models. + """ + call_command('loaddata', 'resume_manager/section_css.json') + + def populate_resume_sections(self): + """ + Loads fixtures for ResumeSection models. + """ + call_command('loaddata', 'resume_manager/resume_sections.json') def populate_resume_section_intermediaries(self): """ diff --git a/resume_manager_core/management/commands/resume_manager_core__seed.py b/resume_manager_core/management/commands/resume_manager_core__seed.py index 5685e951927ec5785d28ef832913d07e54d377e8..b2962741038f5254453b5666fff6675dbd60de9e 100644 --- a/resume_manager_core/management/commands/resume_manager_core__seed.py +++ b/resume_manager_core/management/commands/resume_manager_core__seed.py @@ -49,12 +49,13 @@ class Command(BaseCommand): )) # Run fixture imports. - self.create_resume_sections(model_count) self.create_resume_themes(model_count) self.create_resumes(model_count) - self.create_resume_section_intermediaries(model_count) - self.create_data_section_headers(model_count) - self.create_data_section_items(model_count) + self.create_section_css(model_count) + self.create_resume_sections(model_count) + # self.create_resume_section_intermediaries(model_count) + # self.create_data_section_headers(model_count) + # self.create_data_section_items(model_count) print('{0}RESUME_MANAGER{1}: {2}Seeding{3} complete.\n'.format( ConsoleColors.purple, @@ -63,15 +64,6 @@ class Command(BaseCommand): ConsoleColors.reset, )) - def create_resume_sections(self, model_count): - """ - Create seeds for ResumeSection models. - """ - # Load preset fixtures. - app_fixtures.populate_resume_sections() - - # Seed logic goes here. - def create_resume_themes(self, model_count): """ Create seeds for ResumeTheme models. @@ -90,6 +82,24 @@ class Command(BaseCommand): # Seed logic goes here. + def create_section_css(self, model_count): + """ + Create seeds for SectionCss models. + """ + # Load preset fixtures. + app_fixtures.populate_section_css() + + # Seed logic goes here. + + def create_resume_sections(self, model_count): + """ + Create seeds for ResumeSection models. + """ + # Load preset fixtures. + app_fixtures.populate_resume_sections() + + # Seed logic goes here. + def create_resume_section_intermediaries(self, model_count): """ Create seeds for ResumeToSectionIntermediary models. diff --git a/resume_manager_core/models.py b/resume_manager_core/models.py index 922945d69d57d4f47655e15e53d83adc00c63bc6..6688dd2761640818c0681fdaad05be1d731efde1 100644 --- a/resume_manager_core/models.py +++ b/resume_manager_core/models.py @@ -7,9 +7,10 @@ from django.db import models from django.conf import settings from django.core.exceptions import ValidationError from django.utils import timezone +from phonenumber_field.modelfields import PhoneNumberField # Internal Imports. -from core.models import WorkspaceModel +from core.models import EmailField, WebsiteField, WorkspaceModel class Resume(WorkspaceModel): @@ -21,6 +22,7 @@ class Resume(WorkspaceModel): # Model fields. name = models.CharField(max_length=settings.MAX_LENGTH) + profession = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) last_activity = models.DateField(default=timezone.now) # Field helper text. @@ -59,13 +61,39 @@ class ResumeTheme(WorkspaceModel): class ResumeSection(WorkspaceModel): """Formatting of a section/subsection within a resume.""" + + # Preset field choices. + DISPLAY_CATEGORY__NONE = 0 + DISPLAY_CATEGORY__NAME = 1 + DISPLAY_CATEGORY__PROFESSION = 2 + DISPLAY_CATEGORY__CONTACT = 3 + DISPLAY_CATEGORY__EDUCATION = 4 + DISPLAY_CATEGORY__EXPERIENCE = 5 + DISPLAY_CATEGORY__SKILLS = 6 + DISPLAY_CATEGORY_CHOICES = ( + (DISPLAY_CATEGORY__NONE, 'None'), + (DISPLAY_CATEGORY__CONTACT, 'Name'), + (DISPLAY_CATEGORY__CONTACT, 'Profession'), + (DISPLAY_CATEGORY__CONTACT, 'Contact'), + (DISPLAY_CATEGORY__EDUCATION, 'Education'), + (DISPLAY_CATEGORY__EXPERIENCE, 'Experience'), + (DISPLAY_CATEGORY__SKILLS, 'Skills'), + ) + + # Relationship Keys. + css = models.ManyToManyField('SectionCss', related_name='section') + resume = models.ForeignKey('Resume', on_delete=models.CASCADE) + parent_section = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True) + display_category = models.PositiveSmallIntegerField(choices=DISPLAY_CATEGORY_CHOICES, default=0) + # Model fields. name = models.CharField(max_length=settings.MAX_LENGTH) - css_classes = models.CharField(max_length=settings.MAX_LENGTH) # Field helper text. - name.help_text = 'Name of resume section.' - css_classes.help_text = 'CSS classes to apply for section.' + css.help_text = 'CSS stylings to apply to section.' + resume.help_text = 'Resume that section applies to.' + parent_section.help_text = 'Parent section/subsection to nest within.' + name.help_text = 'Name to help identify section purpose.' class Meta: verbose_name = 'Resume Section' @@ -75,117 +103,210 @@ class ResumeSection(WorkspaceModel): return '{0}'.format(self.name) -class ResumeToSectionIntermediary(WorkspaceModel): - """An intermediary model that associates a resume and a section. +class SectionCss(WorkspaceModel): + """Visual style formatting of a section/subsection within a resume.""" - Can be nested within other section/subsections, denoted by having a "parent". - Each subsection is a unique div. - """ - - # # Preset field choices. - # DISPLAY_CATEGORY__NONE = 0 - # DISPLAY_CATEGORY__EXPERIENCE = 1 - # DISPLAY_CATEGORY__EDUCATION = 2 - # DISPLAY_CATEGORY__SKILLS = 3 - # DISPLAY_CATEGORY_CHOICES = ( - # (DISPLAY_CATEGORY__NONE, 'None'), - # (DISPLAY_CATEGORY__EXPERIENCE, 'Experience'), - # (DISPLAY_CATEGORY__EDUCATION, 'Education'), - # (DISPLAY_CATEGORY__SKILLS, 'Skills'), - # ) - - # Relationship Keys. - resume = models.ForeignKey('Resume', on_delete=models.CASCADE) - parent_section = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True) - section_format = models.ForeignKey('ResumeSection', on_delete=models.CASCADE) - title = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) - is_data_subsection = models.BooleanField(default=False) - # display_category = models.PositiveSmallIntegerField(choices=DISPLAY_CATEGORY_CHOICES, default=0) + # Model fields. + name = models.CharField(max_length=settings.MAX_LENGTH) + css_classes = models.CharField(max_length=settings.MAX_LENGTH) # Field helper text. - resume.help_text = 'The resume to place the section within.' - parent_section.help_text = 'Parent section/subsection to nest within.' - section_format.help_text = 'The section formatting to use within the resume.' - is_data_subsection.help_text = 'Indicates if this is part of a larger parent data section. Results in de-emphasized text.' - # display_category.help_text = 'Category of data to display in section.' + name.help_text = 'Name of resume section.' + css_classes.help_text = 'CSS classes to apply for section.' class Meta: - verbose_name = 'Resume-to-Section Intermediary' - verbose_name_plural = 'Resume-to-Section Intermediaries' + verbose_name = 'Section CSS' + verbose_name_plural = 'Section CSS' def __str__(self): - return '{0} - {1}'.format(self.resume, self.section_format) - - -class DataSectionHeader(WorkspaceModel): - """Implementation of a header for a data section. + return '{0}'.format(self.name) - Each data section can only have a single header, but one is not required. - """ +class ContactInfo(WorkspaceModel): + """""" # Relationship Keys. - resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) + resume = models.ForeignKey('Resume', on_delete=models.CASCADE) # Model fields. - header = models.CharField(max_length=settings.MAX_LENGTH) + phone_number = PhoneNumberField(blank=True, null=True) + email = EmailField(max_length=settings.MAX_LENGTH) + website = WebsiteField(max_length=settings.MAX_LENGTH, blank=True, null=True) + git = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) + linkedin = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) + art_station = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) + + # Field helper text. + resume.help_text = 'Resume that contact info applies to. Allows user to have different contact info per resume.' + website.help_text = 'Personal website, or other professional site not provided here.' + git.help_text = 'Site to showcase personal git projects.' + linkedin.help_text = 'LinkedIn site profile.' + art_station.help_text = 'ArtStation site profile.' class Meta: - verbose_name = 'Data Section Header' - verbose_name_plural = 'Data Section Headers' + verbose_name = 'Contact Info' + verbose_name_plural = 'Contact Info' def __str__(self): - return '{0} - {1}'.format(self.resume_section.resume, self.header[15:]) - + return '{0} {1} - {2}'.format(self.resume.user.first_name, self.resume.user.last_name, self.resume.name) -class DataItem(WorkspaceModel): - """Implementation of a data item. - If a data section has only a single item, then the item is displayed in block/paragraph format. - If a data section has two or more items, then they are displayed in bullet point format. - """ - - # Relationship Keys. - resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) +class EducationInstance(WorkspaceModel): + """.""" # Model fields. - title_left = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) - title_right = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) - item = models.TextField(max_length=500) - show_bullet_point = models.BooleanField(default=True) - is_emphasized = models.BooleanField(default=False) - is_minimized = models.BooleanField(default=False) + name = models.CharField(max_length=settings.MAX_LENGTH) + degree = models.CharField(max_length=settings.MAX_LENGTH) + date_start = models.DateField() + date_end = models.DateField(blank=True, null=True) + + # Field helper text. + name.help_text = 'Name of institution.' + degree.help_text = 'Degree pursued/acquired.' + date_start.help_text = 'Starting date of pursuing education.' + date_end.help_text = 'Ending date of finishing education, or none if ongoing.' class Meta: - verbose_name = 'Data Section Item' - verbose_name_plural = 'Data Section Items' + verbose_name = 'Education Instance' + verbose_name_plural = 'Education Instances' def __str__(self): - return '{0} - {1}'.format(self.resume_section.resume, self.item[15:]) - - def clean(self): - """ - Custom cleaning implementation. Includes validation, setting fields, etc. - """ - # Call parent logic. - super().clean() - - if self.is_emphasized and self.is_minimized: - raise ValidationError('Data cannot be both emphasized and minimized in style.') + return '{0} - {1}'.format(self.name, self.degree) -class DataKeyValue(WorkspaceModel): - """Implementation of a data key-value pair.""" - - # Relationship Keys. - resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) +class ExperienceInstance(WorkspaceModel): + """.""" # Model fields. - data_key = models.CharField(max_length=settings.MAX_LENGTH) - data_value = models.CharField(max_length=settings.MAX_LENGTH) + name = models.CharField(max_length=settings.MAX_LENGTH) + position = models.CharField(max_length=settings.MAX_LENGTH) + date_start = models.DateField() + date_end = models.DateField(blank=True, null=True) + + # Field helper text. + name.help_text = 'Name of company/workplace.' + position.help_text = 'Title of position.' + date_start.help_text = 'Initial hiring date.' + date_end.help_text = 'Termination date, or none if ongoing.' class Meta: - verbose_name = 'Data Section Key-Value Pair' - verbose_name_plural = 'Data Section Key-Value Pairs' + verbose_name = 'Experience Instance' + verbose_name_plural = 'Experience Instances' def __str__(self): - return '{0} - {1}'.format(self.resume_section.resume, self.data_key) + return '{0} - {1}'.format(self.name, self.position) + + +# class ResumeToSectionIntermediary(WorkspaceModel): +# """An intermediary model that associates a resume and a section. +# +# Can be nested within other section/subsections, denoted by having a "parent". +# Each subsection is a unique div. +# """ +# +# # # Preset field choices. +# # DISPLAY_CATEGORY__NONE = 0 +# # DISPLAY_CATEGORY__EXPERIENCE = 1 +# # DISPLAY_CATEGORY__EDUCATION = 2 +# # DISPLAY_CATEGORY__SKILLS = 3 +# # DISPLAY_CATEGORY_CHOICES = ( +# # (DISPLAY_CATEGORY__NONE, 'None'), +# # (DISPLAY_CATEGORY__EXPERIENCE, 'Experience'), +# # (DISPLAY_CATEGORY__EDUCATION, 'Education'), +# # (DISPLAY_CATEGORY__SKILLS, 'Skills'), +# # ) +# +# # Relationship Keys. +# resume = models.ForeignKey('Resume', on_delete=models.CASCADE) +# parent_section = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True) +# section_format = models.ForeignKey('ResumeSection', on_delete=models.CASCADE) +# title = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) +# is_data_subsection = models.BooleanField(default=False) +# # display_category = models.PositiveSmallIntegerField(choices=DISPLAY_CATEGORY_CHOICES, default=0) +# +# # Field helper text. +# resume.help_text = 'The resume to place the section within.' +# parent_section.help_text = 'Parent section/subsection to nest within.' +# section_format.help_text = 'The section formatting to use within the resume.' +# is_data_subsection.help_text = 'Indicates if this is part of a larger parent data section. Results in de-emphasized text.' +# # display_category.help_text = 'Category of data to display in section.' +# +# class Meta: +# verbose_name = 'Resume-to-Section Intermediary' +# verbose_name_plural = 'Resume-to-Section Intermediaries' +# +# def __str__(self): +# return '{0} - {1}'.format(self.resume, self.section_format) +# +# +# class DataSectionHeader(WorkspaceModel): +# """Implementation of a header for a data section. +# +# Each data section can only have a single header, but one is not required. +# """ +# +# # Relationship Keys. +# resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) +# +# # Model fields. +# header = models.CharField(max_length=settings.MAX_LENGTH) +# +# class Meta: +# verbose_name = 'Data Section Header' +# verbose_name_plural = 'Data Section Headers' +# +# def __str__(self): +# return '{0} - {1}'.format(self.resume_section.resume, self.header[15:]) +# +# +# class DataItem(WorkspaceModel): +# """Implementation of a data item. +# +# If a data section has only a single item, then the item is displayed in block/paragraph format. +# If a data section has two or more items, then they are displayed in bullet point format. +# """ +# +# # Relationship Keys. +# resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) +# +# # Model fields. +# title_left = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) +# title_right = models.CharField(max_length=settings.MAX_LENGTH, blank=True, null=True) +# item = models.TextField(max_length=500) +# show_bullet_point = models.BooleanField(default=True) +# is_emphasized = models.BooleanField(default=False) +# is_minimized = models.BooleanField(default=False) +# +# class Meta: +# verbose_name = 'Data Section Item' +# verbose_name_plural = 'Data Section Items' +# +# def __str__(self): +# return '{0} - {1}'.format(self.resume_section.resume, self.item[15:]) +# +# def clean(self): +# """ +# Custom cleaning implementation. Includes validation, setting fields, etc. +# """ +# # Call parent logic. +# super().clean() +# +# if self.is_emphasized and self.is_minimized: +# raise ValidationError('Data cannot be both emphasized and minimized in style.') +# +# +# class DataKeyValue(WorkspaceModel): +# """Implementation of a data key-value pair.""" +# +# # Relationship Keys. +# resume_section = models.ForeignKey('ResumeToSectionIntermediary', on_delete=models.CASCADE) +# +# # Model fields. +# data_key = models.CharField(max_length=settings.MAX_LENGTH) +# data_value = models.CharField(max_length=settings.MAX_LENGTH) +# +# class Meta: +# verbose_name = 'Data Section Key-Value Pair' +# verbose_name_plural = 'Data Section Key-Value Pairs' +# +# def __str__(self): +# return '{0} - {1}'.format(self.resume_section.resume, self.data_key) diff --git a/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky.scss b/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky.scss index 45a7fa96c3dbf1cbd088bf55498fcb2640f3ff86..940566c6d0f0f8c818dd73a3a3c469377eb43250 100644 --- a/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky.scss +++ b/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky.scss @@ -34,7 +34,14 @@ } +* { + box-sizing: border-box; +} + + html, body { + max-width: 8.5in; +// max-height: 11in; margin: 0; padding: 0; } @@ -42,14 +49,19 @@ html, body { html { - .resume-page.full-size { - width: 8.5in; - height: 11in; - margin: 1.5cm; + .page { + width: 100%; + max-width: 8.5in; + height: 10.8in; + max-height: 10.8in; + margin: 0.75cm; + padding: 0; + + page-break-after: always; } // General Styles. - .resume-page { + .page { position: relative; @@ -61,14 +73,14 @@ html { @include var.mixin-flex(1); width: 100%; + max-width: 8.5in; // height: 100%; + max-height: 11in; margin: 0; padding: 0; border: 1px solid black; - box-sizing: border-box; - div, ul, li { position: relative; @@ -82,8 +94,6 @@ html { height: 100%; margin: 0; padding: 0; - - box-sizing: border-box; } p { @@ -95,32 +105,20 @@ html { h2, h3, h4 { width: 100%; -// height: 100%; margin: 0; padding: 0; } - .resume-section-row, - .resume-section-row > ul, - .resume-section-row > ul > li { - @include var.mixin-flex-direction(row); - } - - .resume-section-row.minimal { - @include var.mixin-flex(0); - height: auto; + .flex { + display: flex; } - .resume-section-column, - .resume-section-column > ul, - .resume-section-column > ul > li{ - @include var.mixin-flex-direction(column); + .flex-horizontal { + flex-direction: horizontal; } - .resume-section-column.minimal { - @include var.mixin-flex(0); - - width: auto; + .flex-vertical { + flex-direction: vertical; } li.no-bullet { @@ -131,8 +129,27 @@ html { font-weight: bold; } - .resume-section-style.full-color { + .full-border { border: 1px solid black; + } + + .border-top { + border: 1px solid black; + } + + .border-right { + border: 1px solid black; + } + + .border-bottom { + border: 1px solid black; + } + + .border-left { + border: 1px solid black; + } + + .color-section { background-color: grey; } @@ -147,19 +164,65 @@ html { .align-right { text-align: right; } + + .flex-ratio-10 { + flex: 10; + } + + .flex-ratio-20 { + flex: 20; + } + + .flex-ratio-30 { + flex: 30; + } + + .flex-ratio-40 { + flex: 40; + } + + .flex-ratio-50 { + flex: 50; + } + + .flex-ratio-60 { + flex: 60; + } + + .flex-ratio-70 { + flex: 70; + } + + .flex-ratio-80 { + flex: 80; + } + + .flex-ratio-90 { + flex: 90; + } + + .flex-ratio-100 { + flex: 100; + } } // Purple blocky theme styles. - .theme-blocky.purple .resume-page .resume-section-style.full-color { - border-color: #27212e; - background-color: #27212e; + .theme-blocky.purple .page { - h2, h3, h4 { - color: #fff; + .full-border, .border-top, .border-right, .border-bottom, .border-left { + border-color: #27212e; } - span.bold { - color: #233d74; + .color-section { + background-color: #27212e; + + h2, h3, h4 { + color: #fff; + } + + span.bold { + color: #233d74; + } } } } diff --git a/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky_old.scss b/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky_old.scss new file mode 100644 index 0000000000000000000000000000000000000000..45a7fa96c3dbf1cbd088bf55498fcb2640f3ff86 --- /dev/null +++ b/resume_manager_core/static/resume_manager_core/css/sass/src/_resume_blocky_old.scss @@ -0,0 +1,165 @@ + +//=============================// +//==== Dev-Tools Color CSS ====// +//=============================// + +// CSS for DevTools "color test" views. + + +//================================================================================// +//==================================== Imports ===================================// +//================================================================================// + +// Import variables. +@use '../../../../../../../../core/static/core/css/sass/src/variables/init' as var; + + +//================================================================================// +//==================================== Styles ====================================// +//================================================================================// + +//===========================================// +//==== START Resume Theme "Blocky" Block ====// +//===========================================// + +.color_section { + @include var.mixin-display-flex; + @include var.mixin-flex-direction(column); + @include var.mixin-justify-content(space-evenly); + @include var.mixin-align-items(center); + + width: 100%; + padding-top: 5px; + padding-bottom: 5px; +} + + +html, body { + margin: 0; + padding: 0; +} + + +html { + + .resume-page.full-size { + width: 8.5in; + height: 11in; + margin: 1.5cm; + } + + // General Styles. + .resume-page { + + position: relative; + + @include var.mixin-display-flex; + @include var.mixin-flex-direction(column); + @include var.mixin-flex-wrap(wrap); + @include var.mixin-justify-content(space-evenly); + @include var.mixin-align-items(center); + @include var.mixin-flex(1); + + width: 100%; +// height: 100%; + margin: 0; + padding: 0; + + border: 1px solid black; + + box-sizing: border-box; + + div, ul, li { + position: relative; + + @include var.mixin-display-flex; + @include var.mixin-flex-wrap(wrap); + @include var.mixin-justify-content(space-evenly); + @include var.mixin-align-items(center); + @include var.mixin-flex(1); + + width: 100%; + height: 100%; + margin: 0; + padding: 0; + + box-sizing: border-box; + } + + p { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + } + + h2, h3, h4 { + width: 100%; +// height: 100%; + margin: 0; + padding: 0; + } + + .resume-section-row, + .resume-section-row > ul, + .resume-section-row > ul > li { + @include var.mixin-flex-direction(row); + } + + .resume-section-row.minimal { + @include var.mixin-flex(0); + height: auto; + } + + .resume-section-column, + .resume-section-column > ul, + .resume-section-column > ul > li{ + @include var.mixin-flex-direction(column); + } + + .resume-section-column.minimal { + @include var.mixin-flex(0); + + width: auto; + } + + li.no-bullet { + list-style-type: none; + } + + span.bold { + font-weight: bold; + } + + .resume-section-style.full-color { + border: 1px solid black; + background-color: grey; + } + + .align-left { + text-align: left; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } + } + + // Purple blocky theme styles. + .theme-blocky.purple .resume-page .resume-section-style.full-color { + border-color: #27212e; + background-color: #27212e; + + h2, h3, h4 { + color: #fff; + } + + span.bold { + color: #233d74; + } + } +} diff --git a/resume_manager_core/templates/resume_manager/_display_recurse.html b/resume_manager_core/templates/resume_manager/_display_recurse.html index 5af7c35fc89ce381603f864c67570761f904a23a..76c677f257e3cf485c6f39468683af3ac86b434e 100644 --- a/resume_manager_core/templates/resume_manager/_display_recurse.html +++ b/resume_manager_core/templates/resume_manager/_display_recurse.html @@ -2,7 +2,7 @@ {% for data in data_subsection %} - <div class="{{ data.section.section_format.css_classes }}"> + <div class="{{ data.section_css }}"> {% if data.section.title %} <h3>{{ data.section.title }}</h3> {% endif %} diff --git a/resume_manager_core/templates/resume_manager/_display_recurse_debug.html b/resume_manager_core/templates/resume_manager/_display_recurse_debug.html index 76f1d94b392e5da9dd380e6802551d59abe7e579..461c94a04beafe460a45a33242d604cd3a321fb3 100644 --- a/resume_manager_core/templates/resume_manager/_display_recurse_debug.html +++ b/resume_manager_core/templates/resume_manager/_display_recurse_debug.html @@ -4,7 +4,7 @@ <ul> {% for data in data_subsection %} <li> - <p>{{ data.section }}</p> + <p>Element: {{ data.section }} || CSS Classes: {{ data.section_css }}</p> {% include 'resume_manager/_display_recurse_debug.html' with data_subsection=data.section_children %} </li> diff --git a/resume_manager_core/views.py b/resume_manager_core/views.py index 6eeeb3d3f3a4482a05d93081c66970f912d65af6..8ae43f9f04285663bf81d1d99cf6a9216e72585e 100644 --- a/resume_manager_core/views.py +++ b/resume_manager_core/views.py @@ -2,6 +2,8 @@ Views for ResumeManagerCore app. """ +# System Imports. + # Third-Party Imports. from django.db.models import Q from django.contrib import messages @@ -15,10 +17,11 @@ from django.urls import reverse from .models import ( Resume, ResumeSection, - ResumeToSectionIntermediary, - DataSectionHeader, - DataKeyValue, - DataItem, + SectionCss, + # ResumeToSectionIntermediary, + # DataSectionHeader, + # DataKeyValue, + # DataItem, ) @@ -37,7 +40,8 @@ class Index(LoginRequiredMixin, ListView): return Resume.objects.filter(user__in=[example_user, self.request.user]) -class DisplayResume(LoginRequiredMixin, DetailView): +# class DisplayResume(LoginRequiredMixin, DetailView): +class DisplayResume(DetailView): """Resume display view.""" model = Resume @@ -86,34 +90,44 @@ class DisplayResume(LoginRequiredMixin, DetailView): # Call parent logic. context = super().get_context_data(**kwargs) - # Get all intermediary relations for resume. - resume_intermediaries = ResumeToSectionIntermediary.objects.filter( - resume=self.resume, - ) + # Get all sections for resume. + resume_sections = ResumeSection.objects.filter(resume=self.resume) # Organize into parse-able data. resume_data = [] - resume_format_page = ResumeSection.objects.get(name='Page') - resume_pages = resume_intermediaries.filter( - section_format=resume_format_page, - ) + resume_pages = resume_sections.filter(parent_section=None) + page_css_data = SectionCss.objects.filter(id=1) + # Add each page as its own unique base element in dataset. for resume_page in resume_pages: # Get all related children. - section_children = resume_intermediaries.filter( + section_children = resume_sections.filter( parent_section=resume_page ) # Build sub-dictionaries for children. section_children_data = [] for child in section_children: - section_children_data.append(self.build_resume_data(child, resume_intermediaries)) + section_children_data.append(self.build_resume_data(child, resume_sections)) + + # Get all css styles associated with section. + # Note: We always associate the base "page" styles here, with the full "page" sections. + section_css_data = set() + section_css = resume_page.css.all() + section_css = section_css.union(page_css_data) # Concatenate pulled css and "page" css. + for css_object in section_css: + css_classes = css_object.css_classes.strip().split(' ') + for css_class in css_classes: + section_css_data.add(css_class) + section_css_data = ' '.join(list(section_css_data)) # Organize data value. + # Note: We always associate the base "page" styles with the outer-most pages. resume_data.append({ 'section': resume_page, 'section_children': section_children_data, + 'section_css': section_css_data, }) # Update template context. @@ -138,26 +152,36 @@ class DisplayResume(LoginRequiredMixin, DetailView): for child in section_children: section_children_data.append(self.build_resume_data(child, all_sections)) - # Get all headers for section. - section_header = DataSectionHeader.objects.filter( - resume_section=current_section, - ) - - # Get all key-value pairs for section. - section_key_value_pairs = DataKeyValue.objects.filter( - resume_section=current_section, - ) - - # Get all items for section. - section_items = DataItem.objects.filter( - resume_section=current_section, - ) + # Get all CSS styles associated with section. + section_css_data = set() + section_css = current_section.css.all() + for css_object in section_css: + css_classes = css_object.css_classes.strip().split(' ') + for css_class in css_classes: + section_css_data.add(css_class) + section_css_data = ' '.join(list(section_css_data)) + + # # Get all headers for section. + # section_header = DataSectionHeader.objects.filter( + # resume_section=current_section, + # ) + # + # # Get all key-value pairs for section. + # section_key_value_pairs = DataKeyValue.objects.filter( + # resume_section=current_section, + # ) + # + # # Get all items for section. + # section_items = DataItem.objects.filter( + # resume_section=current_section, + # ) # Organize data value. return { 'section': current_section, 'section_children': section_children_data, - 'section_header': section_header, - 'section_key_value_pairs': section_key_value_pairs, - 'section_items': section_items, + 'section_css': section_css_data, + # 'section_header': section_header, + # 'section_key_value_pairs': section_key_value_pairs, + # 'section_items': section_items, }