Skip to content

Commit

Permalink
Refs #254. Implemented POST for participant sessions API.
Browse files Browse the repository at this point in the history
  • Loading branch information
SBriere committed Jul 8, 2024
1 parent 1dbf593 commit 9ea0332
Show file tree
Hide file tree
Showing 15 changed files with 845 additions and 283 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraDeviceParticipant import TeraDeviceParticipant
from opentera.db.models.TeraSessionType import TeraSessionType
from opentera.db.models.TeraSession import TeraSession

from sqlalchemy import func

import datetime

class DBManagerTeraParticipantAccess:
def __init__(self, participant: TeraParticipant):
Expand Down Expand Up @@ -46,3 +51,36 @@ def get_accessible_services(self):

return [service_project.service_project_service for service_project in service_projects]

def get_accessible_session_types(self):
session_types = TeraSessionType.query.join(TeraSessionType.session_type_projects)\
.filter_by(id_project=self.participant.id_project).all()

return session_types

def get_accessible_session_types_ids(self):
types = []
for my_type in self.get_accessible_session_types():
types.append(my_type.id_session_type)
return types

def query_existing_session(self, session_name: str, session_type_id: int, session_date: datetime,
participant_uuids: list):
sessions = TeraSession.query.filter(TeraSession.id_creator_participant == self.participant.id_participant).\
filter(TeraSession.session_name == session_name).filter(TeraSession.id_session_type == session_type_id).\
filter(func.date(TeraSession.session_start_datetime) == session_date.date()).\
order_by(TeraSession.id_session.asc()).all()

for session in sessions:
sessions_participants_uuids = set([part.participant_uuid for part in session.session_participants])
if set(participant_uuids) == sessions_participants_uuids:
# We have a match on that session participants too
return session
return None

def get_accessible_sessions(self):
query = TeraSession.query.filter(TeraSession.id_creator_participant == self.participant.id_participant)
return query.all()

def get_accessible_sessions_ids(self):
sessions = self.get_accessible_sessions()
return [session.id_session for session in sessions]
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from flask_restx import Resource, fields, inputs
from flask_babel import gettext
from modules.LoginModule.LoginModule import participant_multi_auth, current_participant
from modules.DatabaseModule.DBManager import DBManager
from modules.FlaskModule.FlaskModule import participant_api_ns as api
from opentera.redis.RedisVars import RedisVars
from opentera.redis.RedisRPCClient import RedisRPCClient
Expand Down Expand Up @@ -99,6 +100,14 @@ def get(self):
if websocket_url:
reply["websocket_url"] = websocket_url

# Reply accessible sessions type ids
participant_access = DBManager.participantAccess(current_participant)
session_types = participant_access.get_accessible_session_types()
reply['session_types_info'] = list()

for st in session_types:
reply['session_types_info'].append(st.to_json(minimal=True))

# Set token according to API access (http auth is full access, token is not)
if current_participant.fullAccess:
token_key = self.module.redisGet(RedisVars.RedisVar_ParticipantTokenAPIKey)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from flask import session
from flask import request
from flask_restx import Resource, inputs
from flask_babel import gettext
from sqlalchemy import exc
from modules.LoginModule.LoginModule import participant_multi_auth, current_participant
from modules.FlaskModule.FlaskModule import participant_api_ns as api
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraSession import TeraSession
from modules.DatabaseModule.DBManager import DBManager

import datetime

# Parser definition(s)
get_parser = api.parser()
get_parser.add_argument('id_session', type=int, help='ID of the session to query')
Expand All @@ -19,7 +22,43 @@
get_parser.add_argument('start_date', type=inputs.date, help='Start date, sessions before that date will be ignored')
get_parser.add_argument('end_date', type=inputs.date, help='End date, sessions after that date will be ignored')

post_parser = api.parser()
session_schema = api.schema_model('device_session', {
'properties': {
'session': {
'type': 'object',
'properties': {
'id_session': {
'type': 'integer'
},
'session_participants': {
'type': 'array',
'uniqueItems': True,
'items': {
'type': 'string',
'format': 'uuid'
}
},
'id_session_type': {
'type': 'integer'
},
'session_name': {
'type': 'string'
},
'session_status': {
'type': 'integer'
},
'session_start_datetime': {
'type': 'string'
}
},
'required': ['id_session', 'session_participants',
'id_session_type', 'session_name', 'session_status', 'session_start_datetime']
},

},
'type': 'object',
'required': ['session']
})


class ParticipantQuerySessions(Resource):
Expand Down Expand Up @@ -60,14 +99,117 @@ def get(self):

return sessions_list

@api.doc(description='To be documented '
'To be documented',
responses={200: 'Success - To be documented',
500: 'Required parameter is missing',
501: 'Not implemented.',
403: 'Logged user doesn\'t have permission to access the requested data'},
@api.doc(description='Update/Create session',
responses={200: 'Success',
400: 'Required parameter is missing',
500: 'Internal server error',
501: 'Not implemented',
403: 'Logged participant doesn\'t have permission to access the requested data'},
params={'token': 'Secret token'})
@api.expect(post_parser)
@participant_multi_auth.login_required(role='full')
@api.expect(session_schema)
@participant_multi_auth.login_required(role='limited')
def post(self):
return gettext('Not implemented'), 501
# args = post_parser.parse_args()
# Using request.json instead of parser, since parser messes up the json!
if 'session' not in request.json:
return gettext('Missing session'), 400

json_session = request.json['session']

participant_access = DBManager.participantAccess(current_participant)

# Validate if we have an id
if 'id_session' not in json_session:
return gettext('Missing id_session value'), 400

# Validate if we have an id
if 'id_session_type' not in json_session:
return gettext('Missing id_session_type value'), 400

# Validate that we have people in a new sessions
if ('session_participants' not in json_session and 'session_users' not in json_session) \
and 'session_devices' not in json_session and json_session['id_session'] == 0:
return gettext('Missing session participants and/or users and/or devices'), 400

# We know we have a participant,avoid identity thief
json_session['id_creator_participant'] = current_participant.id_participant

# Validate session type
session_types = participant_access.get_accessible_session_types_ids()

if not json_session['id_session_type'] in session_types:
return gettext('No access to session type'), 403

# Check if a session of that type and name already exists. If so, don't create it, just returns it.
if json_session['id_session'] == 0:
if 'session_name' not in json_session:
return gettext('Missing argument \'session name\''), 400
if 'session_start_datetime' not in json_session:
return gettext('Missing argument \'session_start_datetime\''), 400

existing_session = participant_access.query_existing_session(
session_name=json_session['session_name'],
session_type_id=json_session['id_session_type'],
session_date=datetime.datetime.fromisoformat(json_session['session_start_datetime']),
participant_uuids=json_session['session_participants']
)

if existing_session:
json_session['id_session'] = existing_session.id_session
# Don't change session start datetime
json_session['session_start_datetime'] = existing_session.session_start_datetime.isoformat()
else:
# Existing session - check if we can access it
if json_session['id_session'] not in participant_access.get_accessible_sessions_ids():
return gettext('Unauthorized'), 403

# Do the update!
if json_session['id_session'] > 0:

# Already existing
# TODO handle participant list (remove, add) in session

try:
if 'session_participants' in json_session:
participants = json_session.pop('session_participants')

TeraSession.update(json_session['id_session'], json_session)
except exc.SQLAlchemyError as e:
import sys
print(sys.exc_info())
self.module.logger.log_error(self.module.module_name,
ParticipantQuerySessions.__name__,
'post', 500, 'Database error', str(e))
return gettext('Database error'), 500
else:
# New
try:
new_ses = TeraSession()
participants = json_session.pop('session_participants')
new_ses.from_json(json_session)

TeraSession.insert(new_ses)

for p_uuid in participants:
participant = TeraParticipant.get_participant_by_uuid(p_uuid)
if participant is None:
return gettext('Invalid participant uuid'), 400
new_ses.session_participants.append(participant)

if len(participants) > 0:
new_ses.commit() # Commits added participants

# Update ID for further use
json_session['id_session'] = new_ses.id_session

except exc.SQLAlchemyError as e:
import sys
print(sys.exc_info(), e)
self.module.logger.log_error(self.module.module_name,
ParticipantQuerySessions.__name__,
'post', 500, 'Database error', str(e))
return '', 500

update_session = TeraSession.get_session_by_id(json_session['id_session'])

return update_session.to_json()
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def create_defaults(test=False):
stp.id_project = project.id_project
TeraSessionTypeProject.db().session.add(stp)

project = TeraProject.get_project_by_projectname('Default Project #2')
stp = TeraSessionTypeProject()
stp.id_session_type = bureau_session.id_session_type
stp.id_project = project.id_project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-03-04 11:09-0500\n"
"POT-Creation-Date: 2024-05-13 11:02-0400\n"
"PO-Revision-Date: 2021-01-19 16:16-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
Expand All @@ -16,33 +16,93 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
"Generated-By: Babel 2.15.0\n"

#: API/QueryAssetFile.py:44 API/QueryAssetFile.py:242
msgid "Access denied to asset"
#: API/QueryArchiveFile.py:53 API/QueryArchiveFile.py:109
#: API/QueryArchiveFile.py:172 API/QueryArchiveFileInfos.py:64
#: API/QueryArchiveFileInfos.py:131
msgid "No archive found"
msgstr ""

#: API/QueryAssetFile.py:49 API/QueryAssetFileInfos.py:55
#: API/QueryAssetFileInfos.py:112
msgid "No asset found"
#: API/QueryArchiveFile.py:57 API/QueryArchiveFile.py:175
msgid "Access denied to the requested archive"
msgstr ""

#: API/QueryAssetFile.py:63
#: API/QueryArchiveFile.py:87 API/QueryAssetFile.py:63
msgid "Wrong content type"
msgstr ""

#: API/QueryAssetFile.py:66
msgid "Missing file asset information"
#: API/QueryArchiveFile.py:90
msgid "Missing archive information"
msgstr ""

#: API/QueryAssetFile.py:69
#: API/QueryArchiveFile.py:93 API/QueryAssetFile.py:69
msgid "Missing uploaded file"
msgstr ""

#: API/QueryAssetFile.py:74
#: API/QueryArchiveFile.py:98 API/QueryAssetFile.py:74
msgid "Missing filename"
msgstr ""

#: API/QueryArchiveFile.py:105
msgid "Missing archive ID"
msgstr ""

#: API/QueryArchiveFile.py:112
msgid "Archive has already been uploaded."
msgstr ""

#: API/QueryArchiveFile.py:115
msgid "Filename does not match the archive information"
msgstr ""

#: API/QueryArchiveFile.py:146
msgid "Error parsing archive information : "
msgstr ""

#: API/QueryArchiveFile.py:185
msgid "Archive deleted"
msgstr ""

#: API/QueryArchiveFileInfos.py:68 API/QueryArchiveFileInfos.py:72
#: API/QueryArchiveFileInfos.py:76 API/QueryArchiveFileInfos.py:81
msgid "Access denied for that archive"
msgstr ""

#: API/QueryArchiveFileInfos.py:91 API/QueryArchiveFileInfos.py:96
#: API/QueryAssetFileInfos.py:69
msgid "Badly formatted request"
msgstr ""

#: API/QueryArchiveFileInfos.py:99
msgid "Missing original filename"
msgstr ""

#: API/QueryArchiveFileInfos.py:102
msgid "Missing owner UUID"
msgstr ""

#: API/QueryArchiveFileInfos.py:119 API/QueryArchiveFileInfos.py:140
msgid "Error parsing archive information"
msgstr ""

#: API/QueryArchiveFileInfos.py:134
msgid "Missing archive status"
msgstr ""

#: API/QueryAssetFile.py:44 API/QueryAssetFile.py:242
msgid "Access denied to asset"
msgstr ""

#: API/QueryAssetFile.py:49 API/QueryAssetFileInfos.py:55
#: API/QueryAssetFileInfos.py:112
msgid "No asset found"
msgstr ""

#: API/QueryAssetFile.py:66
msgid "Missing file asset information"
msgstr ""

#: API/QueryAssetFile.py:80
msgid "Invalid file_asset format"
msgstr ""
Expand Down Expand Up @@ -72,10 +132,6 @@ msgstr ""
msgid "Access denied for that asset"
msgstr ""

#: API/QueryAssetFileInfos.py:69
msgid "Badly formatted request"
msgstr ""

#: API/QueryAssetFileInfos.py:77
msgid "Missing access token for at least one requested asset"
msgstr ""
Expand Down
Loading

0 comments on commit 9ea0332

Please sign in to comment.