From 28b97c964079014c7f668c48bde141e6e0ac1ae9 Mon Sep 17 00:00:00 2001 From: shayan Date: Sun, 12 May 2019 17:02:15 +0430 Subject: [PATCH] Revise time card to daily report, closes #805 closes #806 closes #807 closes #808 closes #809 --- dolphin/controllers/dailyreport.py | 69 ++++++++++++++ dolphin/controllers/root.py | 4 +- dolphin/controllers/timecard.py | 58 ----------- dolphin/exceptions.py | 16 ++-- dolphin/models/__init__.py | 2 +- dolphin/models/dailyreport.py | 72 ++++++++++++++ dolphin/models/item.py | 10 +- dolphin/models/timecard.py | 87 ----------------- ...d_create.py => test_dailyreport_create.py} | 92 +++++------------- ...imecard_get.py => test_dailyreport_get.py} | 21 ++-- ...ecard_list.py => test_dailyreport_list.py} | 41 ++++---- dolphin/tests/test_dailyreport_metadata.py | 47 +++++++++ dolphin/tests/test_timecard_metadata.py | 48 ---------- dolphin/tests/test_timecard_update.py | 95 ++++++------------- dolphin/validators.py | 52 +++------- 15 files changed, 301 insertions(+), 413 deletions(-) create mode 100644 dolphin/controllers/dailyreport.py delete mode 100644 dolphin/controllers/timecard.py create mode 100644 dolphin/models/dailyreport.py delete mode 100644 dolphin/models/timecard.py rename dolphin/tests/{test_timecard_create.py => test_dailyreport_create.py} (54%) rename dolphin/tests/{test_timecard_get.py => test_dailyreport_get.py} (84%) rename dolphin/tests/{test_timecard_list.py => test_dailyreport_list.py} (77%) create mode 100644 dolphin/tests/test_dailyreport_metadata.py delete mode 100644 dolphin/tests/test_timecard_metadata.py diff --git a/dolphin/controllers/dailyreport.py b/dolphin/controllers/dailyreport.py new file mode 100644 index 00000000..832e3ddc --- /dev/null +++ b/dolphin/controllers/dailyreport.py @@ -0,0 +1,69 @@ +from datetime import datetime + +from nanohttp import json, int_or_notfound, HTTPNotFound +from restfulpy.authorization import authorize +from restfulpy.controllers import ModelRestController +from restfulpy.orm import DBSession, commit + +from ..exceptions import StatusEndDateMustBeGreaterThanStartDate +from ..models import Dailyreport +from ..validators import dailyreport_create_validator, \ + dailyreport_update_validator + + +class DailyreportController(ModelRestController): + __model__ = Dailyreport + + @authorize + @json( + prevent_empty_form='708 Empty Form', + form_whitelist=( + ['hours', 'note', 'itemId'], + '707 Invalid field, only following fields are accepted: ' \ + 'hours, note and itemId' + ) + ) + @dailyreport_create_validator + @commit + def create(self): + dailyreport = Dailyreport() + dailyreport.update_from_request() + dailyreport.date = datetime.now().date() + DBSession.add(dailyreport) + return dailyreport + + @authorize + @json(prevent_form='709 Form Not Allowed') + def get(self, id): + id = int_or_notfound(id) + dailyreport = DBSession.query(Dailyreport).get(id) + if dailyreport is None: + raise HTTPNotFound() + + return dailyreport + + @authorize + @json( + prevent_empty_form='708 Empty Form', + form_whitelist=( + ['hours', 'note'], + '707 Invalid field, only following fields are accepted: hours, note' + ) + ) + @dailyreport_update_validator + @commit + def update(self, id): + id = int_or_notfound(id) + dailyreport = DBSession.query(Dailyreport).get(id) + if dailyreport is None: + raise HTTPNotFound() + + dailyreport.update_from_request() + return dailyreport + + @authorize + @json(prevent_form='709 Form Not Allowed') + @Dailyreport.expose + def list(self): + return DBSession.query(Dailyreport) + diff --git a/dolphin/controllers/root.py b/dolphin/controllers/root.py index 3813a7a1..76bf0d09 100644 --- a/dolphin/controllers/root.py +++ b/dolphin/controllers/root.py @@ -25,7 +25,7 @@ from .activity import ActivityController from .eventtype import EventTypeController from .event import EventController -from .timecard import TimecardController +from .dailyreport import DailyreportController here = abspath(dirname(__file__)) @@ -54,7 +54,7 @@ class Apiv1(RestController, JsonPatchControllerMixin): activities = ActivityController() eventtypes = EventTypeController() events = EventController() - timecards = TimecardController() + dailyreports = DailyreportController() @json def version(self): diff --git a/dolphin/controllers/timecard.py b/dolphin/controllers/timecard.py deleted file mode 100644 index 5892c37e..00000000 --- a/dolphin/controllers/timecard.py +++ /dev/null @@ -1,58 +0,0 @@ -from nanohttp import json, int_or_notfound, HTTPNotFound -from restfulpy.authorization import authorize -from restfulpy.controllers import ModelRestController -from restfulpy.orm import DBSession, commit - -from ..exceptions import StatusEndDateMustBeGreaterThanStartDate -from ..models import Timecard -from ..validators import timecard_create_validator, timecard_update_validator - - -class TimecardController(ModelRestController): - __model__ = Timecard - - @authorize - @json(prevent_empty_form='708 Empty Form') - @timecard_create_validator - @commit - def create(self): - time_card = Timecard() - time_card.update_from_request() - if time_card.start_date > time_card.end_date: - raise StatusEndDateMustBeGreaterThanStartDate() - - DBSession.add(time_card) - return time_card - - @authorize - @json(prevent_form='709 Form Not Allowed') - def get(self, id): - id = int_or_notfound(id) - timecard = DBSession.query(Timecard).get(id) - if timecard is None: - raise HTTPNotFound() - - return timecard - - @authorize - @json(prevent_empty_form='708 Empty Form') - @timecard_update_validator - @commit - def update(self, id): - id = int_or_notfound(id) - timecard = DBSession.query(Timecard).get(id) - if timecard is None: - raise HTTPNotFound() - - timecard.update_from_request() - if timecard.start_date > timecard.end_date: - raise StatusEndDateMustBeGreaterThanStartDate() - - return timecard - - @authorize - @json(prevent_form='709 Form Not Allowed') - @Timecard.expose - def list(self): - return DBSession.query(Timecard) - diff --git a/dolphin/exceptions.py b/dolphin/exceptions.py index 4464b81d..4267c1d3 100644 --- a/dolphin/exceptions.py +++ b/dolphin/exceptions.py @@ -156,12 +156,12 @@ class StatusInvalidEndDateFormat(HTTPKnownStatus): status = '790 Invalid End Date Format' -class StatusLimitedCharecterForSummary(HTTPKnownStatus): - status = '902 At Most 1024 Characters Are Valid For Summary' +class StatusLimitedCharecterForNote(HTTPKnownStatus): + status = '902 At Most 1024 Characters Are Valid For Note' -class StatusInvalidEstimatedTimeType(HTTPKnownStatus): - status = '900 Invalid Estimated Time Type' +class StatusInvalidHoursType(HTTPKnownStatus): + status = '900 Invalid Hours Type' class StatusSummaryNotInForm(HTTPKnownStatus): @@ -180,8 +180,8 @@ class StatusEstimatedTimeNotInForm(HTTPKnownStatus): status = '901 Estimated Time Not In Form' -class StatusSummaryIsNull(HTTPKnownStatus): - status = '903 Summary Is Null' +class StatusNoteIsNull(HTTPKnownStatus): + status = '903 Note Is Null' class StatusEstimatedTimeIsNull(HTTPKnownStatus): @@ -203,3 +203,7 @@ class StatusRepeatNotInForm(HTTPKnownStatus): class StatusQueryParameterNotInFormOrQueryString(HTTPKnownStatus): status = '912 Query Parameter Not In Form Or Query String' + +class StatusHoursMustBeGreaterThanZero(HTTPKnownStatus): + status = '914 Hours Must Be Greater Than 0' + diff --git a/dolphin/models/__init__.py b/dolphin/models/__init__.py index fd4f50f8..21142efb 100644 --- a/dolphin/models/__init__.py +++ b/dolphin/models/__init__.py @@ -20,6 +20,6 @@ from .skill import Skill, SkillMember from .activity import Activity from .eventtype import EventType -from .timecard import Timecard +from .dailyreport import Dailyreport from .event import Event, event_repeats diff --git a/dolphin/models/dailyreport.py b/dolphin/models/dailyreport.py new file mode 100644 index 00000000..55abb789 --- /dev/null +++ b/dolphin/models/dailyreport.py @@ -0,0 +1,72 @@ +from datetime import datetime + +from restfulpy.orm import Field, DeclarativeBase, OrderingMixin, \ + FilteringMixin, PaginationMixin, relationship +from sqlalchemy import Integer, Unicode, ForeignKey, Date + + +class Dailyreport(OrderingMixin, FilteringMixin, PaginationMixin, \ + DeclarativeBase): + __tablename__ = 'dailyreport' + + item_id = Field( + Integer, + ForeignKey('item.id'), + python_type=int, + nullable=False, + watermark='Choose a assginment', + label='Assginment', + not_none=True, + required=True, + example='Lorem Ipsum' + ) + id = Field( + Integer, + primary_key=True, + readonly=True, + not_none=True, + required=False, + label='ID', + minimum=1, + ) + date = Field( + Date, + python_type=datetime.date, + label='Date', + pattern=r'^(\d{4})-(0[1-9]|1[012]|[1-9])-(0[1-9]|[12]\d{1}|3[01]|[1-9])', + pattern_description='ISO format like "yyyy-mm-dd" is valid', + example='2018-02-02', + watermark='Date of daily report', + nullable=False, + not_none=True, + required=False, + readonly=True, + ) + hours = Field( + Integer, + python_type=int, + watermark='Hours spent on the assignment', + label='Hours', + example=2, + nullable=True, + not_none=False, + required=False, + ) + note = Field( + Unicode, + min_length=1, + max_length=1024, + label='Lorem Isum', + watermark='Lorem Ipsum', + not_none=False, + nullable=True, + required=False, + python_type=str, + example='Lorem Ipsum', + ) + + item = relationship( + 'Item', + back_populates='dailyreports' + ) + diff --git a/dolphin/models/item.py b/dolphin/models/item.py index cd9354ed..eb7bbe6d 100644 --- a/dolphin/models/item.py +++ b/dolphin/models/item.py @@ -7,7 +7,7 @@ String, select, func from sqlalchemy.orm import column_property -from .timecard import Timecard +from .dailyreport import Dailyreport item_statuses = [ @@ -133,14 +133,14 @@ class Item(TimestampMixin, OrderingMixin, FilteringMixin, PaginationMixin, foreign_keys=issue_id, back_populates='items' ) - timecards = relationship( - 'Timecard', + dailyreports = relationship( + 'Dailyreport', back_populates='item' ) hours_worked = column_property( - select([func.sum(Timecard.estimated_time)]) - .where(Timecard.item_id == id) + select([func.sum(Dailyreport.hours)]) + .where(Dailyreport.item_id == id) ) UniqueConstraint(phase_id, issue_id, member_id) diff --git a/dolphin/models/timecard.py b/dolphin/models/timecard.py deleted file mode 100644 index b849ca1f..00000000 --- a/dolphin/models/timecard.py +++ /dev/null @@ -1,87 +0,0 @@ -from datetime import datetime - -from restfulpy.orm import Field, DeclarativeBase, OrderingMixin, \ - FilteringMixin, PaginationMixin, relationship -from sqlalchemy import Integer, Unicode, DateTime, ForeignKey - - -class Timecard(OrderingMixin, FilteringMixin, PaginationMixin, \ - DeclarativeBase): - __tablename__ = 'timecard' - - item_id = Field( - Integer, - ForeignKey('item.id'), - python_type=int, - nullable=False, - watermark='Choose a assginment', - label='Assginment', - not_none=True, - required=True, - example='Lorem Ipsum' - ) - id = Field( - Integer, - primary_key=True, - readonly=True, - not_none=True, - required=False, - label='ID', - minimum=1, - ) - start_date= Field( - DateTime, - python_type=datetime, - label='Start Date', - pattern= - r'^(\d{4})-(0[1-9]|1[012]|[1-9])-(0[1-9]|[12]\d{1}|3[01]|[1-9])' - r'(T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z)?)?$', - pattern_description='ISO format and format like "yyyy-mm-dd" is valid', - example='2018-02-02T1:12:12.000Z', - watermark='Enter a start date', - nullable=False, - not_none=True, - required=True, - ) - end_date = Field( - DateTime, - python_type=datetime, - label='End Date', - pattern= - r'^(\d{4})-(0[1-9]|1[012]|[1-9])-(0[1-9]|[12]\d{1}|3[01]|[1-9])' - r'(T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z)?)?$', - pattern_description='ISO format and format like "yyyy-mm-dd" is valid', - example='2018-02-02T1:12:12.000Z', - watermark='Enter a end date', - nullable=False, - not_none=True, - required=True, - ) - estimated_time = Field( - Integer, - python_type=int, - watermark='Estimated Time', - label='Estimated Time', - example=2, - nullable=False, - not_none=True, - required=True, - ) - summary = Field( - Unicode, - min_length=1, - max_length=1024, - label='Lorem Isum', - watermark='Lorem Ipsum', - not_none=True, - nullable=False, - required=True, - python_type=str, - example='Lorem Ipsum', - ) - - item = relationship( - 'Item', - back_populates='timecards' - ) - diff --git a/dolphin/tests/test_timecard_create.py b/dolphin/tests/test_dailyreport_create.py similarity index 54% rename from dolphin/tests/test_timecard_create.py rename to dolphin/tests/test_dailyreport_create.py index 5708f138..b415c51b 100644 --- a/dolphin/tests/test_timecard_create.py +++ b/dolphin/tests/test_dailyreport_create.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime from bddrest import status, response, when, given from auditor.context import Context as AuditLogContext @@ -8,7 +8,7 @@ from dolphin.tests.helpers import LocalApplicationTestCase, oauth_mockup_server -class TestTimecard(LocalApplicationTestCase): +class TestDailyreport(LocalApplicationTestCase): @classmethod @AuditLogContext(dict()) @@ -79,25 +79,22 @@ def mockup(cls): def test_create(self): self.login(self.member.email) form = dict( - startDate=datetime.datetime.now().isoformat(), - endDate=datetime.datetime.now().isoformat(), - summary='Some summary', - estimatedTime=2, + note='Some summary', + hours=2, itemId=self.item.id, ) with oauth_mockup_server(), self.given( - 'Creating a timecard', - '/apiv1/timecards', + 'Creating a dailyreport', + '/apiv1/dailyreports', 'CREATE', json=form ): assert status == 200 assert response.json['id'] is not None - assert response.json['startDate'] == form['startDate'] - assert response.json['endDate'] == form['endDate'] - assert response.json['estimatedTime'] == form['estimatedTime'] - assert response.json['summary'] == form['summary'] + assert response.json['date'] == str(datetime.now().date()) + assert response.json['hours'] == form['hours'] + assert response.json['note'] == form['note'] assert response.json['itemId'] == form['itemId'] when( @@ -122,74 +119,29 @@ def test_create(self): assert status == '708 Empty Form' when( - 'Summary length is less than limit', - json=given | dict(summary=(1024 + 1) * 'a'), + 'Invalid parameter is in form', + json=given | dict(parameter='invalid parameter') ) - assert status == '902 At Most 1024 Characters Are Valid For ' \ - 'Summary' + assert status == '707 Invalid field, only following fields are ' \ + 'accepted: hours, note and itemId' when( - 'Trying to pass without start date', - json=given - 'startDate', + 'Note length is less than limit', + json=given | dict(note=(1024 + 1) * 'a'), ) - assert status == '792 Start Date Not In Form' + assert status == '902 At Most 1024 Characters Are Valid For Note' when( - 'Trying to pass without end date', - json=given - 'endDate', + 'Hours value type is wrong', + json=given | dict(hours='a') ) - assert status == '793 End Date Not In Form' + assert status == '900 Invalid Hours Type' when( - 'Trying to pass without summary', - json=given - 'summary', + 'Hours value is less then 1', + json=given | dict(hours=0) ) - assert status == '799 Summary Not In Form' - - when( - 'Trying to pass without estimated time', - json=given - 'estimatedTime', - ) - assert status == '901 Estimated Time Not In Form' - - when( - 'Estimated time type is wrong', - json=given | dict(estimatedTime='time'), - ) - assert status == '900 Invalid Estimated Time Type' - - when( - 'Start date format is wrong', - json=given | dict(startDate='30-20-20') - ) - assert status == '791 Invalid Start Date Format' - - when( - 'End date format is wrong', - json=given | dict(endDate='30-20-20') - ) - assert status == '790 Invalid End Date Format' - - when( - 'End date must be greater than start date', - json=given | dict( - startDate=form['endDate'], - endDate=form['startDate'], - ) - ) - assert status == '657 End Date Must Be Greater Than Start Date' - - when('Start date is null', json=given | dict(startDate=None)) - assert status == '905 Start Date Is Null' - - when('End date is null', json=given | dict(endDate=None)) - assert status == '906 End Date Is Null' - - when('Estimated time is null', json=given | dict(estimatedTime=None)) - assert status == '904 Estimated Time Is Null' - - when('Summary is null', json=given | dict(summary=None)) - assert status == '903 Summary Is Null' + assert status == '914 Hours Must Be Greater Than 0' when('Request is not authorized', authorization=None) assert status == 401 diff --git a/dolphin/tests/test_timecard_get.py b/dolphin/tests/test_dailyreport_get.py similarity index 84% rename from dolphin/tests/test_timecard_get.py rename to dolphin/tests/test_dailyreport_get.py index 3d1d533c..089c09d7 100644 --- a/dolphin/tests/test_timecard_get.py +++ b/dolphin/tests/test_dailyreport_get.py @@ -3,12 +3,12 @@ from bddrest import status, response, when from auditor.context import Context as AuditLogContext -from dolphin.models import Member, Timecard, Workflow, Skill, Group, Release, \ +from dolphin.models import Member, Dailyreport, Workflow, Skill, Group, Release, \ Project, Issue, Item, Phase from dolphin.tests.helpers import LocalApplicationTestCase, oauth_mockup_server -class TestTimecard(LocalApplicationTestCase): +class TestDailyreport(LocalApplicationTestCase): @classmethod @AuditLogContext(dict()) @@ -75,26 +75,25 @@ def mockup(cls): ) session.add(cls.item) - cls.timecard = Timecard( - start_date=datetime.datetime.now().isoformat(), - end_date=datetime.datetime.now().isoformat(), - estimated_time=2, - summary='Summary for timecard', + cls.dailyreport = Dailyreport( + date=datetime.datetime.now().date(), + hours=3, + note='The note for a daily report', item=cls.item, ) - session.add(cls.timecard) + session.add(cls.dailyreport) session.commit() def test_get(self): self.login(self.member.email) with oauth_mockup_server(), self.given( - f'Get a timecard', - f'/apiv1/timecards/id: {self.timecard.id}', + f'Get a dailyreport', + f'/apiv1/dailyreports/id: {self.dailyreport.id}', f'GET', ): assert status == 200 - assert response.json['id'] == self.timecard.id + assert response.json['id'] == self.dailyreport.id when( 'Intended group with string type not found', diff --git a/dolphin/tests/test_timecard_list.py b/dolphin/tests/test_dailyreport_list.py similarity index 77% rename from dolphin/tests/test_timecard_list.py rename to dolphin/tests/test_dailyreport_list.py index 4925461d..0faba45e 100644 --- a/dolphin/tests/test_timecard_list.py +++ b/dolphin/tests/test_dailyreport_list.py @@ -3,12 +3,12 @@ from bddrest import when, response, status from auditor.context import Context as AuditLogContext -from dolphin.models import Member, Timecard, Workflow, Skill, Group, Phase, \ +from dolphin.models import Member, Dailyreport, Workflow, Skill, Group, Phase, \ Release, Project, Issue, Item from dolphin.tests.helpers import LocalApplicationTestCase, oauth_mockup_server -class TestTimecard(LocalApplicationTestCase): +class TestDailyreport(LocalApplicationTestCase): @classmethod @AuditLogContext(dict()) @@ -74,40 +74,37 @@ def mockup(cls): ) session.add(item) - timecard1 = Timecard( - start_date=datetime.datetime.now().isoformat(), - end_date=datetime.datetime.now().isoformat(), - estimated_time=1, - summary='Summary for timecard1', + dailyreport1 = Dailyreport( + date=datetime.datetime.now().date(), + hours=1, + note='note for dailyreport1', item=item, ) - session.add(timecard1) + session.add(dailyreport1) - timecard2 = Timecard( - start_date=datetime.datetime.now().isoformat(), - end_date=datetime.datetime.now().isoformat(), - estimated_time=2, - summary='Summary for timecard2', + dailyreport2 = Dailyreport( + date=datetime.datetime.now().date(), + hours=2, + note='note for dailyreport2', item=item, ) - session.add(timecard2) + session.add(dailyreport2) - timecard3 = Timecard( - start_date=datetime.datetime.now().isoformat(), - end_date=datetime.datetime.now().isoformat(), - estimated_time=3, - summary='Summary for timecard3', + dailyreport3 = Dailyreport( + date=datetime.datetime.now().date(), + hours=3, + note='note for dailyreport3', item=item, ) - session.add(timecard3) + session.add(dailyreport3) session.commit() def test_list(self): self.login(self.member.email) with oauth_mockup_server(), self.given( - 'List of timecards', - '/apiv1/timecards', + 'List of dailyreports', + '/apiv1/dailyreports', 'LIST', ): assert status == 200 diff --git a/dolphin/tests/test_dailyreport_metadata.py b/dolphin/tests/test_dailyreport_metadata.py new file mode 100644 index 00000000..01037945 --- /dev/null +++ b/dolphin/tests/test_dailyreport_metadata.py @@ -0,0 +1,47 @@ +from bddrest.authoring import status, response + +from dolphin.tests.helpers import LocalApplicationTestCase + + +class TestDailyreport(LocalApplicationTestCase): + + def test_metadata(self): + with self.given( + 'Test metadata verb', + '/apiv1/dailyreports', + 'METADATA' + ): + assert status == 200 + fields = response.json['fields'] + + assert fields['id']['label'] is not None + assert fields['id']['minimum'] is not None + assert fields['id']['name'] is not None + assert fields['id']['key'] is not None + assert fields['id']['notNone'] is not None + assert fields['id']['required'] is not None + assert fields['id']['readonly'] is not None + assert fields['id']['primaryKey'] is not None + + assert fields['date']['label'] is not None + assert fields['date']['watermark'] is not None + assert fields['date']['pattern'] is not None + assert fields['date']['example'] is not None + assert fields['date']['name'] is not None + assert fields['date']['notNone'] is not None + assert fields['date']['required'] is not None + + assert fields['hours']['label'] is not None + assert fields['hours']['watermark'] is not None + assert fields['hours']['example'] is not None + assert fields['hours']['name'] is not None + assert fields['hours']['notNone'] is not None + assert fields['hours']['required'] is not None + + assert fields['itemId']['label'] is not None + assert fields['itemId']['watermark'] is not None + assert fields['itemId']['example'] is not None + assert fields['itemId']['name'] is not None + assert fields['itemId']['notNone'] is not None + assert fields['itemId']['required'] is not None + diff --git a/dolphin/tests/test_timecard_metadata.py b/dolphin/tests/test_timecard_metadata.py deleted file mode 100644 index 56a80132..00000000 --- a/dolphin/tests/test_timecard_metadata.py +++ /dev/null @@ -1,48 +0,0 @@ -from bddrest.authoring import status, response - -from dolphin.tests.helpers import LocalApplicationTestCase - - -class TestTimecard(LocalApplicationTestCase): - - def test_metadata(self): - with self.given( - 'Test metadata verb', - '/apiv1/timecards', - 'METADATA' - ): - assert status == 200 - fields = response.json['fields'] - - assert fields['id']['label'] is not None - assert fields['id']['minimum'] is not None - assert fields['id']['name'] is not None - assert fields['id']['key'] is not None - assert fields['id']['notNone'] is not None - assert fields['id']['required'] is not None - assert fields['id']['readonly'] is not None - assert fields['id']['primaryKey'] is not None - - assert fields['startDate']['label'] is not None - assert fields['startDate']['watermark'] is not None - assert fields['startDate']['pattern'] is not None - assert fields['startDate']['example'] is not None - assert fields['startDate']['name'] is not None - assert fields['startDate']['notNone'] is not None - assert fields['startDate']['required'] is not None - - assert fields['endDate']['label'] is not None - assert fields['endDate']['watermark'] is not None - assert fields['endDate']['pattern'] is not None - assert fields['endDate']['example'] is not None - assert fields['endDate']['name'] is not None - assert fields['endDate']['notNone'] is not None - assert fields['endDate']['required'] is not None - - assert fields['estimatedTime']['label'] is not None - assert fields['estimatedTime']['watermark'] is not None - assert fields['estimatedTime']['example'] is not None - assert fields['estimatedTime']['name'] is not None - assert fields['estimatedTime']['notNone'] is not None - assert fields['estimatedTime']['required'] is not None - diff --git a/dolphin/tests/test_timecard_update.py b/dolphin/tests/test_timecard_update.py index 0b742902..24a0bc27 100644 --- a/dolphin/tests/test_timecard_update.py +++ b/dolphin/tests/test_timecard_update.py @@ -3,12 +3,12 @@ from bddrest import status, response, when, given from auditor.context import Context as AuditLogContext -from dolphin.models import Member, Timecard, Workflow, Skill, Group, Phase, \ +from dolphin.models import Member, Dailyreport, Workflow, Skill, Group, Phase, \ Release, Project, Issue, Item from dolphin.tests.helpers import LocalApplicationTestCase, oauth_mockup_server -class TestTimecard(LocalApplicationTestCase): +class TestDailyreport(LocalApplicationTestCase): @classmethod @AuditLogContext(dict()) @@ -75,39 +75,32 @@ def mockup(cls): ) session.add(item) - cls.timecard = Timecard( - start_date=datetime.datetime.now().isoformat(), - end_date=datetime.datetime.now().isoformat(), - estimated_time=3, - summary='The summary for a time card', + cls.dailyreport = Dailyreport( + date=datetime.datetime.now().date(), + hours=3, + note='The note for a daily report', item=item, ) - session.add(cls.timecard) + session.add(cls.dailyreport) session.commit() def test_update(self): self.login(self.member.email) - start_date = datetime.datetime.now().isoformat() - end_date = datetime.datetime.now().isoformat() - summary = 'Some summary' + form = dict( + hours=4, + note='Some note' + ) with oauth_mockup_server(), self.given( - f'Updating a timecard', - f'/apiv1/timecards/id: {self.timecard.id}', + f'Updating a dailyreport', + f'/apiv1/dailyreports/id: {self.dailyreport.id}', f'UPDATE', - json=dict( - startDate=start_date, - endDate=end_date, - estimatedTime=2, - summary=summary, - ), + json=form, ): assert status == 200 assert response.json['id'] is not None - assert response.json['startDate'] == start_date - assert response.json['endDate'] == end_date - assert response.json['estimatedTime'] == 2 - assert response.json['summary'] == summary + assert response.json['hours'] == form['hours'] + assert response.json['note'] == form['note'] when('Trying to pass without form parameters', json={}) assert status == '708 Empty Form' @@ -119,59 +112,29 @@ def test_update(self): assert status == 404 when( - 'Intended timecard with integer type not found', - url_parameters=dict(id=0) - ) - assert status == 404 - - when( - 'Summary length is less than limit', - json=given | dict(summary=(1024 + 1) * 'a'), - ) - assert status == '902 At Most 1024 Characters Are Valid For ' \ - 'Summary' - - when( - 'Estimated time type is wrong', - json=given | dict(estimatedTime='time'), + 'Invalid parameter is in form', + json=given | dict(parameter='invalid parameter') ) - assert status == '900 Invalid Estimated Time Type' + assert status == '707 Invalid field, only following fields are ' \ + 'accepted: hours, note' when( - 'Start date format is wrong', - json=given | dict(startDate='30-20-20') + 'Note length is less than limit', + json=given | dict(note=(1024 + 1) * 'a'), ) - assert status == '791 Invalid Start Date Format' + assert status == '902 At Most 1024 Characters Are Valid For Note' when( - 'End date format is wrong', - json=given | dict(endDate='30-20-20') + 'Hours value type is wrong', + json=given | dict(hours='a') ) - assert status == '790 Invalid End Date Format' - - when('Start date is null', json=given | dict(startDate=None)) - assert status == '905 Start Date Is Null' - - when('End date is null', json=given | dict(endDate=None)) - assert status == '906 End Date Is Null' - - when( - 'Estimated time is null', - json=given | dict(estimatedTime=None) - ) - assert status == '904 Estimated Time Is Null' - - when('Summary is null', json=given | dict(summary=None)) - assert status == '903 Summary Is Null' + assert status == '900 Invalid Hours Type' when( - 'End date must be greater than start date', - json=given | dict( - startDate=end_date, - endDate=start_date, - ) + 'Hours value is less then 1', + json=given | dict(hours=0) ) - assert status == '657 End Date Must Be Greater Than Start Date' + assert status == '914 Hours Must Be Greater Than 0' when('Request is not authorized', authorization=None) assert status == 401 diff --git a/dolphin/validators.py b/dolphin/validators.py index f6b238af..11f2d6a0 100644 --- a/dolphin/validators.py +++ b/dolphin/validators.py @@ -6,11 +6,11 @@ from .exceptions import StatusResourceNotFound, StatusRepetitiveTitle, \ StatusRelatedIssueNotFound, StatusEventTypeNotFound, \ StatusInvalidStartDateFormat, StatusInvalidEndDateFormat, \ - StatusLimitedCharecterForSummary, StatusInvalidEstimatedTimeType, \ + StatusLimitedCharecterForNote, StatusHoursMustBeGreaterThanZero, \ StatusSummaryNotInForm, StatusEstimatedTimeNotInForm, \ - StatusEndDateNotInForm, StatusStartDateNotInForm, StatusSummaryIsNull, \ + StatusEndDateNotInForm, StatusStartDateNotInForm, StatusNoteIsNull, \ StatusEstimatedTimeIsNull, StatusStartDateIsNull, StatusEndDateIsNull, \ - StatusRepeatNotInForm + StatusRepeatNotInForm, StatusInvalidHoursType from .models import * from .models.organization import roles @@ -913,26 +913,13 @@ def item_exists_validator(item_id, container, field): ) -timecard_create_validator = validate( - summary=dict( - required=StatusSummaryNotInForm, - max_length=(1024, StatusLimitedCharecterForSummary), - not_none=StatusSummaryIsNull, +dailyreport_create_validator = validate( + note=dict( + max_length=(1024, StatusLimitedCharecterForNote), ), - startDate=dict( - required=StatusStartDateNotInForm, - pattern=(DATETIME_PATTERN, StatusInvalidStartDateFormat), - not_none=StatusStartDateIsNull, - ), - endDate=dict( - required=StatusEndDateNotInForm, - pattern=(DATETIME_PATTERN, StatusInvalidEndDateFormat), - not_none=StatusEndDateIsNull, - ), - estimatedTime=dict( - required=StatusEstimatedTimeNotInForm, - type_=(int, StatusInvalidEstimatedTimeType), - not_none=StatusEstimatedTimeIsNull, + hours=dict( + type_=(int, StatusInvalidHoursType), + minimum=(1, StatusHoursMustBeGreaterThanZero) ), itemId=dict( required='732 Item Id Not In Form', @@ -942,22 +929,13 @@ def item_exists_validator(item_id, container, field): ) -timecard_update_validator = validate( - summary=dict( - max_length=(1024, StatusLimitedCharecterForSummary), - not_none=StatusSummaryIsNull, - ), - startDate=dict( - pattern=(DATETIME_PATTERN, StatusInvalidStartDateFormat), - not_none=StatusStartDateIsNull, - ), - endDate=dict( - pattern=(DATETIME_PATTERN, StatusInvalidEndDateFormat), - not_none=StatusEndDateIsNull, +dailyreport_update_validator = validate( + note=dict( + max_length=(1024, StatusLimitedCharecterForNote), ), - estimatedTime=dict( - type_=(int, StatusInvalidEstimatedTimeType), - not_none=StatusEstimatedTimeIsNull, + hours=dict( + type_=(int, StatusInvalidHoursType), + minimum=(1, StatusHoursMustBeGreaterThanZero) ), )