Skip to content

Commit 9b40fad

Browse files
nnhathungHieu Lam - TMAlthanhhieunorbusanmariobehling
authored
feature-8961: Stats API for registration and stations (#9002)
* feature-8958: Store check in kiosk id to mark association * feature-8958: Store check in kiosk id to mark association * feature-8957: Store and Retrieve check in and out data on server implement api post and get detail * feature-8958: Store check in kiosk id to mark association set validate for station type and station name * feature-8958: Store check in kiosk id to mark association fix ci/cd * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association fix ci and update latest branch from development * feature-8958: Store check in kiosk id to mark association * feature-8958: Store check in kiosk id to mark association update station_type, remove session, add new 2 types check in and check out * feature-8958: Store check in kiosk id to mark association * feature-8958: Store check in kiosk id to mark association * fix multiple migration head * feature-8957:Store and Retrieve check in and out data on server * feature-8957: Store and Retrieve check in and out data on server * feature-8957: Store and Retrieve check in and out data on server * fix ci/cd * fix ci/cd * fix ci/cd * fix function too conplex * feature-8961: Stats API for registration and stations * fix alembic revision * fix alembic revision * filter by track for stats * add docs methods * fix ci/cd * ignore invalid track/session * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations * feature-8961: Stats API for registration and stations fix issue when count total track check in/out --------- Co-authored-by: Hieu Lam - TMA <[email protected]> Co-authored-by: lthanhhieu <[email protected]> Co-authored-by: Norbert Preining <[email protected]> Co-authored-by: Mario Behling <[email protected]> Co-authored-by: Khang On - TMA <[email protected]> Co-authored-by: cweitat <[email protected]>
1 parent 95266c0 commit 9b40fad

File tree

6 files changed

+401
-3
lines changed

6 files changed

+401
-3
lines changed

app/api/custom/check_in_stats.py

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
import datetime
2+
3+
from flask import Blueprint, request
4+
from sqlalchemy import desc
5+
6+
from app.api.helpers.permissions import jwt_required
7+
from app.api.helpers.static import STATION_TYPE
8+
from app.models import db
9+
from app.models.event import Event
10+
from app.models.session import Session
11+
from app.models.session_type import SessionType
12+
from app.models.station import Station
13+
from app.models.station_store_pax import StationStorePax
14+
from app.models.ticket_holder import TicketHolder
15+
from app.models.track import Track
16+
from app.models.user_check_in import UserCheckIn
17+
18+
check_in_stats_routes = Blueprint(
19+
'check_in_stats_routes', __name__, url_prefix='/v1/user-check-in/stats'
20+
)
21+
22+
23+
@check_in_stats_routes.route('/event/<int:event_id>', methods=['GET'])
24+
@jwt_required
25+
def get_registration_stats(event_id):
26+
"""
27+
API for get event check in/out stats
28+
@param event_id: event id to check
29+
@return: stats
30+
"""
31+
# check if event is existed
32+
event = Event.query.filter(Event.id == event_id).first()
33+
current_time = datetime.datetime.utcnow().date()
34+
if event is None:
35+
return {"message": "Event can not be found."}, 404
36+
stations = Station.query.filter(Station.event_id == event_id).all()
37+
total_attendee = TicketHolder.query.filter(TicketHolder.event_id == event_id).count()
38+
registration_stations = [
39+
station.id
40+
for station in stations
41+
if station.station_type == STATION_TYPE.get('registration')
42+
]
43+
check_in_stations = [
44+
station.id
45+
for station in stations
46+
if station.station_type == STATION_TYPE.get('check in')
47+
]
48+
check_out_stations = [
49+
station.id
50+
for station in stations
51+
if station.station_type == STATION_TYPE.get('check out')
52+
]
53+
registered_attendee = (
54+
UserCheckIn.query.with_entities(UserCheckIn.ticket_holder_id)
55+
.filter(
56+
UserCheckIn.station_id.in_(registration_stations),
57+
UserCheckIn.created_at >= current_time,
58+
)
59+
.group_by(UserCheckIn.ticket_holder_id)
60+
.count()
61+
)
62+
63+
check_in_attendee = UserCheckIn.query.filter(
64+
UserCheckIn.station_id.in_(check_in_stations),
65+
UserCheckIn.created_at >= current_time,
66+
)
67+
68+
check_out_attendee = UserCheckIn.query.filter(
69+
UserCheckIn.station_id.in_(check_out_stations),
70+
UserCheckIn.created_at >= current_time,
71+
)
72+
73+
session_checked_in = check_in_attendee.with_entities(
74+
UserCheckIn.session_id, UserCheckIn.ticket_holder_id
75+
).group_by(UserCheckIn.session_id, UserCheckIn.ticket_holder_id)
76+
77+
session_checked_out = check_out_attendee.with_entities(
78+
UserCheckIn.session_id, UserCheckIn.ticket_holder_id
79+
).group_by(UserCheckIn.session_id, UserCheckIn.ticket_holder_id)
80+
81+
session_checked_in_count = session_checked_in.count()
82+
83+
session_checked_out_count = session_checked_out.count()
84+
85+
track_checked_in = db.session.query(UserCheckIn, Session).filter(
86+
Session.id.in_(
87+
[user_check_in.session_id for user_check_in in session_checked_in]
88+
),
89+
UserCheckIn.station_id.in_(check_in_stations),
90+
Session.id == UserCheckIn.session_id,
91+
UserCheckIn.created_at >= current_time,
92+
)
93+
94+
track_checked_in_count = (
95+
track_checked_in.with_entities(UserCheckIn.ticket_holder_id, Session.track_id)
96+
.group_by(UserCheckIn.ticket_holder_id, Session.track_id)
97+
.count()
98+
)
99+
100+
track_checked_out = db.session.query(UserCheckIn, Session).filter(
101+
Session.id.in_(
102+
[user_check_in.session_id for user_check_in in session_checked_out]
103+
),
104+
UserCheckIn.station_id.in_(check_out_stations),
105+
UserCheckIn.session_id == Session.id,
106+
UserCheckIn.created_at >= current_time,
107+
)
108+
109+
track_checked_out_count = (
110+
track_checked_out.with_entities(UserCheckIn.ticket_holder_id, Session.track_id)
111+
.group_by(UserCheckIn.ticket_holder_id, Session.track_id)
112+
.count()
113+
)
114+
session_stat = []
115+
track_stat = []
116+
if request.args.get('session_ids'):
117+
session_stat = get_session_stats(
118+
request.args.get('session_ids'), session_checked_in, session_checked_out
119+
)
120+
if request.args.get('track_ids'):
121+
track_stat = get_track_stats(
122+
request.args.get('track_ids'),
123+
check_in_attendee,
124+
check_out_attendee,
125+
current_time,
126+
)
127+
128+
return {
129+
"total_attendee": total_attendee,
130+
"total_registered": registered_attendee,
131+
"total_not_checked_in": total_attendee - registered_attendee,
132+
"total_track_checked_in": track_checked_in_count,
133+
"total_track_checked_out": track_checked_out_count,
134+
"total_session_checked_in": session_checked_in_count,
135+
"total_session_checked_out": session_checked_out_count,
136+
"session_stats": session_stat,
137+
"track_stats": track_stat,
138+
}, 200
139+
140+
141+
def get_session_stats(session_ids, session_checked_in, session_checked_out):
142+
"""
143+
Get session stats
144+
@param session_ids: session id to get
145+
@param session_checked_in: session_checked_in
146+
@param session_checked_out: session_checked_out
147+
@return: list of session stats
148+
"""
149+
session_stat = []
150+
session_ids = [session_id.strip() for session_id in session_ids.split(",")]
151+
for session_id in session_ids:
152+
speakers = db.session.query(Session).filter(Session.id == session_id).first()
153+
if speakers:
154+
current_speakers = [str(speaker.name) for speaker in speakers.speakers]
155+
else:
156+
break
157+
session_check_in = session_checked_in.filter(
158+
UserCheckIn.session_id == session_id
159+
).count()
160+
session_check_out = session_checked_out.filter(
161+
UserCheckIn.session_id == session_id
162+
).count()
163+
current_session = (
164+
db.session.query(Session, SessionType)
165+
.filter(Session.id == session_id, Session.session_type_id == SessionType.id)
166+
.with_entities(SessionType.name)
167+
.first()
168+
)
169+
session_name = ''
170+
if current_session:
171+
session_name = current_session._asdict()['name']
172+
current_track = (
173+
db.session.query(Session, Track)
174+
.filter(Session.id == session_id, Session.track_id == Track.id)
175+
.with_entities(Track.name)
176+
.first()
177+
)
178+
track_name = ''
179+
if current_track:
180+
track_name = current_track._asdict()['name']
181+
182+
stationStorePaxs = (
183+
db.session.query(StationStorePax)
184+
.filter(StationStorePax.session_id == session_id)
185+
.order_by(desc("created_at"))
186+
.all()
187+
)
188+
manual_count = {}
189+
if stationStorePaxs:
190+
pax = stationStorePaxs[0]
191+
manual_count = {
192+
"current_pax": pax.current_pax,
193+
"created_at": pax.created_at.strftime("%Y-%m-%dT%H:%M:%S%z"),
194+
"modified_at": pax.modified_at.strftime("%Y-%m-%dT%H:%M:%S%z"),
195+
}
196+
197+
session_stat.append(
198+
{
199+
"session_id": session_id,
200+
"session_name": session_name,
201+
"track_name": track_name,
202+
"speakers": current_speakers,
203+
"check_in": session_check_in,
204+
"check_out": session_check_out,
205+
"manual_count": manual_count,
206+
}
207+
)
208+
return session_stat
209+
210+
211+
def get_track_stats(track_ids, check_in_attendee, check_out_attendee, current_time):
212+
"""
213+
get track stats
214+
@param track_ids: track_ids
215+
@param check_in_attendee: check_in_attendee
216+
@param check_out_attendee: check_out_attendee
217+
@param current_time: current_time
218+
@return: list of track stats
219+
"""
220+
track_stat = []
221+
track_ids = [session_id.strip() for session_id in track_ids.split(",")]
222+
for track_id in track_ids:
223+
current_track = Track.query.filter(Track.id == track_id).first()
224+
if current_track:
225+
track_name = current_track.name
226+
else:
227+
break
228+
track_checked_in = db.session.query(UserCheckIn, Session).filter(
229+
Session.id.in_(
230+
[user_check_in.session_id for user_check_in in check_in_attendee]
231+
),
232+
UserCheckIn.id.in_([user_check_in.id for user_check_in in check_in_attendee]),
233+
Session.id == UserCheckIn.session_id,
234+
UserCheckIn.created_at >= current_time,
235+
Session.track_id == track_id,
236+
)
237+
238+
track_checked_in_count = (
239+
track_checked_in.with_entities(UserCheckIn.ticket_holder_id, Session.track_id)
240+
.group_by(UserCheckIn.ticket_holder_id, Session.track_id)
241+
.count()
242+
)
243+
244+
track_checked_out = db.session.query(UserCheckIn, Session).filter(
245+
Session.id.in_(
246+
[user_check_in.session_id for user_check_in in check_out_attendee]
247+
),
248+
UserCheckIn.id.in_(
249+
[user_check_in.id for user_check_in in check_out_attendee]
250+
),
251+
UserCheckIn.session_id == Session.id,
252+
UserCheckIn.created_at >= current_time,
253+
Session.track_id == track_id,
254+
)
255+
256+
track_checked_out_count = (
257+
track_checked_out.with_entities(
258+
UserCheckIn.ticket_holder_id, Session.track_id
259+
)
260+
.group_by(UserCheckIn.ticket_holder_id, Session.track_id)
261+
.count()
262+
)
263+
264+
current_session = Session.query.filter(
265+
Session.track_id == track_id,
266+
Session.starts_at <= datetime.datetime.utcnow(),
267+
Session.ends_at >= datetime.datetime.utcnow(),
268+
).first()
269+
current_speakers = ''
270+
session_name = ''
271+
if current_session:
272+
current_speakers = [str(speaker.name) for speaker in current_session.speakers]
273+
current_session_name = (
274+
db.session.query(Session, SessionType)
275+
.filter(
276+
Session.id == current_session.id,
277+
Session.session_type_id == SessionType.id,
278+
)
279+
.with_entities(SessionType.name)
280+
.first()
281+
)
282+
session_name = ''
283+
if current_session:
284+
session_name = current_session_name._asdict()['name']
285+
286+
track_stat.append(
287+
{
288+
"track_id": track_id,
289+
"track_name": track_name,
290+
"current_speakers": current_speakers,
291+
"current_session": session_name,
292+
"check_in": track_checked_in_count,
293+
"check_out": track_checked_out_count,
294+
}
295+
)
296+
return track_stat

app/instance.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from werkzeug.middleware.profiler import ProfilerMiddleware
2424

2525
from app.api import routes # noqa: Used for registering routes
26+
from app.api.custom.check_in_stats import check_in_stats_routes
2627
from app.api.helpers.auth import AuthManager, is_token_blacklisted
2728
from app.api.helpers.cache import cache
2829
from app.api.helpers.errors import ErrorResponse
@@ -54,8 +55,8 @@ class ReverseProxied:
5455
ReverseProxied flask wsgi app wrapper from http://stackoverflow.com/a/37842465/1562480 by aldel
5556
"""
5657

57-
def __init__(self, app):
58-
self.app = app
58+
def __init__(self, wsgi_app):
59+
self.app = wsgi_app
5960

6061
def __call__(self, environ, start_response):
6162
scheme = environ.get('HTTP_X_FORWARDED_PROTO')
@@ -209,6 +210,7 @@ def create_app():
209210
app.register_blueprint(badge_forms_routes)
210211
app.register_blueprint(ticket_routes)
211212
app.register_blueprint(users_routes)
213+
app.register_blueprint(check_in_stats_routes)
212214
app.register_blueprint(users_check_in_routes)
213215

214216
add_engine_pidguard(db.engine)

docs/api/api_blueprint_source.apib

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,6 @@ The Open Event API Server
133133

134134
<!-- include(blueprint/video_stream.apib) -->
135135

136-
<!-- include(blueprint/badge_forms.apib) -->
136+
<!-- include(blueprint/badge_forms.apib) -->
137+
138+
<!-- include(blueprint/user_check_in.apib) -->

docs/api/blueprint/user_check_in.apib

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Group Users Check In
2+
3+
For using the API you need(mostly) to register as an user check in. Registering gives you access to all non admin API endpoints. After registration, you need to create your JWT access token to send requests to the API endpoints.
4+
5+
6+
## Get Registration Stats [/v1/user-check-in/stats/event/{event_id}?session_ids=1]
7+
+ Parameters
8+
+ event_id: 1 (integer) - ID of the event in the form of an integer
9+
10+
11+
### Get Registration Stats [GET]
12+
Get registration stats.
13+
14+
+ Request
15+
16+
+ Headers
17+
18+
Accept: application/vnd.api+json
19+
20+
Authorization: JWT <Auth Key>
21+
22+
23+
+ Response 200 (application/json)
24+
25+
{
26+
"session_stats": [
27+
{
28+
"check_in": 0,
29+
"check_out": 0,
30+
"manual_count": {},
31+
"session_id": "1",
32+
"session_name": "example",
33+
"speakers": [],
34+
"track_name": "example"
35+
}
36+
],
37+
"total_attendee": 0,
38+
"total_not_checked_in": 0,
39+
"total_registered": 0,
40+
"total_session_checked_in": 0,
41+
"total_session_checked_out": 0,
42+
"total_track_checked_in": 0,
43+
"total_track_checked_out": 0,
44+
"track_stats": []
45+
}

0 commit comments

Comments
 (0)