1+ """The analytics explorer, a subset of server analytics that was taken out because the file was still too big"""
2+
3+ import curses
4+ import cursesplus
5+ import tempdir
6+ import jsongz
7+ import uicomponents
8+ import analyticsstructures
9+ import utils
10+ import enum
11+ import datetime
12+
13+ def server_analytics_explorer (stdscr ,data :dict [int ,analyticsstructures .ServerMinuteFrame ]):
14+ cursesplus .displaymsg (stdscr ,["Analytics Explorer" ,"Initializing..." ],False )
15+ #This function allows users to explore their analytics
16+ offset = 0
17+ datasize = len (data )- 1
18+ currentzoomlevel = analyticsstructures .AnalyticsExplorerZoomLevels .MINUTE #Also passively the list chunk size
19+ currentdatatype = analyticsstructures .AnalyticsExplorerDataTypes .PLAYERCOUNT
20+ zoomlevels = {
21+ analyticsstructures .AnalyticsExplorerZoomLevels .MINUTE : "Minute (default)" ,
22+ analyticsstructures .AnalyticsExplorerZoomLevels .HOUR : "Hour" ,
23+ analyticsstructures .AnalyticsExplorerZoomLevels .DAY : "Day" ,
24+ analyticsstructures .AnalyticsExplorerZoomLevels .WEEK : "Week"
25+ }
26+ datatypes = {
27+ analyticsstructures .AnalyticsExplorerDataTypes .TOTALPLAYERMINUTES : "Player Minutes Spent" ,
28+ analyticsstructures .AnalyticsExplorerDataTypes .PLAYERCOUNT : "Online Players (default)"
29+ }
30+ ldata = list (data .values ())#List representation of data to prevent performance issues?
31+
32+ td = tempdir .generate_temp_dir ()
33+
34+ #ogdata = copy.deepcopy(data)
35+ #ogldata = copy.deepcopy(ldata)#For use later
36+ jsongz .write (td + "/data.json.gz" ,{k :analyticsstructures .serialize_smf (v ) for k ,v in list (data .items ())})
37+ maxval = max ([len (p .onlineplayers ) for p in ldata ])
38+ while True :
39+ my ,mx = stdscr .getmaxyx ()
40+ xspace = mx - 1
41+ yspace = my - 4 #Top Bottom, and weird bug
42+ stdscr .erase ()
43+ cursesplus .utils .fill_line (stdscr ,0 ,cursesplus .set_colour (cursesplus .BLUE ,cursesplus .BLUE ))
44+ stdscr .addstr (0 ,0 ,"Analytics Explorer - Press Q to quit and H for help" ,cursesplus .set_colour (cursesplus .BLUE ,cursesplus .WHITE ))
45+
46+
47+ ti = 0
48+ for i in range (offset - xspace // 2 ,offset + xspace // 2 ):
49+ if i < 0 or i > datasize :
50+ #Write red bar
51+ #print("w")
52+ for dy in range (1 ,yspace + 1 ):
53+ stdscr .addstr (dy ,ti ,"█" ,cursesplus .set_colour (cursesplus .RED ,cursesplus .RED ))
54+ else :
55+ seldata = ldata [i ]
56+ if currentdatatype == analyticsstructures .AnalyticsExplorerDataTypes .PLAYERCOUNT :
57+ scale = int (seldata .howmanyonline ()/ maxval * yspace )
58+ else :
59+ scale = int (seldata .getplayerminutes ()/ maxval * yspace )
60+
61+ if i == offset :
62+ for p in range (1 ,yspace + 2 ):
63+ stdscr .addstr (p ,ti ,"█" ,cursesplus .set_colour (cursesplus .GREEN ,cursesplus .GREEN ))#Central marker
64+ for p in range (yspace + 1 ,yspace + 1 - scale ,- 1 ):
65+ stdscr .addstr (p ,ti ,"█" ,cursesplus .set_colour (cursesplus .CYAN ,cursesplus .CYAN ))
66+ else :
67+ for p in range (yspace + 1 ,yspace + 1 - scale ,- 1 ):
68+ stdscr .addstr (p ,ti ,"█" ,cursesplus .set_colour (cursesplus .WHITE ,cursesplus .WHITE ))
69+
70+ ti += 1
71+
72+ seldata = ldata [offset ]
73+ if currentdatatype == analyticsstructures .AnalyticsExplorerDataTypes .TOTALPLAYERMINUTES :
74+ stdscr .addstr (my - 2 ,0 ,f"{ utils .strip_datetime (analyticsstructures .get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .getplayerminutes ()} player-minutes" )
75+ else :
76+ try :
77+ stdscr .addstr (my - 2 ,0 ,f"{ utils .strip_datetime (analyticsstructures .get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .howmanyonline ()} players online - { seldata .onlineplayers } " )
78+ except :
79+ stdscr .addstr (my - 2 ,0 ,f"{ utils .strip_datetime (analyticsstructures .get_datetime_from_minute_id (seldata .minuteid ))} || { seldata .howmanyonline ()} players online" )#Too long for list
80+
81+ ch = curses .keyname (stdscr .getch ()).decode ()
82+ if ch == "q" :
83+ break
84+ elif ch == "h" :
85+ cursesplus .displaymsg (stdscr ,["KEYBINDS" ,"q - Quit" ,"h - Help" ,"<- -> Scroll" ,"END - Go to end" ,"HOME - Go to beginning" ,"j - Jump to time" ,"SHIFT <-- --> - Jump hour" ,"Ctrl <-- --> - Jump day" ,"T - Select time unit" ,"D - Select data type" ])
86+ elif ch == "KEY_LEFT" :
87+ if offset > 0 :
88+ offset -= 1
89+ elif ch == "KEY_RIGHT" :
90+ offset += 1
91+ elif ch == "KEY_SLEFT" :
92+ if offset > 60 :
93+ offset -= 60
94+ else :
95+ offset = 0
96+ elif ch == "KEY_SRIGHT" :#Jump around by an hour
97+ offset += 60
98+ elif ch == "kRIT5" :#ctrl left
99+ offset += 1440
100+ elif ch == "kLFT5" :#ctrl right
101+ if offset > 1440 :
102+ offset -= 1440
103+ else :
104+ offset = 0
105+ elif ch == "KEY_END" :
106+ offset = datasize - 1
107+ elif ch == "KEY_HOME" :
108+ offset = 0
109+ elif ch == "j" :
110+ stdscr .clear ()
111+ ndate :datetime .datetime = cursesplus .date_time_selector (stdscr ,cursesplus .DateTimeSelectorTypes .DATEANDTIME ,"Choose a date and time to jump to" ,True ,False ,analyticsstructures .get_datetime_from_minute_id (ldata [offset ].minuteid )) # type: ignore
112+ if currentzoomlevel == analyticsstructures .AnalyticsExplorerZoomLevels .HOUR :
113+ ndate = ndate .replace (second = 0 ,minute = 0 )
114+ if currentzoomlevel == analyticsstructures .AnalyticsExplorerZoomLevels .DAY or currentzoomlevel == analyticsstructures .AnalyticsExplorerZoomLevels .WEEK :
115+ ndate = ndate .replace (hour = 0 )
116+ nmid = analyticsstructures .get_minute_id_from_datetime (ndate )
117+ if not nmid in data :
118+ cursesplus .messagebox .showerror (stdscr ,["Records do not exist for the selected date." ])
119+ else :
120+ offset = list (data .keys ()).index (nmid )
121+ elif ch == "t" :
122+ currentzoomlevel = list (zoomlevels .keys ())[uicomponents .menu (stdscr ,list (zoomlevels .values ()),"Choose a zoom level" )]
123+ cursesplus .displaymsg (stdscr ,["Generating Data" ],False )
124+ #This will use ogdat because data may have been used for hour or day, which will screw it up
125+ offset = 0
126+ if currentzoomlevel == analyticsstructures .AnalyticsExplorerZoomLevels .MINUTE :
127+ data = {k :analyticsstructures .deserialize_smf (v ) for k ,v in list (jsongz .read (td + "/data.json.gz" ).items ())}
128+ ldata = list (data .values ())
129+ else :
130+ chunked_data :list [list [analyticsstructures .ServerMinuteFrame ]] = list (utils .split_list_into_chunks (list ({k :analyticsstructures .deserialize_smf (v ) for k ,v in list (jsongz .read (td + "/data.json.gz" ).items ())}.values ()),get_chunk_size_from_aezl (currentzoomlevel )))
131+ final_data :dict [int ,analyticsstructures .ServerMinuteFrame ] = {}
132+ for chunk in chunked_data :
133+ s :analyticsstructures .ServerMinuteFrame = analyticsstructures .ServerMinuteFrame (chunk [0 ].minuteid )
134+ #Append all unique players, and set playerminutes
135+ total_playerminutes = 0
136+ unique_players = []
137+ for minute in chunk :
138+ total_playerminutes += minute .howmanyonline ()
139+ for onlineplayer in minute .onlineplayers :
140+ if onlineplayer not in unique_players :
141+ unique_players .append (onlineplayer )
142+ s .playerminutes = total_playerminutes
143+ s .onlineplayers = unique_players
144+ final_data [s .minuteid ] = s
145+ data = final_data
146+ del chunked_data
147+ del final_data
148+ ldata = list (data .values ())
149+ maxval = max ([p .get_data (currentdatatype ) for p in list (data .values ())])
150+ #if currentdatatype == analyticsstructures.AnalyticsExplorerDataTypes.TOTALPLAYERMINUTES:
151+ # maxval = maxval*get_chunk_size_from_aezl(currentzoomlevel)
152+ datasize = len (data )- 1
153+
154+ elif ch == "d" :
155+ currentdatatype = list (datatypes .keys ())[uicomponents .menu (stdscr ,list (datatypes .values ()),"Choose a data type" )]
156+ maxval = max ([p .get_data (currentdatatype ) for p in ldata ])
157+ #if currentdatatype == analyticsstructures.AnalyticsExplorerDataTypes.TOTALPLAYERMINUTES:
158+ # maxval = maxval*get_chunk_size_from_aezl(currentzoomlevel)
159+ if offset > datasize :
160+ offset = datasize - 1
161+
162+
163+
164+ def get_chunk_size_from_aezl (s :analyticsstructures .AnalyticsExplorerZoomLevels ):
165+ return {
166+ analyticsstructures .AnalyticsExplorerZoomLevels .MINUTE :1 ,
167+ analyticsstructures .AnalyticsExplorerZoomLevels .HOUR :60 ,
168+ analyticsstructures .AnalyticsExplorerZoomLevels .DAY :1440 ,
169+ analyticsstructures .AnalyticsExplorerZoomLevels .WEEK :1440 * 7
170+ }[s ]
0 commit comments