cs2tracker 2.1.4__py3-none-any.whl → 2.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cs2tracker might be problematic. Click here for more details.
- cs2tracker/_version.py +2 -2
- cs2tracker/application.py +148 -50
- cs2tracker/constants.py +388 -108
- cs2tracker/data/config.ini +150 -65
- cs2tracker/data/output.csv +0 -0
- cs2tracker/main.py +13 -22
- cs2tracker/padded_console.py +22 -0
- cs2tracker/scraper.py +51 -32
- {cs2tracker-2.1.4.dist-info → cs2tracker-2.1.6.dist-info}/METADATA +17 -11
- cs2tracker-2.1.6.dist-info/RECORD +16 -0
- cs2tracker-2.1.4.dist-info/RECORD +0 -14
- {cs2tracker-2.1.4.dist-info → cs2tracker-2.1.6.dist-info}/WHEEL +0 -0
- {cs2tracker-2.1.4.dist-info → cs2tracker-2.1.6.dist-info}/entry_points.txt +0 -0
- {cs2tracker-2.1.4.dist-info → cs2tracker-2.1.6.dist-info}/licenses/LICENSE.md +0 -0
- {cs2tracker-2.1.4.dist-info → cs2tracker-2.1.6.dist-info}/top_level.txt +0 -0
cs2tracker/_version.py
CHANGED
cs2tracker/application.py
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import subprocess
|
|
3
1
|
import tkinter as tk
|
|
2
|
+
from subprocess import Popen
|
|
3
|
+
from threading import Thread
|
|
4
4
|
from typing import cast
|
|
5
5
|
|
|
6
6
|
import matplotlib.pyplot as plt
|
|
7
7
|
from matplotlib.axes import Axes
|
|
8
8
|
from matplotlib.dates import DateFormatter
|
|
9
9
|
|
|
10
|
-
from cs2tracker.constants import
|
|
10
|
+
from cs2tracker.constants import (
|
|
11
|
+
CONFIG_FILE,
|
|
12
|
+
OS,
|
|
13
|
+
OUTPUT_FILE,
|
|
14
|
+
PYTHON_EXECUTABLE,
|
|
15
|
+
RUNNING_IN_EXE,
|
|
16
|
+
TEXT_EDITOR,
|
|
17
|
+
OSType,
|
|
18
|
+
)
|
|
11
19
|
from cs2tracker.scraper import Scraper
|
|
12
20
|
|
|
21
|
+
WINDOW_TITLE = "CS2Tracker"
|
|
22
|
+
WINDOW_SIZE = "450x380"
|
|
23
|
+
BACKGROUND_COLOR = "#1e1e1e"
|
|
24
|
+
BUTTON_COLOR = "#3c3f41"
|
|
25
|
+
BUTTON_HOVER_COLOR = "#505354"
|
|
26
|
+
BUTTON_ACTIVE_COLOR = "#5c5f61"
|
|
27
|
+
FONT_STYLE = "Segoe UI"
|
|
28
|
+
FONT_COLOR = "white"
|
|
29
|
+
|
|
30
|
+
SCRAPER_WINDOW_TITLE = "CS2Tracker"
|
|
31
|
+
SCRAPER_WINDOW_HEIGHT = 40
|
|
32
|
+
SCRAPER_WINDOW_WIDTH = 100
|
|
33
|
+
SCRAPER_WINDOW_BACKGROUND_COLOR = "Black"
|
|
34
|
+
|
|
13
35
|
|
|
14
36
|
class Application:
|
|
15
37
|
def __init__(self):
|
|
@@ -22,88 +44,164 @@ class Application:
|
|
|
22
44
|
application_window = self._configure_window()
|
|
23
45
|
application_window.mainloop()
|
|
24
46
|
|
|
47
|
+
def _add_button(self, frame, text, command):
|
|
48
|
+
"""Create and style a button for the main application window."""
|
|
49
|
+
button_style = {
|
|
50
|
+
"font": (FONT_STYLE, 12),
|
|
51
|
+
"fg": FONT_COLOR,
|
|
52
|
+
"bg": BUTTON_COLOR,
|
|
53
|
+
"activebackground": BUTTON_ACTIVE_COLOR,
|
|
54
|
+
}
|
|
55
|
+
button = tk.Button(frame, text=text, command=command, **button_style)
|
|
56
|
+
button.pack(pady=5, fill="x")
|
|
57
|
+
button.bind("<Enter>", lambda _: button.config(bg=BUTTON_HOVER_COLOR))
|
|
58
|
+
button.bind("<Leave>", lambda _: button.config(bg=BUTTON_COLOR))
|
|
59
|
+
return button
|
|
60
|
+
|
|
25
61
|
def _configure_window(self):
|
|
26
|
-
"""Configure the main application window
|
|
27
|
-
|
|
62
|
+
"""Configure the main application window UI and add buttons for the main
|
|
63
|
+
functionalities.
|
|
28
64
|
"""
|
|
29
65
|
window = tk.Tk()
|
|
30
|
-
window.title(
|
|
31
|
-
window.geometry(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
window.title(WINDOW_TITLE)
|
|
67
|
+
window.geometry(WINDOW_SIZE)
|
|
68
|
+
window.configure(bg=BACKGROUND_COLOR)
|
|
69
|
+
|
|
70
|
+
frame = tk.Frame(window, bg=BACKGROUND_COLOR, padx=30, pady=30)
|
|
71
|
+
frame.pack(expand=True, fill="both")
|
|
72
|
+
|
|
73
|
+
label = tk.Label(
|
|
74
|
+
frame,
|
|
75
|
+
text=f"Welcome to {WINDOW_TITLE}!",
|
|
76
|
+
font=(FONT_STYLE, 16, "bold"),
|
|
77
|
+
fg=FONT_COLOR,
|
|
78
|
+
bg=BACKGROUND_COLOR,
|
|
39
79
|
)
|
|
80
|
+
label.pack(pady=(0, 30))
|
|
81
|
+
|
|
82
|
+
self._add_button(frame, "Run!", self.scrape_prices)
|
|
83
|
+
self._add_button(frame, "Edit Config", self._edit_config)
|
|
84
|
+
self._add_button(frame, "Show History (Chart)", self._draw_plot)
|
|
85
|
+
self._add_button(frame, "Show History (File)", self._edit_log_file)
|
|
86
|
+
|
|
40
87
|
background_checkbox_value = tk.BooleanVar(value=self.scraper.identify_background_task())
|
|
41
88
|
background_checkbox = tk.Checkbutton(
|
|
42
|
-
|
|
89
|
+
frame,
|
|
43
90
|
text="Daily Background Calculation",
|
|
44
|
-
command=lambda: self._toggle_background_task(background_checkbox_value.get()),
|
|
45
91
|
variable=background_checkbox_value,
|
|
92
|
+
command=lambda: self._toggle_background_task(background_checkbox_value.get()),
|
|
93
|
+
bg=BACKGROUND_COLOR,
|
|
94
|
+
fg=FONT_COLOR,
|
|
95
|
+
selectcolor=BUTTON_COLOR,
|
|
96
|
+
activebackground=BACKGROUND_COLOR,
|
|
97
|
+
font=(FONT_STYLE, 10),
|
|
46
98
|
)
|
|
47
|
-
|
|
48
|
-
label.grid(row=0, column=0, pady=50, sticky="NSEW")
|
|
49
|
-
run_button.grid(row=1, column=0, pady=10, sticky="NSEW")
|
|
50
|
-
edit_button.grid(row=2, column=0, pady=10, sticky="NSEW")
|
|
51
|
-
plot_button.grid(row=3, column=0, pady=10, sticky="NSEW")
|
|
52
|
-
plot_file_button.grid(row=4, column=0, pady=10, sticky="NSEW")
|
|
53
|
-
background_checkbox.grid(row=5, column=0, pady=10, sticky="NS")
|
|
54
|
-
|
|
55
|
-
window.grid_columnconfigure(0, weight=1)
|
|
56
|
-
window.grid_rowconfigure(1, weight=1)
|
|
57
|
-
window.grid_rowconfigure(2, weight=1)
|
|
58
|
-
window.grid_rowconfigure(3, weight=1)
|
|
59
|
-
window.grid_rowconfigure(4, weight=1)
|
|
60
|
-
window.grid_rowconfigure(5, weight=1)
|
|
61
|
-
|
|
62
|
-
label.grid_configure(sticky="NSEW")
|
|
63
|
-
run_button.grid_configure(sticky="NSEW")
|
|
64
|
-
edit_button.grid_configure(sticky="NSEW")
|
|
65
|
-
plot_button.grid_configure(sticky="NSEW")
|
|
66
|
-
plot_file_button.grid_configure(sticky="NSEW")
|
|
67
|
-
background_checkbox.grid_configure(sticky="NSEW")
|
|
99
|
+
background_checkbox.pack(pady=20)
|
|
68
100
|
|
|
69
101
|
return window
|
|
70
102
|
|
|
71
|
-
def
|
|
103
|
+
def _construct_scraper_command_windows(self):
|
|
104
|
+
"""Construct the command to run the scraper in a new window for Windows."""
|
|
105
|
+
set_utf8_encoding = (
|
|
106
|
+
"[Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8;"
|
|
107
|
+
)
|
|
108
|
+
get_size = "$size = $Host.UI.RawUI.WindowSize;"
|
|
109
|
+
set_size = "$Host.UI.RawUI.WindowSize = $size;"
|
|
110
|
+
set_window_title = f"$Host.UI.RawUI.WindowTitle = '{SCRAPER_WINDOW_TITLE}';"
|
|
111
|
+
set_window_width = (
|
|
112
|
+
f"$size.Width = [Math]::Min({SCRAPER_WINDOW_WIDTH}, $Host.UI.RawUI.BufferSize.Width);"
|
|
113
|
+
)
|
|
114
|
+
set_window_height = f"$size.Height = {SCRAPER_WINDOW_HEIGHT};"
|
|
115
|
+
set_background_color = (
|
|
116
|
+
f"$Host.UI.RawUI.BackgroundColor = '{SCRAPER_WINDOW_BACKGROUND_COLOR}';"
|
|
117
|
+
)
|
|
118
|
+
clear = "Clear-Host;"
|
|
119
|
+
|
|
120
|
+
if RUNNING_IN_EXE:
|
|
121
|
+
# The python executable is set as the executable itself in PyInstaller
|
|
122
|
+
scraper_cmd = f"{PYTHON_EXECUTABLE} --only-scrape | Out-Host -Paging"
|
|
123
|
+
else:
|
|
124
|
+
scraper_cmd = f"{PYTHON_EXECUTABLE} -m cs2tracker --only-scrape"
|
|
125
|
+
|
|
126
|
+
cmd = (
|
|
127
|
+
'start powershell -NoExit -Command "& {'
|
|
128
|
+
+ set_utf8_encoding
|
|
129
|
+
+ set_window_title
|
|
130
|
+
+ get_size
|
|
131
|
+
+ set_window_width
|
|
132
|
+
+ set_window_height
|
|
133
|
+
+ set_size
|
|
134
|
+
+ set_background_color
|
|
135
|
+
+ clear
|
|
136
|
+
+ scraper_cmd
|
|
137
|
+
+ '}"'
|
|
138
|
+
)
|
|
139
|
+
return cmd
|
|
140
|
+
|
|
141
|
+
def _construct_scraper_command(self):
|
|
142
|
+
"""Construct the command to run the scraper in a new window."""
|
|
143
|
+
if OS == OSType.WINDOWS:
|
|
144
|
+
return self._construct_scraper_command_windows()
|
|
145
|
+
else:
|
|
146
|
+
# TODO: Implement for Linux
|
|
147
|
+
return ""
|
|
148
|
+
|
|
149
|
+
def scrape_prices(self):
|
|
72
150
|
"""Scrape prices from the configured sources, print the total, and save the
|
|
73
151
|
results to a file.
|
|
74
152
|
"""
|
|
75
|
-
|
|
153
|
+
if OS == OSType.WINDOWS:
|
|
154
|
+
scraper_cmd = self._construct_scraper_command()
|
|
155
|
+
Popen(scraper_cmd, shell=True)
|
|
156
|
+
else:
|
|
157
|
+
# TODO: implement external window for Linux
|
|
158
|
+
self.scraper.scrape_prices()
|
|
76
159
|
|
|
77
160
|
def _edit_config(self):
|
|
78
161
|
"""Edit the configuration file using the specified text editor."""
|
|
79
|
-
|
|
80
|
-
|
|
162
|
+
_popen_and_call(
|
|
163
|
+
popen_args={"args": [TEXT_EDITOR, CONFIG_FILE], "shell": True},
|
|
164
|
+
callback=self.scraper.parse_config,
|
|
165
|
+
)
|
|
81
166
|
|
|
82
167
|
def _draw_plot(self):
|
|
83
168
|
"""Draw a plot of the scraped prices over time."""
|
|
84
169
|
dates, dollars, euros = self.scraper.read_price_log()
|
|
85
170
|
|
|
86
|
-
fig, ax_raw = plt.subplots()
|
|
87
|
-
|
|
171
|
+
fig, ax_raw = plt.subplots(figsize=(10, 8), num="CS2Tracker Price History")
|
|
172
|
+
fig.suptitle("CS2Tracker Price History", fontsize=16)
|
|
173
|
+
fig.autofmt_xdate()
|
|
88
174
|
|
|
175
|
+
ax = cast(Axes, ax_raw)
|
|
89
176
|
ax.plot(dates, dollars, label="Dollars")
|
|
90
177
|
ax.plot(dates, euros, label="Euros")
|
|
91
|
-
ax.set_xlabel("Date")
|
|
92
|
-
ax.set_ylabel("Price")
|
|
93
178
|
ax.legend()
|
|
94
|
-
|
|
95
179
|
date_formatter = DateFormatter("%d-%m-%Y")
|
|
96
180
|
ax.xaxis.set_major_formatter(date_formatter)
|
|
97
|
-
fig.autofmt_xdate()
|
|
98
181
|
|
|
99
182
|
plt.show()
|
|
100
183
|
|
|
101
184
|
def _edit_log_file(self):
|
|
102
185
|
"""Opens the file containing past price calculations."""
|
|
103
|
-
|
|
104
|
-
open(OUTPUT_FILE, "w", encoding="utf-8").close()
|
|
105
|
-
subprocess.call([TEXT_EDITOR, OUTPUT_FILE])
|
|
186
|
+
Popen([TEXT_EDITOR, OUTPUT_FILE], shell=True)
|
|
106
187
|
|
|
107
188
|
def _toggle_background_task(self, enabled: bool):
|
|
108
189
|
"""Toggle whether a daily price calculation should run in the background."""
|
|
109
190
|
self.scraper.toggle_background_task(enabled)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _popen_and_call(popen_args, callback):
|
|
194
|
+
"""
|
|
195
|
+
Runs the given args in a subprocess.Popen, and then calls the function callback when
|
|
196
|
+
the subprocess completes.
|
|
197
|
+
|
|
198
|
+
Source: https://stackoverflow.com/questions/2581817/python-subprocess-callback-when-cmd-exits
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def process_and_callback(popen_args, callback):
|
|
202
|
+
process = Popen(**popen_args)
|
|
203
|
+
process.wait()
|
|
204
|
+
callback()
|
|
205
|
+
|
|
206
|
+
thread = Thread(target=process_and_callback, args=(popen_args, callback), daemon=True)
|
|
207
|
+
thread.start()
|