cs2tracker 2.1.2__tar.gz → 2.1.3__tar.gz

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.

Files changed (28) hide show
  1. cs2tracker-2.1.3/.flake8 +4 -0
  2. cs2tracker-2.1.3/.pylintrc +10 -0
  3. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/PKG-INFO +3 -2
  4. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/README.md +2 -1
  5. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/_version.py +2 -2
  6. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/application.py +25 -9
  7. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/constants.py +7 -5
  8. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/data/config.ini +4 -4
  9. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/main.py +1 -1
  10. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/scraper.py +108 -24
  11. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/PKG-INFO +3 -2
  12. cs2tracker-2.1.2/.flake8 +0 -5
  13. cs2tracker-2.1.2/.pylintrc +0 -13
  14. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/.gitignore +0 -0
  15. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/.isort.cfg +0 -0
  16. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/.pre-commit-config.yaml +0 -0
  17. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/LICENSE.md +0 -0
  18. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/MANIFEST.in +0 -0
  19. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/__init__.py +0 -0
  20. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker/__main__.py +0 -0
  21. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/SOURCES.txt +0 -0
  22. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/dependency_links.txt +0 -0
  23. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/entry_points.txt +0 -0
  24. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/requires.txt +0 -0
  25. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/cs2tracker.egg-info/top_level.txt +0 -0
  26. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/pyproject.toml +0 -0
  27. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/requirements.txt +0 -0
  28. {cs2tracker-2.1.2 → cs2tracker-2.1.3}/setup.cfg +0 -0
@@ -0,0 +1,4 @@
1
+ [flake8]
2
+ ignore = E501, W503
3
+ max-complexity = 18
4
+ select = B,C,E,F,W,T4,B9
@@ -0,0 +1,10 @@
1
+ [MESSAGES CONTROL]
2
+ disable=
3
+ fixme,
4
+ line-too-long,
5
+ missing-module-docstring,
6
+ missing-class-docstring,
7
+ too-few-public-methods,
8
+ consider-using-with,
9
+ import-error,
10
+ no-else-return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cs2tracker
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: Tracking the steam market prices of CS2 items
5
5
  Home-page: https://github.com/ashiven/cs2tracker
6
6
  Author: Jannik Novak
@@ -60,7 +60,8 @@ Dynamic: license-file
60
60
 
61
61
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
62
62
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
63
- - `Show History` to see a price chart consisting of past calculations. A new data point for this chart is generated once a day upon running the program.
63
+ - `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
64
+ - `Daily Background Calculation` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
64
65
  - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field at the end of the config file. This will route every request through a different proxy server.
65
66
 
66
67
  ---
@@ -37,7 +37,8 @@
37
37
 
38
38
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
39
39
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
40
- - `Show History` to see a price chart consisting of past calculations. A new data point for this chart is generated once a day upon running the program.
40
+ - `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
41
+ - `Daily Background Calculation` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
41
42
  - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field at the end of the config file. This will route every request through a different proxy server.
42
43
 
43
44
  ---
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.1.2'
21
- __version_tuple__ = version_tuple = (2, 1, 2)
20
+ __version__ = version = '2.1.3'
21
+ __version_tuple__ = version_tuple = (2, 1, 3)
@@ -28,31 +28,43 @@ class Application:
28
28
  """
29
29
  window = tk.Tk()
30
30
  window.title("CS2Tracker")
31
- window.geometry("400x400")
31
+ window.geometry("450x450")
32
32
 
33
33
  label = tk.Label(window, text="Welcome to CS2Tracker!")
34
- label.grid(column=0, row=0, pady=50, sticky="NSEW")
35
-
36
34
  run_button = tk.Button(window, text="Run!", command=self._scrape_prices)
37
35
  edit_button = tk.Button(window, text="Edit Config", command=self._edit_config)
38
36
  plot_button = tk.Button(window, text="Show History (Chart)", command=self._draw_plot)
39
- plotfile_button = tk.Button(window, text="Show History (File)", command=self._edit_log_file)
40
-
37
+ plot_file_button = tk.Button(
38
+ window, text="Show History (File)", command=self._edit_log_file
39
+ )
40
+ background_checkbox_value = tk.BooleanVar(value=self.scraper.identify_background_task())
41
+ background_checkbox = tk.Checkbutton(
42
+ window,
43
+ text="Daily Background Calculation",
44
+ command=lambda: self._toggle_background_task(background_checkbox_value.get()),
45
+ variable=background_checkbox_value,
46
+ )
47
+
48
+ label.grid(row=0, column=0, pady=50, sticky="NSEW")
41
49
  run_button.grid(row=1, column=0, pady=10, sticky="NSEW")
42
50
  edit_button.grid(row=2, column=0, pady=10, sticky="NSEW")
43
51
  plot_button.grid(row=3, column=0, pady=10, sticky="NSEW")
44
- plotfile_button.grid(row=4, 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")
45
54
 
46
55
  window.grid_columnconfigure(0, weight=1)
47
56
  window.grid_rowconfigure(1, weight=1)
48
57
  window.grid_rowconfigure(2, weight=1)
49
58
  window.grid_rowconfigure(3, weight=1)
50
59
  window.grid_rowconfigure(4, weight=1)
60
+ window.grid_rowconfigure(5, weight=1)
61
+
51
62
  label.grid_configure(sticky="NSEW")
52
63
  run_button.grid_configure(sticky="NSEW")
53
64
  edit_button.grid_configure(sticky="NSEW")
54
65
  plot_button.grid_configure(sticky="NSEW")
55
- plotfile_button.grid_configure(sticky="NSEW")
66
+ plot_file_button.grid_configure(sticky="NSEW")
67
+ background_checkbox.grid_configure(sticky="NSEW")
56
68
 
57
69
  return window
58
70
 
@@ -80,8 +92,8 @@ class Application:
80
92
  ax.set_ylabel("Price")
81
93
  ax.legend()
82
94
 
83
- date_form = DateFormatter("%d-%m-%Y")
84
- ax.xaxis.set_major_formatter(date_form)
95
+ date_formatter = DateFormatter("%d-%m-%Y")
96
+ ax.xaxis.set_major_formatter(date_formatter)
85
97
  fig.autofmt_xdate()
86
98
 
87
99
  plt.show()
@@ -91,3 +103,7 @@ class Application:
91
103
  if not os.path.isfile(OUTPUT_FILE):
92
104
  open(OUTPUT_FILE, "w", encoding="utf-8").close()
93
105
  subprocess.call([TEXT_EDITOR, OUTPUT_FILE])
106
+
107
+ def _toggle_background_task(self, enabled: bool):
108
+ """Toggle whether a daily price calculation should run in the background."""
109
+ self.scraper.toggle_background_task(enabled)
@@ -1,10 +1,13 @@
1
1
  import os
2
2
  import sys
3
3
 
4
- TEXT_EDITOR = "notepad" if sys.platform == "win32" else "nano"
5
- BASE_DIR = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/")
6
- OUTPUT_FILE = f"{BASE_DIR}/data/output.csv"
7
- CONFIG_FILE = f"{BASE_DIR}/data/config.ini"
4
+ TEXT_EDITOR = "notepad" if sys.platform.startswith("win") else "nano"
5
+ PYTHON_EXECUTABLE = sys.executable
6
+ MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
7
+ PROJECT_DIR = os.path.dirname(MODULE_DIR)
8
+ OUTPUT_FILE = os.path.join(MODULE_DIR, "data", "output.csv")
9
+ CONFIG_FILE = os.path.join(MODULE_DIR, "data", "config.ini")
10
+ BATCH_FILE = os.path.join(MODULE_DIR, "data", "cs2tracker_scraper.bat")
8
11
 
9
12
 
10
13
  RMR_CAPSULES = {
@@ -145,7 +148,6 @@ SHANGHAI_CAPSULES = {
145
148
  ],
146
149
  }
147
150
 
148
-
149
151
  AUSTIN_CAPSULES = {
150
152
  "page": "https://steamcommunity.com/market/search?q=austin+capsule",
151
153
  "items": [
@@ -1,3 +1,7 @@
1
+ [Settings]
2
+ Use_Proxy = False
3
+ API_Key = None
4
+
1
5
  [2020 RMR Sticker Capsule]
2
6
  RMR_Challengers = 0
3
7
  RMR_Legends = 0
@@ -107,7 +111,3 @@ Winter_Offensive_Case = 0
107
111
  Kilowatt_Case = 0
108
112
  Gallery_Case = 0
109
113
  Fever_Case = 0
110
-
111
- [Settings]
112
- Use_Proxy = False
113
- API_Key = None
@@ -15,7 +15,7 @@ def main():
15
15
  application.
16
16
  """
17
17
 
18
- ## disable warnings for proxy requests
18
+ # Disable warnings for proxy requests
19
19
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
20
20
 
21
21
  console = Console()
@@ -1,8 +1,10 @@
1
1
  import csv
2
2
  import os
3
+ import sys
3
4
  import time
4
5
  from configparser import ConfigParser
5
6
  from datetime import datetime
7
+ from subprocess import DEVNULL, call
6
8
 
7
9
  from bs4 import BeautifulSoup
8
10
  from bs4.element import Tag
@@ -13,16 +15,21 @@ from rich.console import Console
13
15
  from tenacity import RetryError, retry, stop_after_attempt
14
16
 
15
17
  from cs2tracker.constants import (
18
+ BATCH_FILE,
16
19
  CAPSULE_INFO,
17
20
  CASE_HREFS,
18
21
  CASE_PAGES,
19
22
  CONFIG_FILE,
20
23
  OUTPUT_FILE,
24
+ PROJECT_DIR,
25
+ PYTHON_EXECUTABLE,
21
26
  )
22
27
 
23
28
  MAX_LINE_LEN = 72
24
29
  SEPARATOR = "-"
25
30
  PRICE_INFO = "Owned: {} Steam market price: ${} Total: ${}\n"
31
+ BACKGROUND_TASK_NAME = "CS2Tracker Daily Calculation"
32
+ BACKGROUND_TASK_TIME = "12:00"
26
33
 
27
34
 
28
35
  class Scraper:
@@ -61,7 +68,7 @@ class Scraper:
61
68
  capsule_usd_total = self.scrape_capsule_section_prices()
62
69
  except (RequestException, AttributeError, RetryError, ValueError):
63
70
  self.console.print(
64
- "[bold red]Failed to scrape capsule prices. (Consider using proxies to prevent rate limiting)\n"
71
+ "[bold red][!] Failed to scrape capsule prices. (Consider using proxies to prevent rate limiting)\n"
65
72
  )
66
73
 
67
74
  case_usd_total = 0
@@ -69,7 +76,7 @@ class Scraper:
69
76
  case_usd_total = self._scrape_case_prices()
70
77
  except (RequestException, AttributeError, RetryError, ValueError):
71
78
  self.console.print(
72
- "[bold red]Failed to scrape case prices. (Consider using proxies to prevent rate limiting)\n"
79
+ "[bold red][!] Failed to scrape case prices. (Consider using proxies to prevent rate limiting)\n"
73
80
  )
74
81
 
75
82
  self.usd_total += capsule_usd_total
@@ -79,7 +86,7 @@ class Scraper:
79
86
  self._print_total()
80
87
  self._save_price_log()
81
88
 
82
- # reset totals for next run
89
+ # Reset totals for next run
83
90
  self.usd_total, self.eur_total = 0, 0
84
91
 
85
92
  def _print_total(self):
@@ -109,13 +116,9 @@ class Scraper:
109
116
 
110
117
  with open(OUTPUT_FILE, "r", encoding="utf-8") as price_logs:
111
118
  price_logs_reader = csv.reader(price_logs)
112
- last_row = None
119
+ last_log_date = ""
113
120
  for row in price_logs_reader:
114
- last_row = row
115
- if last_row:
116
- last_log_date = last_row[0][:10]
117
- else:
118
- last_log_date = ""
121
+ last_log_date, _ = row
119
122
 
120
123
  today = datetime.now().strftime("%Y-%m-%d")
121
124
  if last_log_date != today:
@@ -135,7 +138,7 @@ class Scraper:
135
138
  open(OUTPUT_FILE, "w", encoding="utf-8").close()
136
139
 
137
140
  dates, dollars, euros = [], [], []
138
- with open(OUTPUT_FILE, "r", newline="", encoding="utf-8") as price_logs:
141
+ with open(OUTPUT_FILE, "r", encoding="utf-8") as price_logs:
139
142
  price_logs_reader = csv.reader(price_logs)
140
143
  for row in price_logs_reader:
141
144
  date, price_with_currency = row
@@ -217,18 +220,15 @@ class Scraper:
217
220
  console.
218
221
 
219
222
  :param capsule_section: The section name in the config for the capsule.
220
- :param capsule_info: A dictionary containing information about the capsule
221
- section,
223
+ :param capsule_info: A dictionary containing information about the capsule page,
224
+ hrefs, and names.
222
225
  """
223
226
  capsule_title = capsule_section.center(MAX_LINE_LEN, SEPARATOR)
224
227
  self.console.print(f"[bold magenta]{capsule_title}")
225
228
 
226
- capsule_price_total = 0
227
- capsule_page = capsule_info["page"]
228
- capsule_names = capsule_info["names"]
229
- capsule_hrefs = capsule_info["items"]
230
- capsule_page = self._get_page(capsule_page)
231
- for capsule_name, capsule_href in zip(capsule_names, capsule_hrefs):
229
+ capsule_usd_total = 0
230
+ capsule_page = self._get_page(capsule_info["page"])
231
+ for capsule_name, capsule_href in zip(capsule_info["names"], capsule_info["items"]):
232
232
  config_capsule_name = capsule_name.replace(" ", "_")
233
233
  owned = self.config.getint(capsule_section, config_capsule_name, fallback=0)
234
234
  if owned == 0:
@@ -239,15 +239,15 @@ class Scraper:
239
239
 
240
240
  self.console.print(f"[bold deep_sky_blue4]{capsule_name}")
241
241
  self.console.print(PRICE_INFO.format(owned, price_usd, price_usd_owned))
242
- capsule_price_total += price_usd_owned
242
+ capsule_usd_total += price_usd_owned
243
243
 
244
- return capsule_price_total
244
+ return capsule_usd_total
245
245
 
246
246
  def scrape_capsule_section_prices(self):
247
247
  """Scrape prices for all capsule sections defined in the configuration."""
248
248
  capsule_usd_total = 0
249
249
  for capsule_section, capsule_info in CAPSULE_INFO.items():
250
- # only scrape capsule sections where the user owns at least one item
250
+ # Only scrape capsule sections where the user owns at least one item
251
251
  if any(int(owned) > 0 for _, owned in self.config.items(capsule_section)):
252
252
  capsule_usd_total += self._scrape_capsule_prices(capsule_section, capsule_info)
253
253
 
@@ -283,7 +283,7 @@ class Scraper:
283
283
  For each case, it prints the case name, owned count, price per item, and total
284
284
  price for owned items.
285
285
  """
286
- case_price_total = 0
286
+ case_usd_total = 0
287
287
  for case_index, (config_case_name, owned) in enumerate(self.config.items("Cases")):
288
288
  if int(owned) == 0:
289
289
  continue
@@ -297,9 +297,93 @@ class Scraper:
297
297
  price_usd_owned = round(float(int(owned) * price_usd), 2)
298
298
 
299
299
  self.console.print(PRICE_INFO.format(owned, price_usd, price_usd_owned))
300
- case_price_total += price_usd_owned
300
+ case_usd_total += price_usd_owned
301
301
 
302
302
  if not self.config.getboolean("Settings", "Use_Proxy", fallback=False):
303
303
  time.sleep(1)
304
304
 
305
- return case_price_total
305
+ return case_usd_total
306
+
307
+ def identify_background_task(self):
308
+ """
309
+ Search the OS for a daily background task that runs the scraper.
310
+
311
+ :return: True if a background task is found, False otherwise.
312
+ """
313
+ if sys.platform.startswith("win"):
314
+ cmd = ["schtasks", "/query", "/tn", BACKGROUND_TASK_NAME]
315
+ return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
316
+ found = return_code == 0
317
+ return found
318
+ else:
319
+ # TODO: implement finder for cron jobs
320
+ return False
321
+
322
+ def _toggle_task_batch_file(self, enabled: bool):
323
+ """
324
+ Create or delete a batch file that runs the scraper.
325
+
326
+ :param enabled: If True, the batch file will be created; if False, the batch
327
+ file will be deleted.
328
+ """
329
+ if enabled:
330
+ with open(BATCH_FILE, "w", encoding="utf-8") as batch_file:
331
+ batch_file.write(f"cd {PROJECT_DIR}\n")
332
+ batch_file.write(f"{PYTHON_EXECUTABLE} -m cs2tracker.scraper\n")
333
+ else:
334
+ if os.path.exists(BATCH_FILE):
335
+ os.remove(BATCH_FILE)
336
+
337
+ def _toggle_background_task_windows(self, enabled: bool):
338
+ """
339
+ Create or delete a daily background task that runs the scraper on Windows.
340
+
341
+ :param enabled: If True, the task will be created; if False, the task will be
342
+ deleted.
343
+ """
344
+ self._toggle_task_batch_file(enabled)
345
+ if enabled:
346
+ cmd = [
347
+ "schtasks",
348
+ "/create",
349
+ "/tn",
350
+ BACKGROUND_TASK_NAME,
351
+ "/tr",
352
+ BATCH_FILE,
353
+ "/sc",
354
+ "DAILY",
355
+ "/st",
356
+ BACKGROUND_TASK_TIME,
357
+ ]
358
+ return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
359
+ if return_code == 0:
360
+ self.console.print("[bold green][+] Background task enabled.")
361
+ else:
362
+ self.console.print("[bold red][!] Failed to enable background task.")
363
+ else:
364
+ cmd = ["schtasks", "/delete", "/tn", BACKGROUND_TASK_NAME, "/f"]
365
+ return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
366
+ if return_code == 0:
367
+ self.console.print("[bold green][-] Background task disabled.")
368
+ else:
369
+ self.console.print("[bold red][!] Failed to disable background task.")
370
+
371
+ def toggle_background_task(self, enabled: bool):
372
+ """
373
+ Create or delete a daily background task that runs the scraper.
374
+
375
+ :param enabled: If True, the task will be created; if False, the task will be
376
+ deleted.
377
+ """
378
+ if sys.platform.startswith("win"):
379
+ self._toggle_background_task_windows(enabled)
380
+ else:
381
+ # TODO: implement toggle for cron jobs
382
+ pass
383
+
384
+
385
+ if __name__ == "__main__":
386
+ # If this file is run as a script, create a Scraper instance and run the
387
+ # scrape_prices method.
388
+ scraper = Scraper()
389
+ scraper.scrape_prices()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cs2tracker
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: Tracking the steam market prices of CS2 items
5
5
  Home-page: https://github.com/ashiven/cs2tracker
6
6
  Author: Jannik Novak
@@ -60,7 +60,8 @@ Dynamic: license-file
60
60
 
61
61
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
62
62
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
63
- - `Show History` to see a price chart consisting of past calculations. A new data point for this chart is generated once a day upon running the program.
63
+ - `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
64
+ - `Daily Background Calculation` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
64
65
  - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field at the end of the config file. This will route every request through a different proxy server.
65
66
 
66
67
  ---
cs2tracker-2.1.2/.flake8 DELETED
@@ -1,5 +0,0 @@
1
- [flake8]
2
- ignore = E203, E266, E501, W503, F403, F401, F541, W605
3
- max-line-length = 100
4
- max-complexity = 18
5
- select = B,C,E,F,W,T4,B9
@@ -1,13 +0,0 @@
1
- [MESSAGES CONTROL]
2
- disable=
3
- too-many-instance-attributes,
4
- too-many-arguments,
5
- too-many-positional-arguments,
6
- too-many-locals,
7
- line-too-long,
8
- missing-module-docstring,
9
- missing-class-docstring,
10
- too-few-public-methods,
11
- import-error,
12
- simplifiable-if-expression,
13
- consider-using-with
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes