Repository: fossasia/open-event-server

Issue:
Implement Withdraw proposal feature for speakers
Currently we do not have a withdraw proposal option for speakers.


Discussion:
@mariobehling when a speaker _withdraws_ his proposal, should it be deleted or reverted back to the draft state ?

Resolving patch:
diff --git a/app/helpers/data_getter.py b/app/helpers/data_getter.py
--- a/app/helpers/data_getter.py
+++ b/app/helpers/data_getter.py
@@ -176,14 +176,14 @@ def get_sessions_by_event_id(event_id):
         """
         :return: All Sessions with correct event_id
         """
-        return Session.query.filter_by(event_id=event_id)
+        return Session.query.filter_by(event_id=event_id).filter(Session.in_trash == False)
 
     @staticmethod
     def get_sessions_by_state(state):
         """
         :return: All Sessions with correct event_id
         """
-        return Session.query.filter(Session.state == state).filter(Session.in_trash is not True)
+        return Session.query.filter(Session.state == state).filter(Session.in_trash == False)
 
     @staticmethod
     def get_sessions_by_state_and_event_id(state, event_id):
@@ -196,7 +196,7 @@ def get_sessions_by_state_and_event_id(state, event_id):
 
     @staticmethod
     def get_all_sessions():
-        return Session.query.filter(Session.in_trash is not True).all()
+        return Session.query.filter(Session.in_trash == False).all()
 
     @staticmethod
     def get_tracks(event_id):
@@ -223,7 +223,7 @@ def get_sessions(event_id, state='accepted'):
         return Session.query.filter_by(
             event_id=event_id,
             state=state
-        )
+        ).filter(Session.in_trash == False)
 
     @staticmethod
     def get_image_sizes():
@@ -263,7 +263,7 @@ def get_sessions_of_user_by_id(session_id, user=login.current_user):
         """
         try:
             return Session.query.filter(Session.speakers.any(Speaker.user_id == user.id)).filter(
-                Session.id == session_id).one()
+                Session.id == session_id).filter(Session.in_trash == False).one()
         except MultipleResultsFound:
             return None
         except NoResultFound:
@@ -276,10 +276,10 @@ def get_sessions_of_user(upcoming_events=True):
         """
         if upcoming_events:
             return Session.query.filter(Session.speakers.any(Speaker.user_id == login.current_user.id)).filter(
-                Session.start_time >= datetime.datetime.now())
+                Session.start_time >= datetime.datetime.now()).filter(Session.in_trash == False)
         else:
             return Session.query.filter(Session.speakers.any(Speaker.user_id == login.current_user.id)).filter(
-                Session.start_time < datetime.datetime.now())
+                Session.start_time < datetime.datetime.now()).filter(Session.in_trash == False)
 
     @staticmethod
     def get_all_sessions_of_user(upcoming_events=True):
@@ -287,9 +287,9 @@ def get_all_sessions_of_user(upcoming_events=True):
         :return: Return all Sessions objects with the current user as a speaker
         """
         if upcoming_events:
-            return Session.query.filter(Event.state != 'Completed')
+            return Session.query.filter(Event.state != 'Completed').filter(Session.in_trash == False)
         else:
-            return Session.query.filter(Event.state == 'Completed')
+            return Session.query.filter(Event.state == 'Completed').filter(Session.in_trash == False)
 
     @staticmethod
     def get_speakers(event_id):
@@ -855,15 +855,15 @@ def get_all_user_roles(role_name):
 
     @staticmethod
     def get_all_accepted_sessions():
-        return get_count(Session.query.filter_by(state='accepted'))
+        return get_count(Session.query.filter_by(state='accepted').filter(Session.in_trash == False))
 
     @staticmethod
     def get_all_rejected_sessions():
-        return get_count(Session.query.filter_by(state='rejected'))
+        return get_count(Session.query.filter_by(state='rejected').filter(Session.in_trash == False))
 
     @staticmethod
     def get_all_draft_sessions():
-        return get_count(Session.query.filter_by(state='pending'))
+        return get_count(Session.query.filter_by(state='pending').filter(Session.in_trash == False))
 
     @staticmethod
     def get_email_by_times():
diff --git a/app/views/admin/models_views/my_sessions.py b/app/views/admin/models_views/my_sessions.py
--- a/app/views/admin/models_views/my_sessions.py
+++ b/app/views/admin/models_views/my_sessions.py
@@ -1,4 +1,5 @@
 import json
+from datetime import datetime
 
 import flask_login
 from flask import flash, redirect, url_for, request
@@ -6,7 +7,7 @@
 from flask_admin import BaseView, expose
 from markupsafe import Markup
 
-from app.helpers.data import DataManager
+from app.helpers.data import DataManager, save_to_db
 from app.views.admin.models_views.events import is_verified_user
 from ....helpers.data_getter import DataGetter
 
@@ -79,3 +80,13 @@ def process_session_view(self, session_id):
             DataManager.edit_session(request, session)
             flash("The session has been updated successfully", "success")
             return redirect(url_for('.display_session_view', session_id=session_id))
+
+    @expose('/<int:session_id>/withdraw/', methods=('GET',))
+    @flask_login.login_required
+    def withdraw_session_view(self, session_id):
+        session = DataGetter.get_sessions_of_user_by_id(session_id)
+        session.in_trash = True
+        session.trash_date = datetime.now()
+        save_to_db(session)
+        flash("The session has been withdrawn", "success")
+        return redirect(url_for('.display_my_sessions_view', session_id=session_id))


Test patch: