fixed and extended apis

This commit is contained in:
Tobias Kurze
2019-04-26 15:34:23 +02:00
parent 07d01304be
commit 295aadfaeb
9 changed files with 409 additions and 21 deletions

34
.idea/backend.iml generated Normal file
View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="Flask">
<option name="enabled" value="true" />
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/auth/templates" />
</list>
</option>
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

View File

@@ -0,0 +1,46 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="33">
<item index="0" class="java.lang.String" itemvalue="oauth2client" />
<item index="1" class="java.lang.String" itemvalue="pbr" />
<item index="2" class="java.lang.String" itemvalue="rsa" />
<item index="3" class="java.lang.String" itemvalue="decorator" />
<item index="4" class="java.lang.String" itemvalue="Babel" />
<item index="5" class="java.lang.String" itemvalue="Whoosh" />
<item index="6" class="java.lang.String" itemvalue="guess-language" />
<item index="7" class="java.lang.String" itemvalue="SQLAlchemy" />
<item index="8" class="java.lang.String" itemvalue="defusedxml" />
<item index="9" class="java.lang.String" itemvalue="Flask-WhooshAlchemy" />
<item index="10" class="java.lang.String" itemvalue="httplib2" />
<item index="11" class="java.lang.String" itemvalue="pyasn1" />
<item index="12" class="java.lang.String" itemvalue="requests" />
<item index="13" class="java.lang.String" itemvalue="sqlalchemy-migrate" />
<item index="14" class="java.lang.String" itemvalue="Jinja2" />
<item index="15" class="java.lang.String" itemvalue="Flask-Login" />
<item index="16" class="java.lang.String" itemvalue="sqlparse" />
<item index="17" class="java.lang.String" itemvalue="pyasn1-modules" />
<item index="18" class="java.lang.String" itemvalue="itsdangerous" />
<item index="19" class="java.lang.String" itemvalue="Flask" />
<item index="20" class="java.lang.String" itemvalue="blinker" />
<item index="21" class="java.lang.String" itemvalue="coverage" />
<item index="22" class="java.lang.String" itemvalue="Flask-OpenID" />
<item index="23" class="java.lang.String" itemvalue="Werkzeug" />
<item index="24" class="java.lang.String" itemvalue="Flask-WTF" />
<item index="25" class="java.lang.String" itemvalue="Flask-SQLAlchemy" />
<item index="26" class="java.lang.String" itemvalue="python3-openid" />
<item index="27" class="java.lang.String" itemvalue="rauth" />
<item index="28" class="java.lang.String" itemvalue="WTForms" />
<item index="29" class="java.lang.String" itemvalue="flipflop" />
<item index="30" class="java.lang.String" itemvalue="Flask-Mail" />
<item index="31" class="java.lang.String" itemvalue="Tempita" />
<item index="32" class="java.lang.String" itemvalue="Flask-Babel" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (wassS_backend-Ko2xORm-) (2)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/backend.iml" filepath="$PROJECT_DIR$/.idea/backend.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

234
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="dbc2bdbd-c8f7-46d2-961a-6a2b77f8f4c5" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/.idea/backend.iml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/recorder_api.py" beforeDir="false" afterPath="$PROJECT_DIR$/api/recorder_api.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/api/room_api.py" beforeDir="false" afterPath="$PROJECT_DIR$/api/room_api.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app.db" beforeDir="false" afterPath="$PROJECT_DIR$/app.db" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/api/recorder_api.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="874">
<caret line="197" column="54" selection-start-line="197" selection-start-column="54" selection-end-line="197" selection-end-column="54" />
<folding>
<element signature="e#267#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/models/room_model.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="450">
<caret line="33" column="8" selection-start-line="33" selection-start-column="8" selection-end-line="33" selection-end-column="8" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/models/recorder_model.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="810">
<caret line="52" column="8" selection-start-line="52" selection-start-column="8" selection-end-line="52" selection-end-column="8" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/api/room_api.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="80" column="27" selection-start-line="80" selection-start-column="27" selection-end-line="80" selection-end-column="27" />
<folding>
<element signature="e#267#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/api/recorder_api.py" />
<option value="$PROJECT_DIR$/api/room_api.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="3840" />
<option name="y" value="146" />
<option name="width" value="1200" />
<option name="height" value="1894" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="backend" type="b2602c69:ProjectViewProjectNode" />
<item name="backend" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="backend" type="b2602c69:ProjectViewProjectNode" />
<item name="backend" type="462c0819:PsiDirectoryNode" />
<item name="api" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration name="backend" type="Python.FlaskServer">
<module name="backend" />
<option name="target" value="$PROJECT_DIR$/__init__.py" />
<option name="targetType" value="PATH" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="launchJavascriptDebuger" value="false" />
<method v="2" />
</configuration>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="dbc2bdbd-c8f7-46d2-961a-6a2b77f8f4c5" name="Default Changelist" comment="" />
<created>1556266469460</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1556266469460</updated>
<workItem from="1556266473180" duration="10365000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="10365000" />
</component>
<component name="ToolWindowManager">
<frame x="3840" y="146" width="1200" height="1894" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Favorites" side_tool="true" />
<window_info content_ui="combo" id="Project" order="0" weight="0.25" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Docker" show_stripe_button="false" />
<window_info anchor="bottom" id="Database Changes" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Python Console" />
<window_info anchor="bottom" id="Terminal" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" id="SciView" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" order="0" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/models/recorder_model.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="810">
<caret line="52" column="8" selection-start-line="52" selection-start-column="8" selection-end-line="52" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.virtualenvs/wassS_backend-Ko2xORm-/lib/python3.7/site-packages/flask_restplus/fields.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2368">
<caret line="364" column="8" selection-start-line="364" selection-start-column="8" selection-end-line="364" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/recorder_api.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="874">
<caret line="197" column="54" selection-start-line="197" selection-start-column="54" selection-end-line="197" selection-end-column="54" />
<folding>
<element signature="e#267#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/models/room_model.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="450">
<caret line="33" column="8" selection-start-line="33" selection-start-column="8" selection-end-line="33" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/room_api.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1332">
<caret line="80" column="27" selection-start-line="80" selection-start-column="27" selection-end-line="80" selection-end-column="27" />
<folding>
<element signature="e#267#310#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>

View File

@@ -8,25 +8,30 @@ Login through API does not start a new session, but instead returns JWT.
from flask_jwt_extended import jwt_required
from flask_restplus import fields, Resource
from backend import db
from backend import db, app
from backend.api import api_recorder
from backend.models.recorder_model import Recorder, RecorderModel, RecorderCommand
from backend.models.room_model import Room
recorder_model = api_recorder.model('Recorder', {
'id': fields.String(required=False, description='The recorder\'s identifier'),
'created_at': fields.DateTime(required=False, description='Creation date of the recorder'),
'name': fields.String(required=True, description='The recorder\'s name'),
'name': fields.String(min_length=3, required=True, description='The recorder\'s name'),
'description': fields.String(required=False, description='The recorder\'s description'),
'ip': fields.String(required=False, description='The recorder\'s IP address'),
'network_name': fields.String(required=False, description='The recorder\'s network name'),
'recorder_model': fields.Nested(api_recorder.model('recorder_model',
{'id': fields.Integer(), 'name': fields.String()}),
required=False,
allow_null=True,
skip_none=False,
description='Model of the recorder.'),
'room': fields.Nested(api_recorder.model('recorder_room',
{'id': fields.Integer(), 'name': fields.String(),
'number': fields.String(), 'alternate_name': fields.String()}),
required=False,
allow_null=True,
skip_none=False,
description='Room in which the recorder is located.')
})
@@ -39,7 +44,7 @@ recorder_model = api_recorder.model('Recorder', {
class RecorderResource(Resource):
@jwt_required
@api_recorder.doc('get_recorder')
@api_recorder.marshal_with(recorder_model)
@api_recorder.marshal_with(recorder_model, skip_none=False)
def get(self, id):
"""Fetch a recorder given its identifier"""
recorder = Recorder.query.get(id)
@@ -62,7 +67,6 @@ class RecorderResource(Resource):
@jwt_required
@api_recorder.doc('update_recorder')
@api_recorder.expect(recorder_model)
@api_recorder.marshal_with(recorder_model)
def put(self, id):
"""Update a recorder given its identifier"""
num_rows_matched = Recorder.query.filter_by(id=id).update(api_recorder.payload)
@@ -72,12 +76,11 @@ class RecorderResource(Resource):
return "ok"
@api_recorder.route('')
class RecorderList(Resource):
@jwt_required
@api_recorder.doc('recorders')
@api_recorder.marshal_list_with(recorder_model)
@api_recorder.marshal_list_with(recorder_model, skip_none=False)
def get(self):
"""
List all recorders
@@ -88,8 +91,26 @@ class RecorderList(Resource):
@jwt_required
@api_recorder.doc('create_recorder')
@api_recorder.expect(recorder_model)
@api_recorder.marshal_with(recorder_model, code=201)
@api_recorder.marshal_with(recorder_model, skip_none=False, code=201)
def post(self):
if "room_id" in api_recorder.payload:
if api_recorder.payload["room_id"] is None:
api_recorder.payload["room"] = None
else:
room = Room.query.get(api_recorder.payload["room_id"])
if room is not None:
api_recorder.payload["room"] = room
else:
return "specified room (id: {}) does not exist!".format(api_recorder.payload["room_id"]), 404
if "recorder_model_id" in api_recorder.payload:
if api_recorder.payload["recorder_model_id"] is None:
api_recorder.payload["recorder_model"] = None
else:
rec_model = RecorderModel.query.get(api_recorder.payload["recorder_model_id"])
if rec_model is not None:
api_recorder.payload["recorder_model"] = rec_model
else:
return "specified recorder model (id: {}) does not exist!".format(api_recorder.payload["recorder_model_id"]), 404
recorder = Recorder(**api_recorder.payload)
db.session.add(recorder)
db.session.commit()
@@ -172,6 +193,15 @@ class RecorderModelList(Resource):
@api_recorder.expect(recorder_model_model)
@api_recorder.marshal_with(recorder_model_model, code=201)
def post(self):
if "recorders" in api_recorder.payload:
if api_recorder.payload["recorders"] is None or len(api_recorder.payload["recorders"]) < 1 :
api_recorder.payload["recorders"] = []
else:
rec_model = RecorderModel.query.get(api_recorder.payload["recorder_model_id"])
if rec_model is not None:
api_recorder.payload["recorder_model"] = rec_model
else:
return "specified recorder model (id: {}) does not exist!".format(api_recorder.payload["recorder_model_id"]), 404
recorder = Recorder(**api_recorder.payload)
db.session.add(recorder)
db.session.commit()

View File

@@ -12,6 +12,7 @@ from sqlalchemy import exc
from backend import db, app
from backend.api import api_room
from backend.models.room_model import Room
from backend.models.recorder_model import Recorder
room_model = api_room.model('Room', {
'id': fields.String(required=False, description='The room\'s identifier'),
@@ -20,10 +21,14 @@ room_model = api_room.model('Room', {
'alternate_name': fields.String(required=False, description='The room\'s alternate name'),
'comment': fields.String(required=False, description='The room\'s comment'),
'number': fields.String(required=True, description='The room\'s number'),
'recorder': fields.List(fields.Nested(api_room.model('room_recorder',
'recorder': fields.Nested(api_room.model('room_recorder',
{'id': fields.Integer(), 'name': fields.String(),
'ip': fields.String(), 'network_name': fields.String()})),
required=False, description='Room members.')
'ip': fields.String(), 'network_name': fields.String()}),
allow_null=True,
skip_none=False,
required=False,
description='Room recorder.'),
})
@@ -33,7 +38,7 @@ room_model = api_room.model('Room', {
class RoomResource(Resource):
@jwt_required
@api_room.doc('get_room')
@api_room.marshal_with(room_model)
@api_room.marshal_with(room_model, skip_none=False)
def get(self, id):
"""Fetch a user given its identifier"""
room = Room.query.get(id)
@@ -56,16 +61,24 @@ class RoomResource(Resource):
@jwt_required
@api_room.doc('update_room')
@api_room.expect(room_model)
@api_room.marshal_with(room_model)
def put(self, id):
app.logger.debug(api_room.payload)
'''Update a task given its identifier'''
if "recorder_id" in api_room.payload:
if api_room.payload["recorder_id"] is None:
api_room.payload["recorder"] = None
else:
recorder = Recorder.query.get(api_room.payload["recorder_id"])
if recorder is not None:
api_room.payload["recorder"] = recorder
else:
return "specified recorder (id: {}) does not exist!".format(api_room.payload["recorder_id"]), 404
room = Room.query.get(id)
if room is not None:
room.recorder = api_room.payload["recorder"]
else:
num_rows_matched = Room.query.filter_by(id=id).update(api_room.payload)
db.session.commit()
# if room is not None:
# app.logger.debug(room)
# room.update(api_room.payload)
# db.session.commit()
# return room
return "ok"
api_room.abort(404)
@@ -74,7 +87,7 @@ class RoomResource(Resource):
class RoomList(Resource):
@jwt_required
@api_room.doc('rooms')
@api_room.marshal_list_with(room_model)
@api_room.marshal_list_with(room_model, skip_none=False)
def get(self):
"""
List all rooms
@@ -85,8 +98,18 @@ class RoomList(Resource):
@jwt_required
@api_room.doc('create_room')
@api_room.expect(room_model)
@api_room.marshal_with(room_model, code=201)
@api_room.marshal_with(room_model, skip_none=False, code=201)
def post(self):
if "recorder_id" in api_room.payload:
if api_room.payload["recorder_id"] is None:
api_room.payload["recorder"] = None
else:
recorder = Recorder.query.get(api_room.payload["recorder_id"])
if recorder is not None:
api_room.payload["recorder"] = recorder
else:
return "specified recorder (id: {}) does not exist!".format(api_room.payload["recorder_id"]), 404
del api_room.payload["recorder_id"]
room = Room(**api_room.payload)
db.session.add(room)
try:

BIN
app.db

Binary file not shown.