-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding shift models * Updated tests * Added shift tests * Shift system implemented, still needs tests * Added shift tests * Sped up tests * Added migration for shift db * Adding docs * Changed path to includes * Rearranged
- Loading branch information
Showing
20 changed files
with
1,764 additions
and
92 deletions.
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
backend/migrations/versions/7a98a0c9fb44_added_shift_management.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
"""Added shift management | ||
Revision ID: 7a98a0c9fb44 | ||
Revises: 4affc388b48b | ||
Create Date: 2020-05-22 16:43:21.650563 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = '7a98a0c9fb44' | ||
down_revision = '4affc388b48b' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.create_table('schedule', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('name', sa.String(), nullable=True), | ||
sa.Column('description', sa.String(), nullable=True), | ||
sa.Column('event', sa.Integer(), nullable=True), | ||
sa.Column('tags', sa.JSON(), nullable=True), | ||
sa.ForeignKeyConstraint(['event'], ['event.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('job', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('name', sa.String(), nullable=True), | ||
sa.Column('description', sa.String(), nullable=True), | ||
sa.Column('event', sa.Integer(), nullable=True), | ||
sa.Column('documentation', sa.String(), nullable=True), | ||
sa.Column('department', sa.Integer(), nullable=True), | ||
sa.Column('method', sa.JSON(), nullable=True), | ||
sa.Column('signuprules', sa.JSON(), nullable=True), | ||
sa.Column('sticky', sa.Boolean(), nullable=True), | ||
sa.ForeignKeyConstraint(['department'], ['department.id'], ), | ||
sa.ForeignKeyConstraint(['event'], ['event.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('schedule_event', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('name', sa.String(), nullable=True), | ||
sa.Column('description', sa.String(), nullable=True), | ||
sa.Column('starttime', sa.DateTime(), nullable=True), | ||
sa.Column('duration', sa.Float(), nullable=True), | ||
sa.Column('schedule', sa.Integer(), nullable=True), | ||
sa.ForeignKeyConstraint(['schedule'], ['schedule.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('job_role_association', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('job', sa.Integer(), nullable=True), | ||
sa.Column('role', sa.Integer(), nullable=True), | ||
sa.ForeignKeyConstraint(['job'], ['job.id'], ), | ||
sa.ForeignKeyConstraint(['role'], ['role.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('job_schedule_association', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('job', sa.Integer(), nullable=True), | ||
sa.Column('schedule', sa.Integer(), nullable=True), | ||
sa.ForeignKeyConstraint(['job'], ['job.id'], ), | ||
sa.ForeignKeyConstraint(['schedule'], ['schedule.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('job_schedule_event_association', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('job', sa.Integer(), nullable=True), | ||
sa.Column('schedule_event', sa.Integer(), nullable=True), | ||
sa.ForeignKeyConstraint(['job'], ['job.id'], ), | ||
sa.ForeignKeyConstraint(['schedule_event'], ['schedule_event.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('shift', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('job', sa.Integer(), nullable=True), | ||
sa.Column('schedule', sa.Integer(), nullable=True), | ||
sa.Column('schedule_event', sa.Integer(), nullable=True), | ||
sa.Column('starttime', sa.DateTime(), nullable=True), | ||
sa.Column('duration', sa.Float(), nullable=True), | ||
sa.Column('slots', sa.Integer(), nullable=True), | ||
sa.Column('filledslots', sa.Integer(), nullable=True), | ||
sa.Column('weighting', sa.Float(), nullable=True), | ||
sa.ForeignKeyConstraint(['job'], ['job.id'], ), | ||
sa.ForeignKeyConstraint(['schedule'], ['schedule.id'], ), | ||
sa.ForeignKeyConstraint(['schedule_event'], ['schedule_event.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('shift_assignment', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('badge', sa.Integer(), nullable=True), | ||
sa.Column('shift', sa.Integer(), nullable=True), | ||
sa.ForeignKeyConstraint(['badge'], ['badge.id'], ), | ||
sa.ForeignKeyConstraint(['shift'], ['shift.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
op.create_table('shift_signup', | ||
sa.Column('id', sa.Integer(), nullable=False), | ||
sa.Column('badge', sa.Integer(), nullable=True), | ||
sa.Column('job', sa.Integer(), nullable=True), | ||
sa.Column('shift', sa.Integer(), nullable=True), | ||
sa.Column('schedule', sa.Integer(), nullable=True), | ||
sa.Column('schedule_event', sa.Integer(), nullable=True), | ||
sa.Column('starttime', sa.DateTime(), nullable=True), | ||
sa.Column('duration', sa.Float(), nullable=True), | ||
sa.Column('signuptime', sa.DateTime(), nullable=True), | ||
sa.ForeignKeyConstraint(['badge'], ['badge.id'], ), | ||
sa.ForeignKeyConstraint(['job'], ['job.id'], ), | ||
sa.ForeignKeyConstraint(['schedule'], ['schedule.id'], ), | ||
sa.ForeignKeyConstraint(['schedule_event'], ['schedule_event.id'], ), | ||
sa.ForeignKeyConstraint(['shift'], ['shift.id'], ), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
with op.batch_alter_table('badge', schema=None) as batch_op: | ||
batch_op.alter_column('badge_type', | ||
existing_type=sa.INTEGER(), | ||
nullable=True, | ||
existing_server_default=sa.text('1')) | ||
batch_op.alter_column('email', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=True) | ||
batch_op.alter_column('first_name', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=True) | ||
batch_op.alter_column('last_name', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=True) | ||
batch_op.alter_column('printed_name', | ||
existing_type=sa.VARCHAR(length=256), | ||
nullable=True) | ||
batch_op.alter_column('search_name', | ||
existing_type=sa.VARCHAR(length=256), | ||
nullable=True) | ||
|
||
# ### end Alembic commands ### | ||
|
||
|
||
def downgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
with op.batch_alter_table('badge', schema=None) as batch_op: | ||
batch_op.alter_column('search_name', | ||
existing_type=sa.VARCHAR(length=256), | ||
nullable=False) | ||
batch_op.alter_column('printed_name', | ||
existing_type=sa.VARCHAR(length=256), | ||
nullable=False) | ||
batch_op.alter_column('last_name', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=False) | ||
batch_op.alter_column('first_name', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=False) | ||
batch_op.alter_column('email', | ||
existing_type=sa.VARCHAR(length=128), | ||
nullable=False) | ||
batch_op.alter_column('badge_type', | ||
existing_type=sa.INTEGER(), | ||
nullable=False, | ||
existing_server_default=sa.text('1')) | ||
|
||
op.drop_table('shift_signup') | ||
op.drop_table('shift_assignment') | ||
op.drop_table('shift') | ||
op.drop_table('job_schedule_event_association') | ||
op.drop_table('job_schedule_association') | ||
op.drop_table('job_role_association') | ||
op.drop_table('schedule_event') | ||
op.drop_table('job') | ||
op.drop_table('schedule') | ||
# ### end Alembic commands ### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
from util import * | ||
import datetime | ||
import json | ||
|
||
def test_schedule_change(client): | ||
"""Make sure that creating an scheduleevent on a schedule creates a shift on the associated job""" | ||
schedule = client.post("/api/events/1/schedules", json={ | ||
"name": "Shift Schedule", | ||
"description": "A schedule for shifts!" | ||
}).json | ||
assert(schedule['name'] == "Shift Schedule") | ||
|
||
eventstart = datetime.datetime.utcnow() | ||
scheduleevent = client.post("/api/scheduleevents", json={ | ||
"name": "The big panel", | ||
"description": "You know the one", | ||
"starttime": eventstart.isoformat(), | ||
"duration": 3600, | ||
"schedule": schedule['id'] | ||
}).json | ||
assert(scheduleevent['name'] == "The big panel") | ||
|
||
job = client.post("/api/events/1/jobs", json={ | ||
"name": "Do a thing", | ||
"description": "Help make the thing happen, at the place", | ||
"documentation": "Here's how to do the thing", | ||
"method": { | ||
"name": "copy", | ||
"slots": 4 | ||
}, | ||
"schedules": [ | ||
schedule['id'] | ||
] | ||
}).json | ||
assert(job['name'] == "Do a thing") | ||
|
||
shifts = client.get("/api/shifts", query_string={"full": True}).json | ||
assert(len(shifts) == 1) | ||
assert(shifts[0]['starttime'] == scheduleevent['starttime']) | ||
assert(shifts[0]['duration'] == scheduleevent['duration']) | ||
assert(shifts[0]['slots'] == 4) | ||
|
||
newevent = client.post("/api/scheduleevents", json={ | ||
"name": "Another cool panel", | ||
"description": "You probably don't know about this one yet", | ||
"starttime": (eventstart + datetime.timedelta(seconds=3600)).isoformat(), | ||
"duration": 3600, | ||
"schedule": schedule['id'] | ||
}).json | ||
assert(newevent['name'] == "Another cool panel") | ||
|
||
shifts = client.get("/api/shifts", query_string={"full": True}).json | ||
assert(len(shifts) == 2) | ||
|
||
client.delete("/api/scheduleevents/"+str(scheduleevent['id'])) | ||
|
||
events = client.get("/api/scheduleevents", query_string={"full": True}).json | ||
assert(len(events) == 1) | ||
|
||
shifts = client.get("/api/shifts", query_string={"full": True}).json | ||
assert(len(shifts) == 1) | ||
|
||
def test_signup_persistence(client): | ||
"""Make sure that a signup survives a shift being deleted and coming back""" | ||
schedule = client.post("/api/events/1/schedules", json={ | ||
"name": "Shift Schedule", | ||
"description": "A schedule for shifts!" | ||
}).json | ||
assert(schedule['name'] == "Shift Schedule") | ||
|
||
eventstart = datetime.datetime.utcnow() | ||
scheduleevent = client.post("/api/scheduleevents", json={ | ||
"name": "The big panel", | ||
"description": "You know the one", | ||
"starttime": eventstart.isoformat(), | ||
"duration": 3600, | ||
"schedule": schedule['id'] | ||
}).json | ||
assert(scheduleevent['name'] == "The big panel") | ||
|
||
department = client.post("/api/events/1/departments", json={ | ||
"name": "Product Testing" | ||
}).json | ||
assert(department['name'] == "Product Testing") | ||
|
||
role = client.post("/api/roles", json={ | ||
"name": "Manager", | ||
"description": "Allowed to sign up for manager shifts", | ||
"event": 1 | ||
}).json | ||
assert(role['name'] == "Manager") | ||
|
||
user = client.post("/api/users", json={ | ||
"username": "testing", | ||
"email": "test@test.com", | ||
"active": True | ||
}).json | ||
assert(user['username'] == "testing") | ||
|
||
grant = client.post("/api/grants", json={ | ||
"user": user['id'], | ||
"role": role['id'], | ||
"department": department['id'] | ||
}).json | ||
assert(grant['user'] == user['id']) | ||
|
||
job = client.post("/api/events/1/jobs", json={ | ||
"name": "Do a thing", | ||
"description": "Help make the thing happen, at the place", | ||
"documentation": "Here's how to do the thing", | ||
"method": { | ||
"name": "copy", | ||
"slots": 4 | ||
}, | ||
"schedules": [ | ||
schedule['id'] | ||
], | ||
"roles": [ | ||
role['id'] | ||
], | ||
"department": department['id'] | ||
}).json | ||
assert(job['name'] == "Do a thing") | ||
assert(job['department'] == department['id']) | ||
|
||
badge = client.post("/api/events/1/badges", json={ | ||
"legal_name": "Test User", | ||
"user": user['id'], | ||
"departments": [ | ||
department['id'] | ||
] | ||
}).json | ||
assert(badge['legal_name'] == "Test User") | ||
|
||
jobs = client.get("/api/events/1/jobs/available", query_string={"badge": badge['id']}).json | ||
assert(len(jobs) == 1) | ||
assert(len(jobs[0]['shifts']) == 1) | ||
|
||
signup = client.post("/api/events/1/shifts/"+str(jobs[0]['shifts'][0]['id'])+"/signup", json={ | ||
"badge": badge['id'] | ||
}).json | ||
assert(signup['shift']) | ||
|
||
assignedshifts = client.get("/api/shiftassignments", query_string={"badge": badge['id']}).json | ||
assert(len(assignedshifts) == 1) | ||
|
||
client.delete("/api/scheduleevents/"+str(scheduleevent['id'])) | ||
|
||
assignedshifts = client.get("/api/shiftassignments", query_string={"badge": badge['id']}).json | ||
assert(len(assignedshifts) == 0) | ||
|
||
replacement_scheduleevent = client.post("/api/scheduleevents", json={ | ||
"name": "A similar large panel", | ||
"description": "It starts the same time as the old one, and is just as long!", | ||
"starttime": eventstart.isoformat(), | ||
"duration": 3600, | ||
"schedule": schedule['id'] | ||
}).json | ||
assert(replacement_scheduleevent['name'] == "A similar large panel") | ||
|
||
assignedshifts = client.get("/api/shiftassignments", query_string={"badge": badge['id']}).json | ||
assert(len(assignedshifts) == 1) |
Oops, something went wrong.