diff --git a/client/cypress/integration/frontend/portfolio/new-project.spec.js b/client/cypress/integration/frontend/portfolio/new-project.spec.js index 2c7346af..048d28ed 100644 --- a/client/cypress/integration/frontend/portfolio/new-project.spec.js +++ b/client/cypress/integration/frontend/portfolio/new-project.spec.js @@ -51,6 +51,6 @@ describe('New project', () => { cy.get('[data-cy=page-form-input-beschreibung]').should('exist').should('be.empty'); cy.get('[data-cy=page-form-input-ziele]').should('not.exist'); cy.get('[data-cy=save-project-button]').click(); - cy.get('.project-widget:first').contains('random'); + cy.getByDataCy('project').contains('random'); }); }); diff --git a/client/cypress/integration/frontend/portfolio/project-entry.spec.js b/client/cypress/integration/frontend/portfolio/project-entry.spec.js index 05933c30..ad16cd1a 100644 --- a/client/cypress/integration/frontend/portfolio/project-entry.spec.js +++ b/client/cypress/integration/frontend/portfolio/project-entry.spec.js @@ -46,21 +46,14 @@ describe('Project Entry', () => { }, 'entriesCount': 1, '__typename': 'ProjectNode', - 'entries': { - 'edges': [{ - 'node': { - 'id': 'UHJvamVjdEVudHJ5Tm9kZTo2NQ==', - 'activity': 'Kill Thanos', - 'reflection': 'He sucks', - 'nextSteps': 'Go for the head', - 'documentUrl': '', - '__typename': 'ProjectEntryNode', - 'created': '2020-01-20T15:20:31.262510+00:00', - }, - '__typename': 'ProjectEntryNodeEdge', - }], - '__typename': 'ProjectEntryNodeConnection', - }, + entries: [ + { + id: 'UHJvamVjdEVudHJ5Tm9kZTo2NQ==', + description: 'Aktivität:\nKill Thanos\n\n\nReflexion:\nHe sucks\n\n\nNächste Schritte:\nGo for the head', + documentUrl: '', + created: '2020-01-20T15:20:31.262510+00:00', + }, + ], }, }, AddProjectEntry: variables => ({ diff --git a/client/src/components/portfolio/NewProjectEntryWizard.vue b/client/src/components/portfolio/NewProjectEntryWizard.vue index 46c779f3..53931b18 100644 --- a/client/src/components/portfolio/NewProjectEntryWizard.vue +++ b/client/src/components/portfolio/NewProjectEntryWizard.vue @@ -50,10 +50,7 @@ const variables = {slug: this.slug}; const data = store.readQuery({query, variables}); if (data.project && data.project.entries) { - data.project.entries.edges.push({ - node: projectEntry, - __typename: 'ProjectEntryNode' - }); + data.project.entries.push(projectEntry); store.writeQuery({query, variables, data}); } } diff --git a/client/src/components/portfolio/ProjectEntry.vue b/client/src/components/portfolio/ProjectEntry.vue index 1dcaff85..78647780 100644 --- a/client/src/components/portfolio/ProjectEntry.vue +++ b/client/src/components/portfolio/ProjectEntry.vue @@ -16,21 +16,11 @@ -
- {{ activity }} -
-- {{ reflection }} -
-- {{ nextSteps }} + {{ description }}
edge.node.id === projectEntry.id), 1); + data.project.entries.splice(data.project.entries.findIndex(entry => entry.id === projectEntry.id), 1); store.writeQuery({query, variables, data}); } } @@ -105,6 +95,7 @@ &__paragraph { margin-bottom: 30px; + white-space: pre-line; } &__date { diff --git a/client/src/components/portfolio/ProjectEntryForm.vue b/client/src/components/portfolio/ProjectEntryForm.vue index a92f4b7e..dce6209c 100644 --- a/client/src/components/portfolio/ProjectEntryForm.vue +++ b/client/src/components/portfolio/ProjectEntryForm.vue @@ -81,6 +81,8 @@ @import "~styles/helpers"; .project-entry-modal { + display: flex; + &__heading { @include heading-3; margin-bottom: 0; diff --git a/client/src/graphql/gql/fragments/projectEntryParts.gql b/client/src/graphql/gql/fragments/projectEntryParts.gql index 415aa2cb..0774e1e0 100644 --- a/client/src/graphql/gql/fragments/projectEntryParts.gql +++ b/client/src/graphql/gql/fragments/projectEntryParts.gql @@ -1,7 +1,5 @@ fragment ProjectEntryParts on ProjectEntryNode { id - activity - reflection - nextSteps + description documentUrl } diff --git a/client/src/graphql/gql/queries/projectQuery.gql b/client/src/graphql/gql/queries/projectQuery.gql index 12b7777a..dd601e64 100644 --- a/client/src/graphql/gql/queries/projectQuery.gql +++ b/client/src/graphql/gql/queries/projectQuery.gql @@ -4,12 +4,8 @@ query ProjectQuery($id: ID, $slug: String){ project(slug: $slug, id: $id) { ...ProjectParts entries { - edges { - node { - ...ProjectEntryParts - created - } - } + ...ProjectEntryParts + created } } diff --git a/server/portfolio/factories.py b/server/portfolio/factories.py index 4432b5fc..d26abda1 100644 --- a/server/portfolio/factories.py +++ b/server/portfolio/factories.py @@ -4,6 +4,7 @@ import factory from core.factories import fake from portfolio.models import Project, ProjectEntry +from users.models import User class ProjectFactory(factory.django.DjangoModelFactory): @@ -14,4 +15,12 @@ class ProjectFactory(factory.django.DjangoModelFactory): title = factory.LazyAttribute(lambda x: fake.sentence(nb_words=random.randint(4, 8))) appearance = factory.LazyAttribute(lambda x: random.choice(['red', 'green', 'yellow'])) final = False + student = factory.Iterator(User.objects.all()) + + +class ProjectEntryFactory(factory.django.DjangoModelFactory): + class Meta: + model = ProjectEntry + + project = factory.SubFactory(ProjectFactory) diff --git a/server/portfolio/migration_helpers.py b/server/portfolio/migration_helpers.py new file mode 100644 index 00000000..5e9b820b --- /dev/null +++ b/server/portfolio/migration_helpers.py @@ -0,0 +1,6 @@ +def migrate_project_entries(ProjectEntry): + template = 'Tätigkeit:\n{}\n\n\nReflexion:\n{}\n\n\nNächste Schritte:\n{}' + for pe in ProjectEntry.objects.all(): + pe.description = template.format(pe.activity, pe.reflection, pe.next_steps) + pe.save() + diff --git a/server/portfolio/migrations/0007_projectentry_description.py b/server/portfolio/migrations/0007_projectentry_description.py new file mode 100644 index 00000000..320a874c --- /dev/null +++ b/server/portfolio/migrations/0007_projectentry_description.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.22 on 2021-09-28 12:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('portfolio', '0006_auto_20210810_1348'), + ] + + operations = [ + migrations.AddField( + model_name='projectentry', + name='description', + field=models.TextField(blank=True), + ), + ] diff --git a/server/portfolio/migrations/0008_auto_20210928_1227.py b/server/portfolio/migrations/0008_auto_20210928_1227.py new file mode 100644 index 00000000..a21dc510 --- /dev/null +++ b/server/portfolio/migrations/0008_auto_20210928_1227.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.22 on 2021-09-28 12:27 + +from django.db import migrations + +from portfolio.migration_helpers import migrate_project_entries + + +def migrate_project_entries_migration(apps, schema_editor): + ProjectEntry = apps.get_model('portfolio', 'ProjectEntry') + migrate_project_entries(ProjectEntry) + +class Migration(migrations.Migration): + + dependencies = [ + ('portfolio', '0007_projectentry_description'), + ] + + operations = [ + migrations.RunPython(migrate_project_entries_migration) + ] diff --git a/server/portfolio/models.py b/server/portfolio/models.py index 9478ced8..d369d20b 100644 --- a/server/portfolio/models.py +++ b/server/portfolio/models.py @@ -19,8 +19,9 @@ class ProjectEntry(models.Model): reflection = models.TextField(blank=True) next_steps = models.TextField(blank=True) document_url = models.TextField(blank=True) + description = models.TextField(blank=True) created = models.DateTimeField(auto_now_add=True) project = models.ForeignKey(Project, related_name='entries', on_delete=models.CASCADE) def __str__(self): - return self.activity + return self.description diff --git a/server/portfolio/schema.py b/server/portfolio/schema.py index fd4176e5..1ebc10a4 100644 --- a/server/portfolio/schema.py +++ b/server/portfolio/schema.py @@ -2,16 +2,23 @@ import graphene from django.db.models import Q from graphene import relay from graphene_django import DjangoObjectType -from graphene_django.filter import DjangoFilterConnectionField from api.utils import get_by_id_or_slug from portfolio.models import Project, ProjectEntry from users.models import Role, UserRole +class ProjectEntryNode(DjangoObjectType): + class Meta: + model = ProjectEntry + interfaces = (relay.Node,) + fields = ('description', 'document_url', 'project', 'created') + + class ProjectNode(DjangoObjectType): pk = graphene.Int() entries_count = graphene.Int() + entries = graphene.List(ProjectEntryNode) class Meta: model = Project @@ -24,11 +31,9 @@ class ProjectNode(DjangoObjectType): def resolve_entries_count(self, *args, **kwargs): return self.entries.count() - -class ProjectEntryNode(DjangoObjectType): - class Meta: - model = ProjectEntry - interfaces = (relay.Node,) + @staticmethod + def resolve_entries(root: Project, info, **kwargs): + return root.entries.all() class PortfolioQuery(object): diff --git a/server/portfolio/tests/test_project_entry_migration.py b/server/portfolio/tests/test_project_entry_migration.py new file mode 100644 index 00000000..e0c8ab45 --- /dev/null +++ b/server/portfolio/tests/test_project_entry_migration.py @@ -0,0 +1,35 @@ +from api.test_utils import DefaultUserTestCase +from core.tests.base_test import SkillboxTestCase +from portfolio.factories import ProjectEntryFactory +from portfolio.migration_helpers import migrate_project_entries +from portfolio.models import ProjectEntry + +ACTIVITY='Kill Thanos' +REFLECTION='He sucks' +NEXT_STEPS='Go for the head' + +class ProjectEntryMigrationTestCase(SkillboxTestCase): + def setUp(self): + self.createDefault() + self.project_entry = ProjectEntryFactory( + activity=ACTIVITY, + reflection=REFLECTION, + next_steps=NEXT_STEPS + ) + + def test_migration(self): + self.assertEqual(self.project_entry.description, '') + self.assertEqual(self.project_entry.activity, ACTIVITY) + self.assertEqual(self.project_entry.reflection, REFLECTION) + self.assertEqual(self.project_entry.next_steps, NEXT_STEPS) + + migrate_project_entries(ProjectEntry) + + project_entry = ProjectEntry.objects.get(id=self.project_entry.id) + self.assertEqual(project_entry.activity, ACTIVITY) + self.assertEqual(project_entry.reflection, REFLECTION) + self.assertEqual(project_entry.next_steps, NEXT_STEPS) + self.assertEqual( + project_entry.description, + f'Tätigkeit:\n{ACTIVITY}\n\n\nReflexion:\n{REFLECTION}\n\n\nNächste Schritte:\n{NEXT_STEPS}' + ) diff --git a/server/schema.graphql b/server/schema.graphql index a58ae6ec..479e2271 100644 --- a/server/schema.graphql +++ b/server/schema.graphql @@ -857,23 +857,11 @@ type PrivateUserNodeEdge { } type ProjectEntryNode implements Node { - id: ID! - activity: String! - reflection: String! - nextSteps: String! documentUrl: String! + description: String! created: DateTime! project: ProjectNode! -} - -type ProjectEntryNodeConnection { - pageInfo: PageInfo! - edges: [ProjectEntryNodeEdge]! -} - -type ProjectEntryNodeEdge { - node: ProjectEntryNode - cursor: String! + id: ID! } type ProjectNode implements Node { @@ -886,7 +874,7 @@ type ProjectNode implements Node { student: PrivateUserNode! final: Boolean! schoolClass: SchoolClassNode - entries(offset: Int, before: String, after: String, first: Int, last: Int): ProjectEntryNodeConnection! + entries: [ProjectEntryNode] pk: Int entriesCount: Int }