Untitled
unknown
plain_text
a year ago
32 kB
44
No Index
"""
EuroMillions QTRNG Correlator
Copyright (c) Jags Uprising
This script provides a GUI-based tool to correlate EuroMillions draw numbers
with a quantum random number generator (QTRNG). It includes features to:
- Input and check drawn numbers.
- Fetch numbers and validate their ranks and scores.
- Import and export data as CSV files.
Requires:
- PyFTDI for USB communication.
- Tkinter for GUI.
"""
import io
import threading
import contextlib
import tkinter as tk
from tkinter import ttk, Canvas, Frame
from datetime import datetime, timedelta
import time
import requests
from pyftdi.ftdi import Ftdi
import numpy as np
from bs4 import BeautifulSoup
from tkinter.filedialog import askopenfilename # Moved import to top-level
# Constants
DATES_ALLOWED = {"Tuesday", "Friday"}
TEST_TIME = timedelta(minutes=10) # Test duration
BITS_PER_SECOND = 620
TOTAL_NUMBERS = 50 + 12 # 50 normal + 12 lucky numbers
BITS_PER_NUMBER = 10
def initialize_ftdi():
"""Initializes the FTDI device for reading random bits."""
def list_devices():
"""Lists connected FTDI devices."""
buffer = io.StringIO()
try:
with contextlib.redirect_stdout(buffer):
Ftdi().show_devices()
output = buffer.getvalue()
for line in output.split("\n"):
if "ftdi://" in line:
return line.split()[0]
return None
except ValueError as e:
print("No FTDI USB device available:", e)
return None
device_url = list_devices()
if not device_url:
raise RuntimeError('Defaulting to "Algorithmic" bit source.')
ftdi = Ftdi()
ftdi.open_from_url(device_url)
ftdi.set_latency_timer(2)
return ftdi
def read_bits_from_ftdi(ftdi, num_bits=8):
"""
Reads bits from the FTDI device.
Args:
ftdi (Ftdi): The FTDI device instance.
num_bits (int): The number of bits to read.
Returns:
list: A list of bits read from the device.
"""
data = ftdi.read_data(num_bits)
bits = []
for byte in data:
bits.extend([(byte >> i) & 1 for i in range(7, -1, -1)])
return bits
class EuroMillionsTracker:
"""
A GUI application for tracking EuroMillions lottery data and analyzing random bit correlations.
"""
def __init__(self, master):
"""
Initializes the EuroMillionsTracker application.
Args:
master (tk.Tk): The Tkinter root window.
"""
self.master = master
self.master.title("EuroMillions QTRNG Correlator")
# Initialize attributes
self.bit_source = "FTDI USB Device" # Default bit source
self.device_available = True # USB device availability flag
try:
self.ftdi = initialize_ftdi()
except RuntimeError as e:
print(f"{e}")
self.device_available = False
self.bit_source = "Algorithmic"
self.sums = np.zeros(TOTAL_NUMBERS)
self.highest = np.full(TOTAL_NUMBERS, -np.inf)
self.lowest = np.full(TOTAL_NUMBERS, np.inf)
self.last = np.zeros(TOTAL_NUMBERS)
self.round_robin_index = 0
self.running = False
self.start_time = None
self.timestamps = []
self.sum_history = []
self.create_widgets()
def get_valid_dates(self):
"""
Calculates valid dates for EuroMillions draws.
Returns:
list: A list of valid dates as strings in YYYY-MM-DD format.
"""
base_date = datetime.now() - timedelta(days=60)
valid_dates = []
for i in range(120):
future_date = base_date + timedelta(days=i)
if future_date.strftime("%A") in DATES_ALLOWED:
valid_dates.append(future_date.strftime("%Y-%m-%d"))
return valid_dates
def import_csv(self, filename):
"""
Imports data from a CSV file.
Args:
filename (str): The path to the CSV file.
"""
try:
with open(filename, "r", encoding="utf-8") as file: # Added encoding
lines = file.readlines()
correlated_date = None
self.start_time = None
self.bit_source = None
self.timestamps = []
self.sum_history = []
# Reset statistics
self.sums = np.zeros(TOTAL_NUMBERS)
self.lowest = np.full(TOTAL_NUMBERS, np.inf)
self.highest = np.full(TOTAL_NUMBERS, -np.inf)
self.last = np.zeros(TOTAL_NUMBERS)
line_index = 0
while line_index < len(lines):
line = lines[line_index].strip()
if line.startswith("Correlated Date:"):
correlated_date = line.split(": ", 1)[1]
elif line.startswith("Test Start Time:"):
test_start_time = line.split(": ", 1)[1]
self.start_time = datetime.strptime(test_start_time, "%Y-%m-%d %H:%M:%S.%f")
elif line.startswith("Bit Source:"):
self.bit_source = line.split(": ", 1)[1]
elif line.startswith("Time (s),"):
line_index += 1
break
line_index += 1
if hasattr(self, "date_picker") and correlated_date:
self.date_picker.set(correlated_date)
while line_index < len(lines):
line = lines[line_index].strip()
if line == "":
break
parts = line.split(", ")
timestamp = float(parts[0])
sums = list(map(lambda x: int(float(x)), parts[1:TOTAL_NUMBERS + 1]))
self.timestamps.append(timestamp)
self.sum_history.append(sums)
for i in range(TOTAL_NUMBERS):
value = sums[i]
self.sums[i] = value
self.lowest[i] = min(self.lowest[i], value)
self.highest[i] = max(self.highest[i], value)
self.last[i] = value
line_index += 1
self.update_table()
print("CSV file import successful.")
except Exception as e:
print(f"Error importing CSV file: {e}")
def create_widgets(self):
"""Creates the widget interface for the application."""
# Control Frame
self.control_frame = ttk.Frame(self.master)
self.control_frame.pack(side="top", fill="x", padx=5, pady=5)
ttk.Label(self.control_frame, text="Draw Date:").pack(side="left", padx=5)
self.date_picker = ttk.Combobox(
self.control_frame, values=self.get_valid_dates(), state="readonly"
)
self.date_picker.pack(side="left", padx=5)
self.date_picker.set(self.get_valid_dates()[0])
self.run_button = ttk.Button(
self.control_frame, text="Run", command=self.run_test
)
self.run_button.pack(side="left", padx=5)
self.stop_button = ttk.Button(
self.control_frame, text="Stop", command=self.stop_test
)
self.stop_button.pack(side="left", padx=5)
self.log_file_button = ttk.Button(
self.control_frame, text="Export CSV", command=self.save_log
)
self.log_file_button.pack(side="left", padx=5)
self.bit_source_button = ttk.Button(
self.control_frame,
text=f"Source: {self.bit_source}",
command=self.toggle_bit_source,
)
self.bit_source_button.pack(side="left", padx=5)
# Disable toggle button if no USB device is available
if not self.device_available:
self.bit_source_button.config(state="disabled")
# Subtitle
subtitle = "Time: 21:00-21:10 CET, Paris, France"
self.subtitle_label = ttk.Label(
self.master,
text=f"EuroMillions RNG Correlation\n{subtitle}",
font=("Arial", 10),
)
self.subtitle_label.pack(side="top", pady=5)
# Input Frame for Drawn Numbers
self.input_frame = ttk.Frame(self.master)
self.input_frame.pack(side="top", fill="x", padx=5, pady=5)
ttk.Label(self.input_frame, text="Drawn Numbers:").grid(row=0, column=0, padx=5)
self.drawn_normal_numbers = [
ttk.Entry(self.input_frame, width=5) for _ in range(5)
]
for i, entry in enumerate(self.drawn_normal_numbers):
entry.grid(row=0, column=i + 1, padx=5)
ttk.Label(self.input_frame, text="Lucky Numbers:").grid(row=0, column=6, padx=5)
self.drawn_lucky_numbers = [
ttk.Entry(self.input_frame, width=5) for _ in range(2)
]
for i, entry in enumerate(self.drawn_lucky_numbers):
entry.grid(row=0, column=i + 7, padx=5)
# "Fetch Numbers" Button
self.fetch_button = ttk.Button(
self.input_frame,
text="Fetch Numbers",
command=self.fetch_and_fill_drawn_numbers,
)
self.fetch_button.grid(row=0, column=9, padx=5)
# Button to check the ranks
self.check_button = ttk.Button(
self.input_frame, text="Check Ranks", command=self.check_drawn_numbers
)
self.check_button.grid(row=0, column=10, padx=5)
# Add "Predicted:" labels
ttk.Label(self.input_frame, text="Predicted:").grid(row=1, column=0, padx=5)
self.predicted_normal_labels = [
ttk.Label(self.input_frame, text="") for _ in range(5)
]
for i in range(5):
self.predicted_normal_labels[i].grid(row=1, column=i + 1, padx=5)
ttk.Label(self.input_frame, text="Predicted:").grid(row=1, column=6, padx=5)
self.predicted_lucky_labels = [
ttk.Label(self.input_frame, text="") for _ in range(2)
]
for i in range(2):
self.predicted_lucky_labels[i].grid(row=1, column=i + 7, padx=5)
# Create labels for normal numbers ranks and scores (displaying results below the input)
ttk.Label(self.input_frame, text="Rank:").grid(row=2, column=0, padx=5)
self.normal_ranks_labels = [
ttk.Label(self.input_frame, text="") for _ in range(5)
]
self.normal_scores_labels = [
ttk.Label(self.input_frame, text="") for _ in range(5)
]
for i in range(5):
self.normal_ranks_labels[i].grid(row=2, column=i + 1, padx=5)
self.normal_scores_labels[i].grid(row=3, column=i + 1, padx=5)
# Create labels for lucky numbers ranks and scores
ttk.Label(self.input_frame, text="Rank:").grid(row=2, column=6, padx=5)
self.lucky_ranks_labels = [
ttk.Label(self.input_frame, text="") for _ in range(2)
]
self.lucky_scores_labels = [
ttk.Label(self.input_frame, text="") for _ in range(2)
]
for i in range(2):
self.lucky_ranks_labels[i].grid(row=2, column=i + 7, padx=5)
self.lucky_scores_labels[i].grid(row=3, column=i + 7, padx=5)
# Create "Score:" labels below the "Rank:" labels for lucky numbers
ttk.Label(self.input_frame, text="Score:").grid(row=3, column=6, padx=5)
ttk.Label(self.input_frame, text="Score:").grid(row=3, column=0, padx=5)
# Tables
self.scroll_frame = Frame(self.master)
self.scroll_frame.pack(side="top", fill="x", padx=5, pady=5)
self.create_tables()
# Add a "Import CSV" button to load a CSV file
self.import_button = ttk.Button(
self.control_frame, text="Import CSV", command=self.import_csv_dialog
)
self.import_button.pack(side="left", padx=5)
def check_drawn_numbers(self):
"""Check the ranks of the entered drawn numbers and predicted numbers."""
try:
# Get the entered numbers
normal_numbers = [
int(entry.get())
for entry in self.drawn_normal_numbers
if entry.get().isdigit()
]
lucky_numbers = [
int(entry.get())
for entry in self.drawn_lucky_numbers
if entry.get().isdigit()
]
# Check if all numbers are valid
if len(normal_numbers) != 5 or len(lucky_numbers) != 2:
print("Please enter exactly 5 normal numbers and 2 lucky numbers.")
return
# Ensure self.highest contains no invalid values
self.highest = np.nan_to_num(self.highest)
# Create sorted lists of indices based on the highest scores
normal_ranks = np.argsort(
-self.highest[:50]
) # Normal numbers
lucky_ranks = np.argsort(
-self.highest[50:]
) # Lucky numbers
# Prepare results for normal and lucky numbers
normal_scores = []
normal_ranks_list = []
for num in normal_numbers:
if 1 <= num <= 50:
index = num - 1 # Convert to zero-based index
rank_indices = np.where(normal_ranks == index)[0]
if rank_indices.size > 0: # Check if the index exists in normal_ranks
rank = rank_indices[0] + 1 # Find rank (1-based)
score = int(self.highest[index])
normal_scores.append(score)
normal_ranks_list.append(rank)
else:
normal_scores.append("Rank not found")
normal_ranks_list.append("N/A")
else:
normal_scores.append("Invalid")
normal_ranks_list.append("Invalid")
lucky_scores = []
lucky_ranks_list = []
for num in lucky_numbers:
if 1 <= num <= 12:
index = 50 + num - 1 # Convert to zero-based index in lucky numbers
rank_indices = np.where(lucky_ranks == (num - 1))[0]
if rank_indices.size > 0: # Check if the index exists in lucky_ranks
rank = rank_indices[0] + 1 # Find rank (1-based)
score = int(self.highest[index])
lucky_scores.append(score)
lucky_ranks_list.append(rank)
else:
lucky_scores.append("Rank not found")
lucky_ranks_list.append("N/A")
else:
lucky_scores.append("Invalid")
lucky_ranks_list.append("Invalid")
# Calculate the predicted numbers
predicted_normal_indices = normal_ranks[:5]
predicted_lucky_indices = lucky_ranks[:2]
predicted_normal_numbers = [idx + 1 for idx in predicted_normal_indices]
predicted_normal_scores = [int(self.highest[idx]) for idx in predicted_normal_indices]
predicted_lucky_numbers = [idx + 1 for idx in predicted_lucky_indices]
predicted_lucky_scores = [int(self.highest[50 + idx]) for idx in predicted_lucky_indices]
# Display the predicted numbers and their scores
for i, label in enumerate(self.predicted_normal_labels):
label.config(text=f"{predicted_normal_numbers[i]} ({predicted_normal_scores[i]})")
for i, label in enumerate(self.predicted_lucky_labels):
label.config(text=f"{predicted_lucky_numbers[i]} ({predicted_lucky_scores[i]})")
# Display the ranks and scores for drawn numbers
for i in range(5):
self.normal_ranks_labels[i].config(text=normal_ranks_list[i])
self.normal_scores_labels[i].config(text=normal_scores[i])
for i in range(2):
self.lucky_ranks_labels[i].config(text=lucky_ranks_list[i])
self.lucky_scores_labels[i].config(text=lucky_scores[i])
except Exception as e:
print(f"Error checking numbers: {e}")
# Update Normal Numbers Ranks and Scores in respective labels
for i, (num, rank, score) in enumerate(
zip(normal_numbers, normal_ranks_list, normal_scores)
):
# Just show the rank and score without color or bold formatting
self.normal_ranks_labels[i].config(text=str(rank))
self.normal_scores_labels[i].config(text=str(score))
# Update Lucky Numbers Ranks and Scores in respective labels
for i, (num, rank, score) in enumerate(
zip(lucky_numbers, lucky_ranks_list, lucky_scores)
):
# Just show the rank and score without color or bold formatting
self.lucky_ranks_labels[i].config(text=str(rank))
self.lucky_scores_labels[i].config(text=str(score))
except Exception as e:
print(f"Error checking ranks: {e}")
def fetch_and_fill_drawn_numbers(self):
"""Fetch drawn numbers from the website and fill them in the input fields."""
try:
# Get the selected date from the combobox
selected_date = self.date_picker.get()
if not selected_date:
print("No date selected.")
return
# Fetch the drawn numbers from the website
drawn_numbers = self.fetch_drawn_numbers_from_web(selected_date)
# Update the fields with fetched numbers
if drawn_numbers: # Ensure we got numbers before updating fields
normal_numbers, lucky_numbers = drawn_numbers
for i in range(5):
self.drawn_normal_numbers[i].delete(0, "end")
self.drawn_normal_numbers[i].insert(0, str(normal_numbers[i]))
for i in range(2):
self.drawn_lucky_numbers[i].delete(0, "end")
self.drawn_lucky_numbers[i].insert(0, str(lucky_numbers[i]))
print("Fetched and updated drawn numbers successfully.")
else:
print("Failed to fetch numbers. Please try again or enter manually.")
except Exception as e:
print(f"Error fetching drawn numbers: {e}")
def fetch_drawn_numbers_from_web(self, selected_date):
"""
Scrape drawn numbers from the website based on the selected date.
Args:
selected_date (str): The date to fetch the drawn numbers for.
Returns:
tuple: Normal numbers and lucky numbers scraped from the website.
"""
try:
# Convert the string date to a datetime object
date_obj = datetime.strptime(selected_date, "%Y-%m-%d")
formatted_date = date_obj.strftime("%d-%m-%Y")
url = f"https://www.lottery.co.uk/euromillions/results-{formatted_date}"
response = requests.get(url, timeout=5) # Added timeout
if response.status_code != 200:
raise Exception(
f"Failed to fetch webpage. Status code: {response.status_code}"
)
soup = BeautifulSoup(response.content, "html.parser")
balls = soup.select("#ballsAscending span")
if len(balls) < 7:
raise ValueError("Unable to find all drawn numbers on the webpage.")
# Extract numbers
normal_numbers = [int(balls[i].text) for i in range(5)]
lucky_numbers = [int(balls[i].text) for i in range(5, 7)]
return normal_numbers, lucky_numbers
except ValueError as ve:
print(f"ValueError: {ve}")
return None
except requests.RequestException as re:
print(f"RequestException: {re}")
return None
except Exception as e:
print(f"Error scraping website for drawn numbers: {e}")
return None
def import_csv_dialog(self):
"""Open a dialog to select a CSV file for import."""
filename = askopenfilename(filetypes=[("CSV Files", "*.csv")])
if filename:
self.import_csv(filename)
def toggle_bit_source(self):
"""Toggle between USB Device and Algorithmic Random Bit sources."""
if not self.device_available:
print("FTDI USB device is not available. Cannot toggle to 'FTDI USB Device'.")
return
self.bit_source = "Algorithmic" if self.bit_source == "FTDI USB Device" else "FTDI USB Device"
self.bit_source_button.config(text=f"Source: {self.bit_source}")
def create_tables(self):
"""Create 1 scrollable table for all normal numbers and 1 for lucky numbers."""
self.labels = [] # Store all labels for updating
table_frame = Frame(self.scroll_frame)
table_frame.pack(side="top", fill="x", pady=10)
# Create 1 scrollable table for all normal numbers (50 numbers)
frame = Frame(table_frame, relief="ridge", borderwidth=2)
frame.pack(side="top", padx=5)
ttk.Label(frame, text="Normal Numbers", font=("Arial", 10, "bold")).grid(
row=0, column=0, columnspan=4, pady=5
)
# Column Headers for Normal Numbers
headers = ["Number", "Lowest", "Highest", "Last"]
for col_idx, header in enumerate(headers):
header_label = ttk.Label(frame, text=header, font=("Arial", 10, "bold"))
header_label.grid(row=1, column=col_idx, padx=5, pady=5)
# Create a canvas with a scrollbar for scrolling the table rows
canvas = Canvas(frame, height=115)
canvas.grid(row=2, column=0, columnspan=4, padx=5, pady=5)
scrollbar = ttk.Scrollbar(frame, orient="vertical", command=canvas.yview)
scrollbar.grid(row=2, column=4, sticky="ns")
canvas.configure(yscrollcommand=scrollbar.set)
canvas_frame = Frame(canvas)
canvas.create_window((0, 0), window=canvas_frame, anchor="nw")
# Store normal number rows as labels
self.normal_number_rows = []
# Add rows for normal numbers (50 total)
for num_idx in range(50):
row = Frame(canvas_frame)
row.grid(row=num_idx, column=0, sticky="w", padx=5, pady=2)
# Align the row content labels properly under the column headers
ttk.Label(row, text=f"Num {num_idx + 1}", width=16, anchor="w").grid(
row=0, column=0, padx=5
)
lowest_label = ttk.Label(row, text="N/A", width=16, anchor="w")
lowest_label.grid(row=0, column=1, padx=5)
highest_label = ttk.Label(row, text="N/A", width=16, anchor="w")
highest_label.grid(row=0, column=2, padx=5)
last_label = ttk.Label(row, text="N/A", width=16, anchor="w")
last_label.grid(row=0, column=3, padx=5)
self.normal_number_rows.append((lowest_label, highest_label, last_label))
# Update canvas scrolling region for normal numbers
canvas_frame.update_idletasks()
canvas.config(scrollregion=canvas.bbox("all"))
# Lucky Numbers Table (below the normal numbers table)
frame = Frame(table_frame, relief="ridge", borderwidth=2)
frame.pack(side="top", padx=5, pady=10)
ttk.Label(frame, text="Lucky Numbers", font=("Arial", 10, "bold")).grid(
row=0, column=0, columnspan=4, pady=5
)
# Column Headers for Lucky Numbers
for col_idx, header in enumerate(headers):
ttk.Label(frame, text=header, font=("Arial", 10, "bold")).grid(
row=1, column=col_idx, padx=5, pady=5
)
# Create a canvas with a scrollbar for lucky numbers
canvas = Canvas(frame, height=115)
canvas.grid(row=2, column=0, columnspan=4, padx=5, pady=5)
scrollbar = ttk.Scrollbar(frame, orient="vertical", command=canvas.yview)
scrollbar.grid(row=2, column=4, sticky="ns")
canvas.configure(yscrollcommand=scrollbar.set)
canvas_frame = Frame(canvas)
canvas.create_window((0, 0), window=canvas_frame, anchor="nw")
# Store lucky number rows as labels
self.lucky_number_rows = []
# Add rows for lucky numbers (12 total)
for num_idx in range(12):
row = Frame(canvas_frame)
row.grid(row=num_idx, column=0, sticky="w", padx=5, pady=2)
# Align the row content labels properly under the column headers
ttk.Label(row, text=f"Lucky {num_idx + 1}", width=16, anchor="w").grid(
row=0, column=0, padx=5
)
lowest_label = ttk.Label(row, text="N/A", width=16, anchor="w")
lowest_label.grid(row=0, column=1, padx=5)
highest_label = ttk.Label(row, text="N/A", width=16, anchor="w")
highest_label.grid(row=0, column=2, padx=5)
last_label = ttk.Label(row, text="N/A", width=16, anchor="w")
last_label.grid(row=0, column=3, padx=5)
self.lucky_number_rows.append((lowest_label, highest_label, last_label))
# Update canvas scrolling region for lucky numbers
canvas_frame.update_idletasks()
canvas.config(scrollregion=canvas.bbox("all"))
def update_table(self):
"""Update the statistics table in the GUI."""
current_time = time.time() # Get the current time in seconds
if not hasattr(self, "last_ui_update_time"):
self.last_ui_update_time = current_time
if current_time - self.last_ui_update_time >= 1:
self.last_ui_update_time = current_time
# Update the rows
for i, (lowest_label, highest_label, last_label) in enumerate(
self.normal_number_rows
):
lowest_label.config(text=str(int(self.lowest[i]))) # Convert to integer
highest_label.config(text=str(int(self.highest[i]))) # Convert to integer
last_label.config(text=str(int(self.last[i]))) # Convert to integer
# Do the same for lucky numbers (if needed)
for i, (lowest_label, highest_label, last_label) in enumerate(
self.lucky_number_rows
):
lowest_label.config(
text=str(int(self.lowest[50 + i]))
) # Convert to integer
highest_label.config(
text=str(int(self.highest[50 + i]))
) # Convert to integer
last_label.config(text=str(int(self.last[50 + i]))) # Convert to integer
def run_test(self):
"""Start the data collection process."""
if self.running:
return
self.running = True
threading.Thread(target=self.collect_data, daemon=True).start()
def stop_test(self):
"""Stop the data collection process."""
self.running = False
def collect_data(self):
"""Collect data from the random bit source."""
try:
if self.bit_source == "USB Device":
self.ftdi = initialize_ftdi()
except RuntimeError as e:
print("No FTDI device available:", e)
self.running = False
return
self.start_time = datetime.now()
end_time = self.start_time + TEST_TIME
last_saved_second = -1 # Track the last saved second
while self.running and datetime.now() < end_time:
if self.bit_source == "USB Device":
bits = read_bits_from_ftdi(self.ftdi, num_bits=BITS_PER_SECOND)
else:
bits = np.random.choice([0, 1], size=BITS_PER_SECOND).tolist()
for bit in bits:
self.update_statistics(bit)
self.round_robin_index = (self.round_robin_index + 1) % TOTAL_NUMBERS
# Check if we have moved to the next full second
current_second = int((datetime.now() - self.start_time).total_seconds())
if current_second > last_saved_second:
last_saved_second = current_second
# Record the sums only for the full second
self.timestamps.append(current_second)
self.sum_history.append(self.last.copy())
self.update_table()
def update_statistics(self, bit):
"""Update statistics based on the received bit."""
value = 1 if bit == 1 else -1
index = self.round_robin_index
self.sums[index] = int(self.sums[index] + value) # Ensure integer sum
self.last[index] = int(self.sums[index]) # Ensure last is an integer
self.highest[index] = int(
max(self.highest[index], self.sums[index])
) # Ensure highest is an integer
self.lowest[index] = int(
min(self.lowest[index], self.sums[index])
) # Ensure lowest is an integer
def save_log(self):
"""Save the collected data to a CSV file."""
if not self.start_time or not self.timestamps:
print("No data to save.")
return
selected_date = self.date_picker.get()
formatted_date = datetime.strptime(selected_date, "%Y-%m-%d").strftime(
"%Y-%m-%d"
)
log_filename = f"EuroMillions_Log_{formatted_date}_{self.start_time.strftime('%Y%m%d_%H%M%S')}.csv"
try:
with open(log_filename, "w", encoding="utf-8") as log_file: # Added encoding
# Write the header
log_file.write(f"Correlated Date: {formatted_date}\n")
log_file.write("Correlated Time: 21:00-21:10 CET\n")
log_file.write("Correlated Place: Paris, France\n")
log_file.write(f"Test Start Time: {self.start_time}\n")
log_file.write(f"Bit Source: {self.bit_source}\n\n")
# Write column headers: First 50 are normal numbers, the last 12 are lucky numbers
headers = (
["Time (s)"]
+ [f"Num {i+1}" for i in range(50)]
+ [f"Lucky {i+1}" for i in range(12)]
)
log_file.write(", ".join(headers) + "\n")
# Write the recorded data
for i, timestamp in enumerate(self.timestamps):
row = (
[str(timestamp)]
+ [str(self.sum_history[i][j]) for j in range(50)]
+ [str(self.sum_history[i][50 + j]) for j in range(12)]
)
log_file.write(", ".join(row) + "\n")
# Write Lowest, Highest, and Last for each number at the end of the log file
log_file.write("\nLowest, Highest, Last Values:\n")
for i in range(50): # For normal numbers (1-50)
log_file.write(
f"Num {i+1}: {int(self.lowest[i])}, {int(self.highest[i])}, {int(self.last[i])}\n"
)
for i in range(12): # For lucky numbers (51-62)
log_file.write(
f"Lucky {i+1}: {int(self.lowest[50 + i])}, {int(self.highest[50 + i])}, {int(self.last[50 + i])}\n"
)
print(f"Data exported saved successfully: {log_filename}")
except IOError as e:
print(f"Error exporting data: {e}")
# Main
if __name__ == "__main__":
root = tk.Tk()
root.geometry("725x610")
app = EuroMillionsTracker(root)
root.mainloop()Editor is loading...
Leave a Comment