diff --git a/pb/__init__.py b/pb/__init__.py index 1740b99..c8d5bdf 100644 --- a/pb/__init__.py +++ b/pb/__init__.py @@ -130,7 +130,7 @@ def site_map(): # ************************** # WEB FORMS # ************************** -from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm +from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm, ConfirmDeleteForm from pb.models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \ StudyDetails, StudyDetailsSchema @@ -156,7 +156,7 @@ def new_study(): study = Study() study.study_details = StudyDetails() _update_study(study, form) - flash('Study created successfully!') + flash('Study created successfully!', 'success') return redirect_home() return render_template( @@ -182,7 +182,7 @@ def edit_study(study_id): form.Q_COMPLETE.checked = True if request.method == 'POST': _update_study(study, form) - flash('Study updated successfully!') + flash('Study updated successfully!', 'success') return redirect_home() return render_template( 'form.html', @@ -205,7 +205,7 @@ def new_investigator(study_id): investigator.set_type(form.INVESTIGATORTYPE.data) db.session.add(investigator) db.session.commit() - flash('Investigator created successfully!') + flash('Investigator created successfully!', 'success') return redirect_home() return render_template( @@ -218,21 +218,81 @@ def new_investigator(study_id): ) -@app.route('/del_investigator/', methods=['GET']) +@app.route('/del_investigator/', methods=['GET', 'POST']) def del_investigator(inv_id): - db.session.query(Investigator).filter(Investigator.id == inv_id).delete() - db.session.commit() + inv_id = int(inv_id) + inv_model = db.session.query(Investigator).filter(Investigator.id == inv_id).first() + if inv_model is None: + flash('Investigator not found.', 'warn') + return redirect_home() + + uid = inv_model.NETBADGEID + study_id = int(inv_model.STUDYID) + form = ConfirmDeleteForm(request.form, obj=inv_model) + + if request.method == 'GET': + action = BASE_HREF + "/del_investigator/%i" % inv_id + title = "Delete Investigator #%i?" % inv_id + details = "Are you sure you want to delete Investigator '%s' from Study %i?" % (uid, study_id) + + return render_template( + 'form.html', + form=form, + action=action, + title=title, + details=details, + description_map=description_map, + base_href=BASE_HREF + ) + + if request.method == 'POST': + if form.confirm and form.confirm.data: + db.session.query(Investigator).filter(Investigator.id == inv_id).delete() + db.session.commit() + flash('Investigator %s deleted from Study %i.' % (uid, study_id), 'success') + else: + flash('Delete canceled.', 'info') + return redirect_home() -@app.route('/del_study/', methods=['GET']) +@app.route('/del_study/', methods=['GET', 'POST']) def del_study(study_id): - db.session.query(RequiredDocument).filter(RequiredDocument.STUDYID == study_id).delete() - db.session.query(Investigator).filter(Investigator.STUDYID == study_id).delete() - db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete() - db.session.query(Study).filter(Study.STUDYID == study_id).delete() - db.session.commit() - return redirect_home() + study_id = int(study_id) + study_model = db.session.query(Study).filter(Study.STUDYID == study_id).first() + if study_model is None: + flash('Study not found.', 'warn') + return redirect_home() + + form = ConfirmDeleteForm(request.form, obj=study_model) + + if request.method == 'GET': + action = BASE_HREF + "/del_study/%i" % study_id + title = "Delete Study #%i?" % study_id + details = "Are you sure you want to delete Study '%s'?" % study_model.TITLE + + return render_template( + 'form.html', + form=form, + action=action, + title=title, + details=details, + description_map=description_map, + base_href=BASE_HREF + ) + + if request.method == 'POST': + if form.confirm and form.confirm.data: + db.session.query(RequiredDocument).filter(RequiredDocument.STUDYID == study_id).delete() + db.session.query(Investigator).filter(Investigator.STUDYID == study_id).delete() + db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete() + db.session.query(Study).filter(Study.STUDYID == study_id).delete() + db.session.commit() + flash('Study %i deleted.' % study_id, 'success') + else: + flash('Delete canceled.', 'info') + + return redirect_home() def _update_study(study, form): @@ -274,7 +334,7 @@ def study_details(study_id): form.populate_obj(study_details) db.session.add(study_details) db.session.commit() - flash('Study updated successfully!') + flash('Study updated successfully!', 'success') return redirect_home() return render_template( 'form.html', diff --git a/pb/forms.py b/pb/forms.py index b40b64a..5546a6a 100644 --- a/pb/forms.py +++ b/pb/forms.py @@ -28,6 +28,10 @@ class StudyDetailsForm(ModelForm, FlaskForm): model = StudyDetails +class ConfirmDeleteForm(FlaskForm): + confirm = BooleanField('Yes, really delete', default='checked', + false_values=(False, 'false', 0, '0')) + class RequirementsTable(Table): AUXDOCID = Col('Code') AUXDOC = Col('Name') @@ -51,11 +55,6 @@ class StudyTable(Table): anchor_attrs={'class': 'btn btn-icon btn-primary', 'title': 'Edit Study'}, th_html_attrs={'class': 'mat-icon text-center', 'title': 'Edit Study'} ) - delete = LinkCol( - 'delete', 'del_study', url_kwargs=dict(study_id='STUDYID'), - anchor_attrs={'class': 'btn btn-icon btn-warn', 'title': 'Delete Study'}, - th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Study'} - ) details = LinkCol( 'ballot', 'study_details', url_kwargs=dict(study_id='STUDYID'), anchor_attrs={'class': 'btn btn-icon btn-default', 'title': 'Edit Questions'}, @@ -73,4 +72,9 @@ class StudyTable(Table): Q_COMPLETE = BoolCol('Complete?') requirements = NestedTableCol('Requirements', RequirementsTable) investigators = NestedTableCol('Investigators', InvestigatorsTable) + delete = LinkCol( + 'delete', 'del_study', url_kwargs=dict(study_id='STUDYID'), + anchor_attrs={'class': 'btn btn-icon btn-warn', 'title': 'Delete Study'}, + th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Study'} + ) diff --git a/pb/static/scss/app.scss b/pb/static/scss/app.scss index 5a04bb1..ede0626 100644 --- a/pb/static/scss/app.scss +++ b/pb/static/scss/app.scss @@ -22,6 +22,11 @@ $color-warn: #DF1E43; $color-warn-light: scale-color($color-warn, $lightness: +30%); $color-warn-dark: scale-color($color-warn, $lightness: -30%); +// success (Green) +$color-success: #64B343; +$color-success-light: scale-color($color-success, $lightness: +30%); +$color-success-dark: scale-color($color-success, $lightness: -30%); + $font-size-default: 16px; $font-size-lg: 24px; $font-size-md: 16px; @@ -242,3 +247,42 @@ select.multi { } } +.alert { + padding: 20px; + background-color: $color-gray; + color: white; + margin-bottom: 15px; + opacity: 1; + border-radius: 5px; + transform: scale3d(1, 1, 1); + transition: all 0.5s ease-in-out; + + &.warn { background-color: $color-warn; } + &.success { background-color: $color-success; } + &.info { background-color: $color-gray-light; color: black; } + + .btn-close { + margin-left: 15px; + color: white; + font-weight: bold; + float: right; + font-size: 22px; + line-height: 20px; + cursor: pointer; + transition: 0.3s; + + &:hover { + color: black; + } + } + + &.fade-out { + opacity: 0; + } + + &.shrink { + transform: scale3d(0, 0, 0); + padding: 0; + margin: 0; + } +} diff --git a/templates/index.html b/templates/index.html index a5e2264..93cd6bf 100644 --- a/templates/index.html +++ b/templates/index.html @@ -17,19 +17,31 @@ New Study

-{% with messages = get_flashed_messages() %} +{% with messages = get_flashed_messages(with_categories=True) %} {% if messages %} -
    - {% for message in messages %} -
  • {{ message }}
  • - {% endfor %} -
+ {% for category, message in messages %} +
+ × + {{ message }} +
+ {% endfor %} {% endif %} {% endwith %}

Current Studies

{{ table }} + + + - -