diff --git a/CSS436/Week2.md b/CSS436/Week2.md
new file mode 100644
index 0000000..b7764a5
--- /dev/null
+++ b/CSS436/Week2.md
@@ -0,0 +1,2 @@
+# chapter 3
+Locard's principle of transferrence
diff --git a/Crowdstrike/ExportReportsScript b/Crowdstrike/ExportReportsScript
new file mode 100644
index 0000000..e69de29
diff --git a/Crowdstrike/ExposureReports.py b/Crowdstrike/ExposureReports.py
new file mode 100644
index 0000000..238f1ce
--- /dev/null
+++ b/Crowdstrike/ExposureReports.py
@@ -0,0 +1,70 @@
+import requests
+import json
+import csv
+from datetime import datetime, timedelta
+
+# Replace with your CrowdStrike API credentials
+CLIENT_ID = 'YOUR_CLIENT_ID'
+CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
+
+# Base URL for the CrowdStrike Falcon API
+BASE_URL = 'https://api.crowdstrike.com'
+
+# Function to obtain an OAuth2 access token
+def get_access_token():
+ url = f"{BASE_URL}/oauth2/token"
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+ data = {
+ 'client_id': CLIENT_ID,
+ 'client_secret': CLIENT_SECRET,
+ 'grant_type': 'client_credentials'
+ }
+ response = requests.post(url, headers=headers, data=data)
+ response.raise_for_status()
+ return response.json()['access_token']
+
+# Function to fetch exposure management reports (V2 API)
+def get_exposure_management_reports(access_token):
+ url = f"{BASE_URL}/spotlight/queries/reports/v2"
+ headers = {
+ 'Authorization': f'Bearer {access_token}',
+ 'Accept': 'application/json'
+ }
+
+ # Calculate the timestamp for 24 hours ago
+ now = datetime.utcnow()
+ twenty_four_hours_ago = now - timedelta(hours=24)
+ timestamp_filter = twenty_four_hours_ago.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+ # Filter for reports generated in the last 24 hours
+ params = {
+ 'filter': f'created_on:>{timestamp_filter}'
+ }
+
+ response = requests.get(url, headers=headers, params=params)
+ response.raise_for_status()
+ return response.json()['resources']
+
+# Function to export a report as CSV (V2 API)
+def export_report_to_csv(report_id, access_token):
+ url = f"{BASE_URL}/spotlight/entities/reports-executions/v1/{report_id}/download"
+ headers = {'Authorization': f'Bearer {access_token}'}
+
+ response = requests.get(url, headers=headers)
+ response.raise_for_status()
+
+ # Assuming the report content is in CSV format
+ with open(f'{report_id}.csv', 'wb') as f:
+ f.write(response.content)
+
+# Main script execution
+if __name__ == '__main__':
+ access_token = get_access_token()
+ reports = get_exposure_management_reports(access_token)
+
+ if reports:
+ most_recent_report_id = reports[0]['id']
+ export_report_to_csv(most_recent_report_id, access_token)
+ print(f"Exported report {most_recent_report_id} to CSV")
+ else:
+ print("No exposure management reports found in the last 24 hours")
\ No newline at end of file
diff --git a/DavidsDevelopment b/DavidsDevelopment
new file mode 160000
index 0000000..db7cc92
--- /dev/null
+++ b/DavidsDevelopment
@@ -0,0 +1 @@
+Subproject commit db7cc92e66436f01da6fcfb7cf619cc2b270ec5a
diff --git a/Docker/Outline/docker-compose.yaml b/Docker/Outline/docker-compose.yaml
new file mode 100644
index 0000000..1fca910
--- /dev/null
+++ b/Docker/Outline/docker-compose.yaml
@@ -0,0 +1,71 @@
+version: "3.2"
+services:
+
+ outline:
+ image: docker.getoutline.com/outlinewiki/outline:latest
+ env_file: ./docker.env
+ ports:
+ - "3000:3000"
+ volumes:
+ - storage-data:/var/lib/outline/data
+ depends_on:
+ - postgres
+ - redis
+
+ redis:
+ image: redis
+ env_file: ./docker.env
+ ports:
+ - "6379:6379"
+ volumes:
+ - ./redis.conf:/redis.conf
+ command: ["redis-server", "/redis.conf"]
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 30s
+ retries: 3
+
+ postgres:
+ image: postgres
+ env_file: ./docker.env
+ ports:
+ - "5432:5432"
+ volumes:
+ - database-data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD", "pg_isready", "-d", "outline", "-U", "user"]
+ interval: 30s
+ timeout: 20s
+ retries: 3
+ environment:
+ POSTGRES_USER: 'user'
+ POSTGRES_PASSWORD: 'pass'
+ POSTGRES_DB: 'outline'
+
+ https-portal:
+ image: steveltn/https-portal
+ env_file: ./docker.env
+ ports:
+ - '80:80'
+ - '443:443'
+ links:
+ - outline
+ restart: always
+ volumes:
+ - https-portal-data:/var/lib/https-portal
+ healthcheck:
+ test: ["CMD", "service", "nginx", "status"]
+ interval: 30s
+ timeout: 20s
+ retries: 3
+ environment:
+ DOMAINS: 'docs.mycompany.com -> http://outline:3000'
+ STAGE: 'production'
+ WEBSOCKET: 'true'
+ CLIENT_MAX_BODY_SIZE: '0'
+
+volumes:
+ https-portal-data:
+ storage-data:
+ database-data:
\ No newline at end of file
diff --git a/Docker/torrents/cyberghost/.~lock.client.key# b/Docker/torrents/cyberghost/.~lock.client.key#
new file mode 100644
index 0000000..7865565
--- /dev/null
+++ b/Docker/torrents/cyberghost/.~lock.client.key#
@@ -0,0 +1 @@
+,david,pop-os,16.06.2023 16:27,file:///home/david/.config/libreoffice/4;
\ No newline at end of file
diff --git a/Docker/torrents/cyberghost/ca.crt b/Docker/torrents/cyberghost/ca.crt
new file mode 100644
index 0000000..1d150e3
--- /dev/null
+++ b/Docker/torrents/cyberghost/ca.crt
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
diff --git a/Docker/torrents/cyberghost/client.crt b/Docker/torrents/cyberghost/client.crt
new file mode 100644
index 0000000..a79b4a5
--- /dev/null
+++ b/Docker/torrents/cyberghost/client.crt
@@ -0,0 +1,38 @@
+-----BEGIN CERTIFICATE-----
+MIIGojCCBIqgAwIBAgIEAmKu/zANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
+TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
+GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
+b0BjeWJlcmdob3N0LnJvMB4XDTIzMDIxNzE1MTE0OFoXDTMzMDIxNDE1MTE0OFow
+czELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
+ZXJHaG9zdCBTLkEuMRMwEQYDVQQDDApYUEVqeGlmWDcyMSEwHwYJKoZIhvcNAQkB
+FhJpbmZvQGN5YmVyZ2hvc3Qucm8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQC7tEeEtyGY/c6Xvev31b0pB5NxpZ33d+nw6hAV6UtZnbOnsaBMDSkXzppL
+VaMWBayoBTmR0cwhkNayn3+BHjQzPxTBIxResxWVqf/Dm/5o4QTT5YqJ1OE/U8GY
+dy4HY8TGdHXjwdt3QkPVGBQUkC2dlsKLMth8eWk7eXOcEC5cLWv37t3Df7WYAzyo
+l1tPbzM1z2cL6GQrp6kuBqr78LuQ1QP9OyBoN14mpHKnzkADcw+gmvVrfpFTX/Yi
+39lgoSZZZU/74GTHpc89o71B9pPkj19XgCIEimsdBuBrXYhfoCnrNh9ChsNPRd/8
+Ba+rqEsTP/kXbxgWh24fO6fL86ZU6VslSHCHIZfoJhX5CLzthnIeMmPNMwJsPGVy
+NN6SPxPPm8tKYAQjzpSbEXmiecO9VYVclU3KQTqjoGUNvUByCUae7wLF3PXIzLVQ
+x4Z2hNpmCmT916TcWGPJDKXMjkNQfBF1o7RnYaIFf5Ne4trwgQo/stHNYAz0y0/U
+A1gyYUs192vYnUTfIYhN5x8mfUWOFaK7RqT2RIidSlaFMhZ8tH3hASqqdo/b2e03
+D8N1PM38Wq4R12Vfq3SlTqQobEWb/1qXusBZhaOFCttck5LauaWP+kIdvI5hKFiP
+VEa7zF1UJuyUjm/ZmN4uYPTrPuvAfkupZybAwuGB1L5pDuRyiQIDAQABo4IBNDCC
+ATAwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwNAYJYIZIAYb4QgENBCcWJUN5YmVy
+R2hvc3QgR2VuZXJhdGVkIFVzZXIgQ2VydGlmaWNhdGUwEQYJYIZIAYb4QgEBBAQD
+AgeAMB0GA1UdDgQWBBQy2dvEDGXhLUYzaV8/VPwiB9+J+jCBrQYDVR0jBIGlMIGi
+gBTq10rWD8d7mrON4Cgzl4e3iKf2JaF/pH0wezELMAkGA1UEBhMCUk8xEjAQBgNV
+BAcTCUJ1Y2hhcmVzdDEYMBYGA1UEChMPQ3liZXJHaG9zdCBTLkEuMRswGQYDVQQD
+ExJDeWJlckdob3N0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEmluZm9AY3liZXJn
+aG9zdC5yb4IJAJxUG61mxDS7MA0GCSqGSIb3DQEBCwUAA4ICAQDVmu2F3OxvBbTA
+yf3Wop9H7KaHtHu8dgEr5q3tL6neH2JuqYYnWMi3eMb/5kviFkJ98GQI/cDls/ae
+NTuvxZ5Bz9lMQjmPvniqk8YgdIbyxkSa4Qfols08go7ZoAIHsNgPvradJoj/1hOg
+1OCl+tlXanPPEDIsNL8S9MnSWUkWfIksq7+jjVXKZzGzrox3OsOSo2kLyoJwPvuH
+SwsjGV8KDByhbBUTRNH6MnXY7PPBLdpsTtBmHCJvhNnC/j9xWTCutxifonZhCI2J
+z4i1GcAuV4nNyBrj5h7uNjgQGKnP1wvxysRtrO3UNJqcJkJqZqOKKi9Sqmjh/NDv
+2o7pwgfvsf+vAXCwVBmv/ea39jKltY7s6ZY3d8JHgfAzU2R8Q0JN+aQRh0L9jcu/
+JC3jB6iYV+lgLfg6ZuBuneTV69yba4G9TRcdEdGpe0SNcj3gWEvDFnmKe3i8c8Px
+RUHknJr6+9ielIZl7TeB3zRNWMBWxGfUmGdUx5/SVIfc8kOOWBBHWi3ice6uxa6C
+/hQh15dy/Dmro3iF2HF3hxNV8C9VQcceZcS7wOF//mqK6721bjk5/fxNhtBh1CB9
+9nxq719le9XCd0zFRPYMTtnVd6ELqHhOdI7Fw90dFeAOIbBDZ5D3AjbjASUaILR0
+X+/aNMPxoNGpbkCY3G/lb0NzmV0FLQ==
+-----END CERTIFICATE-----
diff --git a/Docker/torrents/cyberghost/client.key b/Docker/torrents/cyberghost/client.key
new file mode 100644
index 0000000..8df6504
--- /dev/null
+++ b/Docker/torrents/cyberghost/client.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+T1HH2ae+okFYaHnNKrA9DGZNOgzYGQ==
+-----END PRIVATE KEY-----
diff --git a/Docker/torrents/cyberghost/docker-compose.yml b/Docker/torrents/cyberghost/docker-compose.yml
new file mode 100644
index 0000000..631029a
--- /dev/null
+++ b/Docker/torrents/cyberghost/docker-compose.yml
@@ -0,0 +1,16 @@
+#https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/cyberghost.md
+version: "3"
+services:
+ gluetun:
+ image: qmcgaw/gluetun
+ cap_add:
+ - NET_ADMIN
+ devices:
+ - /dev/net/tun:/dev/net/tun
+ environment:
+ - VPN_SERVICE_PROVIDER=cyberghost
+ - OPENVPN_USER=pvDXWWNiJm
+ - OPENVPN_PASSWORD=WvCrDvzh8r
+ - SERVER_COUNTRIES=Netherlands
+ volumes:
+ - /mnt/storagepool/media/software/gluetun:/gluetun
\ No newline at end of file
diff --git a/Docker/torrents/cyberghost/openvpn.ovpn b/Docker/torrents/cyberghost/openvpn.ovpn
new file mode 100644
index 0000000..1ee23fa
--- /dev/null
+++ b/Docker/torrents/cyberghost/openvpn.ovpn
@@ -0,0 +1,161 @@
+client
+remote 87-1-ca.cg-dialup.net 443
+dev tun
+proto udp
+auth-user-pass
+
+
+resolv-retry infinite
+redirect-gateway def1
+persist-key
+persist-tun
+nobind
+cipher AES-256-CBC
+ncp-disable
+auth SHA256
+ping 5
+ping-exit 60
+ping-timer-rem
+explicit-exit-notify 2
+script-security 2
+remote-cert-tls server
+route-delay 5
+verb 4
+
+pull-filter ignore "ifconfig-ipv6"
+pull-filter ignore "route-ipv6"
+
+
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
+
+
+
+-----BEGIN CERTIFICATE-----
+MIIGojCCBIqgAwIBAgIEAmKu/zANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJS
+TzESMBAGA1UEBxMJQnVjaGFyZXN0MRgwFgYDVQQKEw9DeWJlckdob3N0IFMuQS4x
+GzAZBgNVBAMTEkN5YmVyR2hvc3QgUm9vdCBDQTEhMB8GCSqGSIb3DQEJARYSaW5m
+b0BjeWJlcmdob3N0LnJvMB4XDTIzMDIxNzE1MTE0OFoXDTMzMDIxNDE1MTE0OFow
+czELMAkGA1UEBhMCUk8xEjAQBgNVBAcMCUJ1Y2hhcmVzdDEYMBYGA1UECgwPQ3li
+ZXJHaG9zdCBTLkEuMRMwEQYDVQQDDApYUEVqeGlmWDcyMSEwHwYJKoZIhvcNAQkB
+FhJpbmZvQGN5YmVyZ2hvc3Qucm8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQC7tEeEtyGY/c6Xvev31b0pB5NxpZ33d+nw6hAV6UtZnbOnsaBMDSkXzppL
+VaMWBayoBTmR0cwhkNayn3+BHjQzPxTBIxResxWVqf/Dm/5o4QTT5YqJ1OE/U8GY
+dy4HY8TGdHXjwdt3QkPVGBQUkC2dlsKLMth8eWk7eXOcEC5cLWv37t3Df7WYAzyo
+l1tPbzM1z2cL6GQrp6kuBqr78LuQ1QP9OyBoN14mpHKnzkADcw+gmvVrfpFTX/Yi
+39lgoSZZZU/74GTHpc89o71B9pPkj19XgCIEimsdBuBrXYhfoCnrNh9ChsNPRd/8
+Ba+rqEsTP/kXbxgWh24fO6fL86ZU6VslSHCHIZfoJhX5CLzthnIeMmPNMwJsPGVy
+NN6SPxPPm8tKYAQjzpSbEXmiecO9VYVclU3KQTqjoGUNvUByCUae7wLF3PXIzLVQ
+x4Z2hNpmCmT916TcWGPJDKXMjkNQfBF1o7RnYaIFf5Ne4trwgQo/stHNYAz0y0/U
+A1gyYUs192vYnUTfIYhN5x8mfUWOFaK7RqT2RIidSlaFMhZ8tH3hASqqdo/b2e03
+D8N1PM38Wq4R12Vfq3SlTqQobEWb/1qXusBZhaOFCttck5LauaWP+kIdvI5hKFiP
+VEa7zF1UJuyUjm/ZmN4uYPTrPuvAfkupZybAwuGB1L5pDuRyiQIDAQABo4IBNDCC
+ATAwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwNAYJYIZIAYb4QgENBCcWJUN5YmVy
+R2hvc3QgR2VuZXJhdGVkIFVzZXIgQ2VydGlmaWNhdGUwEQYJYIZIAYb4QgEBBAQD
+AgeAMB0GA1UdDgQWBBQy2dvEDGXhLUYzaV8/VPwiB9+J+jCBrQYDVR0jBIGlMIGi
+gBTq10rWD8d7mrON4Cgzl4e3iKf2JaF/pH0wezELMAkGA1UEBhMCUk8xEjAQBgNV
+BAcTCUJ1Y2hhcmVzdDEYMBYGA1UEChMPQ3liZXJHaG9zdCBTLkEuMRswGQYDVQQD
+ExJDeWJlckdob3N0IFJvb3QgQ0ExITAfBgkqhkiG9w0BCQEWEmluZm9AY3liZXJn
+aG9zdC5yb4IJAJxUG61mxDS7MA0GCSqGSIb3DQEBCwUAA4ICAQDVmu2F3OxvBbTA
+yf3Wop9H7KaHtHu8dgEr5q3tL6neH2JuqYYnWMi3eMb/5kviFkJ98GQI/cDls/ae
+NTuvxZ5Bz9lMQjmPvniqk8YgdIbyxkSa4Qfols08go7ZoAIHsNgPvradJoj/1hOg
+1OCl+tlXanPPEDIsNL8S9MnSWUkWfIksq7+jjVXKZzGzrox3OsOSo2kLyoJwPvuH
+SwsjGV8KDByhbBUTRNH6MnXY7PPBLdpsTtBmHCJvhNnC/j9xWTCutxifonZhCI2J
+z4i1GcAuV4nNyBrj5h7uNjgQGKnP1wvxysRtrO3UNJqcJkJqZqOKKi9Sqmjh/NDv
+2o7pwgfvsf+vAXCwVBmv/ea39jKltY7s6ZY3d8JHgfAzU2R8Q0JN+aQRh0L9jcu/
+JC3jB6iYV+lgLfg6ZuBuneTV69yba4G9TRcdEdGpe0SNcj3gWEvDFnmKe3i8c8Px
+RUHknJr6+9ielIZl7TeB3zRNWMBWxGfUmGdUx5/SVIfc8kOOWBBHWi3ice6uxa6C
+/hQh15dy/Dmro3iF2HF3hxNV8C9VQcceZcS7wOF//mqK6721bjk5/fxNhtBh1CB9
+9nxq719le9XCd0zFRPYMTtnVd6ELqHhOdI7Fw90dFeAOIbBDZ5D3AjbjASUaILR0
+X+/aNMPxoNGpbkCY3G/lb0NzmV0FLQ==
+-----END CERTIFICATE-----
+
+
+
+-----BEGIN PRIVATE KEY-----
+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
+T1HH2ae+okFYaHnNKrA9DGZNOgzYGQ==
+-----END PRIVATE KEY-----
+
diff --git a/Docker/torrents/deluge/docker-compose.yml b/Docker/torrents/deluge/docker-compose.yml
new file mode 100644
index 0000000..6ffab39
--- /dev/null
+++ b/Docker/torrents/deluge/docker-compose.yml
@@ -0,0 +1,12 @@
+deluge:
+ container_name: deluge
+ image: linuxserver/deluge:latest
+ restart: unless-stopped
+ network_mode: service:gluetun # run on the vpn network
+ environment:
+ - PUID=1000 # default user id, defined in .env
+ - PGID=1000 # default group id, defined in .env
+ - America/New_York # timezone, defined in .env
+ volumes:
+ - /mnt/storagepool/media/downloads:/downloads # downloads folder
+ - /mnt/storagepool/media/software/deluge:/config # config files
\ No newline at end of file
diff --git a/Docker/torrents/glueton both 2 deluge.yml b/Docker/torrents/glueton both 2 deluge.yml
new file mode 100644
index 0000000..e69de29
diff --git a/Docker/torrents/gluetun both b/Docker/torrents/gluetun both
new file mode 100644
index 0000000..91fc3e3
--- /dev/null
+++ b/Docker/torrents/gluetun both
@@ -0,0 +1,42 @@
+version: "3"
+services:
+ gluetun:
+ container_name: gluetun
+ image: qmcgaw/gluetun
+ cap_add:
+ - NET_ADMIN
+ devices:
+ - /dev/net/tun:/dev/net/tun
+ environment:
+ - VPN_SERVICE_PROVIDER=cyberghost
+ - OPENVPN_USER=pvDXWWNiJm # Replace with your actual username
+ - OPENVPN_PASSWORD=WvCrDvzh8r # Replace with your actual password
+ - SERVER_COUNTRIES=Netherlands
+ - PUID=1000
+ - PGID=999
+ volumes:
+ - /mnt/storagepool/media/software/gluetun:/gluetun
+ ports:
+ - 8112:8112
+ restart: always
+ healthcheck:
+ test: ["CMD", "curl", "-s", "--fail", "https://www.google.com"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+
+ deluge:
+ container_name: deluge
+ image: linuxserver/deluge:latest
+ restart: unless-stopped
+ network_mode: service:gluetun
+ environment:
+ - PUID=1000
+ - PGID=999
+ - TZ=America/New_York
+ volumes:
+ - /mnt/storagepool/media/downloads:/downloads
+ - /mnt/storagepool/media/software/deluge:/config
+ depends_on:
+ gluetun:
+ condition: service_healthy
\ No newline at end of file
diff --git a/Docker/whispersetup/docker-compose.yml b/Docker/whispersetup/docker-compose.yml
new file mode 100644
index 0000000..4ddcf85
--- /dev/null
+++ b/Docker/whispersetup/docker-compose.yml
@@ -0,0 +1,10 @@
+version: "2.1"
+services:
+ whisperasr:
+ image: onerahmet/openai-whisper-asr-webservice:latest
+ environment:
+ - ASR_MODEL=small
+ - ASR_ENGINE=faster_whisper
+ ports:
+ - 9000:9000
+ restart: unless-stopped
\ No newline at end of file
diff --git a/MarkdownConversion/ConverttoDOCX.py b/MarkdownConversion/ConverttoDOCX.py
new file mode 100644
index 0000000..8058be7
--- /dev/null
+++ b/MarkdownConversion/ConverttoDOCX.py
@@ -0,0 +1,48 @@
+import os
+from markdown import markdown
+from docx import Document
+
+def convert_md_to_docx(md_file_path, docx_file_path):
+ """Converts a Markdown file to a Word document."""
+ with open(md_file_path, 'r', encoding='utf-8') as md_file:
+ md_content = md_file.read()
+
+ # Convert Markdown to HTML
+ html_content = markdown(md_content)
+
+ # Create a Word document and add the HTML content
+ doc = Document()
+ doc.add_paragraph(html_content)
+ doc.save(docx_file_path)
+ print(f"Converted: {md_file_path} -> {docx_file_path}")
+
+def convert_all_md_to_docx(root_directory, output_directory):
+ """Traverse the directory tree and convert all .md files to .docx, storing them in a mirrored structure."""
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ if filename.endswith('.md'):
+ md_file_path = os.path.join(dirpath, filename)
+
+ # Create mirrored directory structure in the output directory
+ relative_path = os.path.relpath(dirpath, root_directory)
+ target_dir = os.path.join(output_directory, relative_path)
+ os.makedirs(target_dir, exist_ok=True)
+
+ # Define the output .docx file path
+ docx_file_path = os.path.join(target_dir, os.path.splitext(filename)[0] + '.docx')
+
+ try:
+ convert_md_to_docx(md_file_path, docx_file_path)
+ except Exception as e:
+ print(f"Failed to convert {md_file_path}: {e}")
+
+if __name__ == "__main__":
+ # Prompt user for the root directory and output directory
+ root_dir = input("Enter the root directory to scan for Markdown files: ").strip()
+ output_dir = input("Enter the output directory to store converted files: ").strip()
+
+ if os.path.exists(root_dir):
+ os.makedirs(output_dir, exist_ok=True) # Ensure output directory exists
+ convert_all_md_to_docx(root_dir, output_dir)
+ else:
+ print("The specified root directory does not exist.")
\ No newline at end of file
diff --git a/MarkdownConversion/ConverttoDOCX.txt b/MarkdownConversion/ConverttoDOCX.txt
new file mode 100644
index 0000000..8058be7
--- /dev/null
+++ b/MarkdownConversion/ConverttoDOCX.txt
@@ -0,0 +1,48 @@
+import os
+from markdown import markdown
+from docx import Document
+
+def convert_md_to_docx(md_file_path, docx_file_path):
+ """Converts a Markdown file to a Word document."""
+ with open(md_file_path, 'r', encoding='utf-8') as md_file:
+ md_content = md_file.read()
+
+ # Convert Markdown to HTML
+ html_content = markdown(md_content)
+
+ # Create a Word document and add the HTML content
+ doc = Document()
+ doc.add_paragraph(html_content)
+ doc.save(docx_file_path)
+ print(f"Converted: {md_file_path} -> {docx_file_path}")
+
+def convert_all_md_to_docx(root_directory, output_directory):
+ """Traverse the directory tree and convert all .md files to .docx, storing them in a mirrored structure."""
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ if filename.endswith('.md'):
+ md_file_path = os.path.join(dirpath, filename)
+
+ # Create mirrored directory structure in the output directory
+ relative_path = os.path.relpath(dirpath, root_directory)
+ target_dir = os.path.join(output_directory, relative_path)
+ os.makedirs(target_dir, exist_ok=True)
+
+ # Define the output .docx file path
+ docx_file_path = os.path.join(target_dir, os.path.splitext(filename)[0] + '.docx')
+
+ try:
+ convert_md_to_docx(md_file_path, docx_file_path)
+ except Exception as e:
+ print(f"Failed to convert {md_file_path}: {e}")
+
+if __name__ == "__main__":
+ # Prompt user for the root directory and output directory
+ root_dir = input("Enter the root directory to scan for Markdown files: ").strip()
+ output_dir = input("Enter the output directory to store converted files: ").strip()
+
+ if os.path.exists(root_dir):
+ os.makedirs(output_dir, exist_ok=True) # Ensure output directory exists
+ convert_all_md_to_docx(root_dir, output_dir)
+ else:
+ print("The specified root directory does not exist.")
\ No newline at end of file
diff --git a/NIST Reports/nvd.py b/NIST Reports/nvd.py
new file mode 100644
index 0000000..54109db
--- /dev/null
+++ b/NIST Reports/nvd.py
@@ -0,0 +1,92 @@
+import os
+import requests
+import pandas as pd
+from datetime import datetime, timedelta
+
+def get_latest_cves(days=3):
+ try:
+ end_date = datetime.now()
+ start_date = end_date - timedelta(days=days)
+ start_date_str = start_date.strftime("%Y-%m-%dT%H:%M:%S")
+ end_date_str = end_date.strftime("%Y-%m-%dT%H:%M:%S")
+ nvd_url = f"https://services.nvd.nist.gov/rest/json/cves/1.0?pubStartDate={start_date_str}&pubEndDate={end_date_str}"
+ response = requests.get(nvd_url)
+ if response.status_code == 200:
+ return response.json()
+ else:
+ print(f"Error: Unable to fetch CVE data. Status code: {response.status_code}")
+ print("Response content:", response.content)
+ return None
+ except requests.exceptions.RequestException as e:
+ print(f"Error: Unable to fetch CVE data. {e}")
+ return None
+
+def extract_cvss_data(cve_entry):
+ cvss_data = cve_entry.get("impact", {}).get("baseMetricV2", {}).get("cvssV2", {})
+ if cvss_data:
+ return {
+ "CVSS Version": "2.0",
+ "Vector String": cvss_data.get("vectorString", ""),
+ "Base Score": cvss_data.get("baseScore", ""),
+ "Base Severity": cvss_data.get("severity", ""),
+ "Access Vector": cvss_data.get("accessVector", ""),
+ "Access Complexity": cvss_data.get("accessComplexity", ""),
+ "Authentication": cvss_data.get("authentication", ""),
+ "Confidentiality Impact": cvss_data.get("confidentialityImpact", ""),
+ "Integrity Impact": cvss_data.get("integrityImpact", ""),
+ "Availability Impact": cvss_data.get("availabilityImpact", ""),
+ "Exploitability Score": cve_entry.get("exploitabilityScore", ""),
+ "Impact Score": cve_entry.get("impactScore", "")
+ }
+ else:
+ return {
+ "CVSS Version": "",
+ "Vector String": "",
+ "Base Score": "",
+ "Base Severity": "",
+ "Access Vector": "",
+ "Access Complexity": "",
+ "Authentication": "",
+ "Confidentiality Impact": "",
+ "Integrity Impact": "",
+ "Availability Impact": "",
+ "Exploitability Score": "",
+ "Impact Score": ""
+ }
+
+def generate_excel_report(cves, excel_filename):
+ try:
+ data = []
+ for vulnerability_entry in cves.get("result", {}).get("CVE_Items", []):
+ cve_entry = vulnerability_entry.get("cve", {})
+ cve_id = cve_entry.get("CVE_data_meta", {}).get("ID", "")
+ descriptions = cve_entry.get("description", {}).get("description_data", [])
+ english_descriptions = [desc.get("value", "") for desc in descriptions if desc.get("lang") == "en"]
+ description = " ".join(english_descriptions)
+ references = cve_entry.get("references", {}).get("reference_data", [])
+ reference_urls = [ref.get("url", "") for ref in references]
+ reference_sources = [ref.get("name", "") for ref in references]
+ reference_url = reference_urls[0] if reference_urls else ""
+ reference_source = reference_sources[0] if reference_sources else ""
+ cvss_data = extract_cvss_data(vulnerability_entry)
+ data.append({
+ "CVE ID": cve_id,
+ "Description": description,
+ "Reference URL": reference_url,
+ "Reference Source": reference_source,
+ **cvss_data
+ })
+ df = pd.DataFrame(data)
+ df.to_excel(excel_filename, index=False)
+ print(f"Excel report generated successfully: {excel_filename}")
+ except Exception as e:
+ print(f"Error: Unable to generate Excel report. {e}")
+
+if __name__ == "__main__":
+ latest_cves = get_latest_cves()
+ if latest_cves:
+ excel_timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
+ excel_filename = f"latest_cves_{excel_timestamp}.xlsx"
+ generate_excel_report(latest_cves, excel_filename)
+ else:
+ print("No CVEs found.")
diff --git a/NIST Reports/nvd_2025.py b/NIST Reports/nvd_2025.py
new file mode 100644
index 0000000..301f656
--- /dev/null
+++ b/NIST Reports/nvd_2025.py
@@ -0,0 +1,121 @@
+import requests
+import json
+import csv
+import datetime
+import os
+import time
+
+def get_nist_vulnerabilities(days=3, limit=5000): # Added limit parameter
+ """Retrieves the latest vulnerabilities from the NIST NVD API v2.0 with a limit."""
+
+ end_date = datetime.date.today()
+ start_date = end_date - datetime.timedelta(days=days)
+
+ all_vulnerabilities = []
+ startIndex = 0
+ resultsPerPage = 2000
+
+ while True:
+ url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?startIndex={startIndex}&resultsPerPage={resultsPerPage}"
+
+ try:
+ response = requests.get(url)
+ response.raise_for_status()
+ data = response.json()
+ vulnerabilities = data.get("vulnerabilities", [])
+
+ if not vulnerabilities:
+ break
+
+ all_vulnerabilities.extend(vulnerabilities)
+ startIndex += resultsPerPage
+ time.sleep(0.5) # Reduced sleep time slightly
+
+ if len(all_vulnerabilities) >= limit: #Enforce limit
+ print(f"Reached CVE limit of {limit}. Stopping retrieval.")
+ break
+
+ except requests.exceptions.RequestException as e:
+ print(f"Error fetching data from NIST API: {e}")
+ return []
+ except json.JSONDecodeError as e:
+ print(f"Error decoding JSON response: {e}")
+ return []
+
+ filtered_vulnerabilities = [
+ cve for cve in all_vulnerabilities
+ if datetime.datetime.fromisoformat(cve.get('cve', {}).get('published', '').replace('Z', '+00:00')).date() >= start_date
+ ]
+ return filtered_vulnerabilities
+
+
+
+def write_to_csv(vulnerabilities, filename):
+ # ... (This function is exactly the same as in my previous responses)
+ if not vulnerabilities:
+ print("No vulnerabilities found for the specified period.")
+ return
+
+ fieldnames = ["Product", "Vendor", "CVE ID", "Date Added", "Date Updated", "Description", "Severity"]
+
+ try:
+ with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+ writer.writeheader()
+ for cve in vulnerabilities:
+ cve_data = cve.get('cve', {})
+
+ products = []
+ vendors = []
+
+ if 'cpeMatch' in cve_data.get('configurations', {}):
+ for match in cve_data['configurations']['cpeMatch']:
+ cpe_string = match.get('cpe23Uri', '')
+ parts = cpe_string.split(':')
+ if len(parts) >= 5:
+ vendors.append(parts[3])
+ products.append(parts[4])
+ else:
+ vendors.append("N/A")
+ products.append("N/A")
+
+ product_str = ", ".join(products)
+ vendor_str = ", ".join(vendors)
+
+ description = cve_data.get('descriptions', [{}])[0].get('value', 'N/A')
+ published_date = cve_data.get('published', 'N/A')
+ last_modified_date = cve_data.get('lastModified', 'N/A')
+ cve_id = cve_data.get('id', 'N/A')
+
+ severity = 'N/A'
+ metrics = cve_data.get('metrics', {})
+ if 'cvssMetricV31' in metrics:
+ severity = metrics['cvssMetricV31'][0].get('cvssData', {}).get('baseSeverity', 'N/A')
+ elif 'cvssMetricV30' in metrics:
+ severity = metrics['cvssMetricV30'][0].get('cvssData', {}).get('baseSeverity', 'N/A')
+ elif 'cvssMetricV2' in metrics:
+ severity = metrics['cvssMetricV2'][0].get('cvssData', {}).get('baseSeverity', 'N/A')
+
+ writer.writerow({
+ "Product": product_str,
+ "Vendor": vendor_str,
+ "CVE ID": cve_id,
+ "Date Added": published_date,
+ "Date Updated": last_modified_date,
+ "Description": description,
+ "Severity": severity
+ })
+ print(f"Vulnerabilities written to {filename}")
+
+ except Exception as e:
+ print(f"Error writing to CSV: {e}")
+
+
+
+if __name__ == "__main__":
+ vulnerabilities = get_nist_vulnerabilities()
+
+ if vulnerabilities:
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ filename = f"nist_vulnerabilities_{timestamp}.csv"
+ write_to_csv(vulnerabilities, filename)
\ No newline at end of file
diff --git a/NIST Reports/readme.md b/NIST Reports/readme.md
new file mode 100644
index 0000000..e69de29
diff --git a/Python Course Materials/Week2/pg29Excercise1.py b/Python Course Materials/Week2/pg29Excercise1.py
new file mode 100644
index 0000000..bc064f4
--- /dev/null
+++ b/Python Course Materials/Week2/pg29Excercise1.py
@@ -0,0 +1,24 @@
+#a
+print("Hello, world!")
+#b
+print("Hello", "World!")
+#c
+print("3")
+#d
+print("3.0")
+#e
+print(2 + 3)
+#f
+print("2.0 + 3.0")
+#g
+print("2" + "3")
+#h
+print("2 + 3 =", 2 + 3)
+#i
+print("2 * 3")
+#j
+print(2 ** 3)
+#k
+print(7 / 3)
+#l
+print(7 // 3)
diff --git a/Python Course Materials/Week2/pg29Exercise1.py b/Python Course Materials/Week2/pg29Exercise1.py
new file mode 100644
index 0000000..bc064f4
--- /dev/null
+++ b/Python Course Materials/Week2/pg29Exercise1.py
@@ -0,0 +1,24 @@
+#a
+print("Hello, world!")
+#b
+print("Hello", "World!")
+#c
+print("3")
+#d
+print("3.0")
+#e
+print(2 + 3)
+#f
+print("2.0 + 3.0")
+#g
+print("2" + "3")
+#h
+print("2 + 3 =", 2 + 3)
+#i
+print("2 * 3")
+#j
+print(2 ** 3)
+#k
+print(7 / 3)
+#l
+print(7 // 3)
diff --git a/Python Course Materials/Week2/pg58exercise10.py b/Python Course Materials/Week2/pg58exercise10.py
new file mode 100644
index 0000000..e9e157c
--- /dev/null
+++ b/Python Course Materials/Week2/pg58exercise10.py
@@ -0,0 +1,27 @@
+#performs the conversion and prints to the console
+def ConvertToMiles():
+ kilometers = float(input("Please provide a number of kilometers that you would like to convert to miles: "))
+ converted_to_miles = kilometers * 0.62
+ print(kilometers, " kilometer(s) is/are equal to " , converted_to_miles , " miles.")
+ askaboutrepeating()
+
+#Ask the users if they would like to do it again.
+def askaboutrepeating():
+ print("Looks like that provided you a converted number. Do you want to convert another number? ")
+ answer = str(input("Please respond with yes (y) or no (n) :"))
+ if answer == "y":
+ ConvertToMiles()
+ if answer == "n":
+ exit
+ else:
+ print("Woah, something about what you typed was definitley not correct.")
+ askaboutrepeating()
+
+# Defines what the script is going to do and calls upon the earlier defined Convert function
+def main():
+ print("Welcome!\n\n This application was made by David Kifer to take a quantity of kilometers and convert them to miles. In the following prompt, you will be asked to provide the amount of kilometers\n\n ")
+ ConvertToMiles()
+
+# Call the main app to kick off the app
+main()
+
diff --git a/Python Course Materials/Week2/pg84exercise5.py b/Python Course Materials/Week2/pg84exercise5.py
new file mode 100644
index 0000000..0caf451
--- /dev/null
+++ b/Python Course Materials/Week2/pg84exercise5.py
@@ -0,0 +1,24 @@
+#Konditorei Coffee shop
+def main():
+ pounds = float(input("How many pounds of coffee would you like to order?: "))
+ coffee_price = float(15.50)
+ shipping_price = float(0.99 * pounds + 4.50)
+ grand_total = coffee_price + shipping_price
+ print("That comes to a grand total of $" , grand_total)
+ askaboutrepeating()
+
+# Ask about repeating
+def askaboutrepeating():
+ print("Thanks for ordering with me! Would you like to order some more?")
+ answer = str(input("Please respond with yes (y) or no (n) :"))
+ if answer == "y":
+ main()
+ if answer == "n":
+ exit
+ else:
+ print("Woah, something about what you typed was definitley not correct.")
+ askaboutrepeating()
+
+# Call the main app to kick off the app
+main()
+
diff --git a/Python Course Materials/Week3/coursera lab.py b/Python Course Materials/Week3/coursera lab.py
new file mode 100644
index 0000000..b97b544
--- /dev/null
+++ b/Python Course Materials/Week3/coursera lab.py
@@ -0,0 +1,119 @@
+menu = {
+ 1: {"name": 'espresso', "price": 1.99},
+ 2: {"name": 'coffee', "price": 2.50},
+ 3: {"name": 'cake', "price": 2.79},
+ 4: {"name": 'soup', "price": 4.50},
+ 5: {"name": 'sandwich', "price": 4.99}
+}
+
+def calculate_subtotal(order):
+ """Calculates the subtotal of an order.
+
+ [IMPLEMENT ME]
+ 1. Add up the prices of all items in the order.
+ 2. Round the result to 2 decimal places and return it.
+
+ Args:
+ order (list): A list of dicts, where each dict represents an item with a name and price.
+
+ Returns:
+ float: The subtotal of all item prices, rounded to 2 decimal places.
+ """
+ print('Calculating bill subtotal...')
+
+ ### WRITE SOLUTION HERE
+ subtotal_calc = float(0.00)
+ for item in order:
+ subtotal_calc += item['price']
+ return round(subtotal_calc, 2)
+
+def calculate_tax(subtotal):
+ """Calculates the tax of an order.
+
+ [IMPLEMENT ME]
+ 1. Calculate 15% of the subtotal.
+ 2. Round the tax to 2 decimal places and return it.
+
+ Args:
+ subtotal (float): The subtotal amount.
+
+ Returns:
+ float: The calculated tax amount, rounded to 2 decimal places.
+ """
+ print('Calculating tax from subtotal...')
+ ### WRITE SOLUTION HERE
+ tax_rate = 0.15
+ tax = subtotal * tax_rate
+ return round(tax, 2)
+
+
+def summarize_order(order):
+ """Summarizes the order.
+
+ [IMPLEMENT ME]
+ 1. Calculate subtotal and tax.
+ 2. Compute the total (subtotal + tax) and round to 2 decimal places.
+ 3. Extract the item names and return them with the total.
+
+ Args:
+ order (list): A list of dicts, where each dict represents an item with a name and price.
+
+ Returns:
+ tuple: A list of item names and the total amount (rounded to 2 decimal places).
+ """
+
+ ### WRITE SOLUTION HERE
+ item_details = list()
+ for item in order:
+ item_details.append((item['name'], item['price']))
+ subtotal = calculate_subtotal(order)
+ subtotal = round(subtotal, 2)
+ tax = calculate_tax(subtotal)
+ total = round(subtotal + tax, 2)
+ names = [item['name'] for item in order]
+ return names, total
+
+
+# This function is provided for you and will print out the items in an order
+def print_order(order):
+ print('You have ordered ' + str(len(order)) + ' items')
+ items = [item["name"] for item in order]
+ print(items)
+ return order
+
+# This function is provided for you and will display the menu
+def display_menu():
+ print("------- Menu -------")
+ for selection in menu:
+ print(f"{selection}. {menu[selection]['name'] : <9} | {menu[selection]['price'] : >5}")
+ print()
+
+# This function is provided for you and will create an order by prompting the user to select menu items
+def take_order():
+ display_menu()
+ order = []
+ count = 1
+ for i in range(3):
+ item = input('Select menu item number ' + str(count) + ' (from 1 to 5): ')
+ count += 1
+ order.append(menu[int(item)])
+ return order
+
+'''
+Here are some sample function calls to help you test your implementations.
+Feel free to change, uncomment, and add these as you wish.
+'''
+def main():
+ order = take_order()
+ print_order(order)
+ names, total = summarize_order(order)
+
+
+ print("Order summary:")
+ for name, price in [(item['name'], item['price']) for item in order]:
+ print(f" {name}: ${price:.2f}")
+
+ print(f"Total: ${total:.2f}")
+
+if __name__ == "__main__":
+ main()
diff --git a/Python Course Materials/Week3/pg263Excercise1.py b/Python Course Materials/Week3/pg263Excercise1.py
new file mode 100644
index 0000000..540ef08
--- /dev/null
+++ b/Python Course Materials/Week3/pg263Excercise1.py
@@ -0,0 +1,36 @@
+# Given the initial statements
+# s1 = "spam"
+# s2 = "ni!"
+
+# Show the result of evaluating each of the following string expressions.
+
+# a) "The knights who say, " + s2
+# b) 3 * s1 +2 * s2
+# c) a1[1]
+# d) s1[1:3]
+# e) s1[2] + s2[:2]
+# f) s1 + s2[-1]
+# g) s1.upper()
+# h) s1.upper().1just(4) * 3
+
+## Initialization
+s1 = "spam"
+s2 = "ni!"
+
+StringValues = {
+ "a": "The knights who say, " + s2,
+ "b": 3 * s1 + 2 * s2,
+ "c": s1[1],
+ "d": s1[1:3],
+ "e": s1[2] + s2[:2],
+ "f": s1 + s2[-1],
+ "g": s1.upper(),
+ "h": s1.upper().ljust(4) * 3,
+}
+
+def main():
+ for key, value in StringValues.items():
+ print(f"{key}) {value}")
+
+
+main()
\ No newline at end of file
diff --git a/Python Course Materials/Week3/pg264Excercise4.py b/Python Course Materials/Week3/pg264Excercise4.py
new file mode 100644
index 0000000..2f166fc
--- /dev/null
+++ b/Python Course Materials/Week3/pg264Excercise4.py
@@ -0,0 +1,13 @@
+s1 = "spam"
+s2 = "eggs"
+num1 = 10
+num2 = 3.14159
+
+def main():
+ print(f"That is {num1} {s1} and {num1//2} {s2}")
+ print(f"That is {num1:5} {s1} and {num2:5.2} {s2}")
+ print(f"{num1:0.2f} {num2:0.2f}")
+ print(f"{num1:7.5f}{num2:7.5f}")
+ print(f"Time left {num1:02}:{num2:05.0f}")
+
+main()
\ No newline at end of file
diff --git a/Python Course Materials/Week3/pg265Excercise2.py b/Python Course Materials/Week3/pg265Excercise2.py
new file mode 100644
index 0000000..11d0cc2
--- /dev/null
+++ b/Python Course Materials/Week3/pg265Excercise2.py
@@ -0,0 +1,47 @@
+gradetable = {
+ "90-100": "A",
+ "80-89": "B",
+ "70-79": "C",
+ "60-69": "D",
+ "0-59": "F"
+}
+
+def main():
+ usercontinue = 'yes'
+ while usercontinue == 'yes':
+ try:
+ score = int(input("What was the exam score?: "))
+
+ if 90 <= score <= 100:
+ grade = gradetable["90-100"]
+ elif 80 <= score <= 89:
+ grade = gradetable["80-89"]
+ elif 70 <= score <= 79:
+ grade = gradetable["70-79"]
+ elif 60 <= score <= 69:
+ grade = gradetable["60-69"]
+ elif 0 <= score <= 59:
+ grade = gradetable["0-59"]
+ else:
+ grade = 'invalid score'
+
+ print(f"The letter score of score of the exam was {grade}")
+
+ except ValueError:
+ print("Invalid input. Please enter a number.")
+
+
+ usercontinueprompt = input("Would you like to determine the letter grade of another score? (yes/no): ")
+ usercontinueprompt = usercontinueprompt.lower() #Convert to lowercase once
+
+ if usercontinueprompt == 'yes': #Corrected to assignment
+ usercontinue = 'yes'
+ elif usercontinueprompt == 'no':
+ print("Thanks, have a great day!")
+ usercontinue = 'no' # Corrected typo and assignment
+ else:
+ print("That wasn't a valid selection. Try again")
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/Python Course Materials/Week3/pythontest.py b/Python Course Materials/Week3/pythontest.py
new file mode 100644
index 0000000..9121df6
--- /dev/null
+++ b/Python Course Materials/Week3/pythontest.py
@@ -0,0 +1,4 @@
+a = "cool"
+input=("")
+
+print(a)
\ No newline at end of file
diff --git a/Python Course Materials/Week4/pg291-292DiscussionExcercise1and2.py b/Python Course Materials/Week4/pg291-292DiscussionExcercise1and2.py
new file mode 100644
index 0000000..32666ea
--- /dev/null
+++ b/Python Course Materials/Week4/pg291-292DiscussionExcercise1and2.py
@@ -0,0 +1,64 @@
+s1 = [2, 1, 4, 3]
+s2 = ["c","a","b"]
+print("------------Discussion Part 1----------")
+# s1 + s2
+print("-----s1 + s2-----")
+print(s1 + s2)
+
+
+#b) 3 * s1 + 2 * s2
+print("--------3 * s1 + 2 * s2-----")
+print(3 * s1 + 2 *s2)
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#c) s1[1]
+print("--------s1[1]-----")
+print(s1[1])
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#d) s1[1:3]
+print("--------s1[1:3]-----")
+print(s1[1:3])
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#e) s1 + s2[-1]
+print("--------s1[1:3]------")
+print(s1 + [s2[-1]])
+print()
+
+print("------------Discussion Part 2----------")
+#a)s1.remove(2)
+print(s1.remove(2))
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#b)s1.sort()
+print(s1.sort())
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#c)s1.append([s2.index("b")]
+print(s1.append([s2.index("b")]))
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#d)s2.pop(s1.pop(0))
+print(s2.pop(s1.pop(0)))
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
+
+#e)s2.insert(s1[1], "d"
+print(s2.insert(s1[1], "d"))
+print(f"This is the value of s1: {s1}")
+print(f"This is the value of s2: {s2}")
+print()
diff --git a/Python Course Materials/Week4/pg294excercise4.py b/Python Course Materials/Week4/pg294excercise4.py
new file mode 100644
index 0000000..200b68d
--- /dev/null
+++ b/Python Course Materials/Week4/pg294excercise4.py
@@ -0,0 +1,27 @@
+def removeDuplicates(somelist):
+ seen = set()
+ result = []
+
+ for i in somelist:
+ if i not in seen:
+ seen.add(i)
+ result.append(i)
+
+ return result
+
+def main():
+ #Create a list with duplicates
+ somelist = [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
+
+ #Display the list
+ print(somelist)
+
+ #Use the remove duplicates function to remove duplicate values
+ list_parsed = removeDuplicates(somelist)
+
+ print(list_parsed)
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/Python Course Materials/Week4/windowscolorexcercise.py b/Python Course Materials/Week4/windowscolorexcercise.py
new file mode 100644
index 0000000..ac0d173
--- /dev/null
+++ b/Python Course Materials/Week4/windowscolorexcercise.py
@@ -0,0 +1,29 @@
+from graphics import *
+
+win = GraphWin("My Window", 500, 500)
+
+while True:
+ key = win.checkKey()
+ if key == "q": #loop exit
+ break
+ if key == "r":
+ win.setBackground("red")
+ elif key == "g":
+ win.setBackground("green")
+ elif key == "b":
+ win.setBackground("blue")
+ elif key == "y":
+ win.setBackground("yellow")
+ elif key == "w":
+ win.setBackground("white")
+ elif key == "p":
+ win.setBackground("purple")
+ elif key == "o":
+ win.setBackground("orange")
+ elif key == "c":
+ win.setBackground("cyan")
+ else :
+ win.close()
+
+
+
diff --git a/Security Stack/CompanyInvestigation/CompanyReview.py b/Security Stack/CompanyInvestigation/CompanyReview.py
new file mode 100644
index 0000000..fb93f75
--- /dev/null
+++ b/Security Stack/CompanyInvestigation/CompanyReview.py
@@ -0,0 +1,220 @@
+import os
+import requests
+import whois
+from docx import Document
+from fpdf import FPDF
+import datetime
+
+def investigate_website(url):
+ """Investigate the website to find the country of operation and past security breaches."""
+ try:
+ # Perform WHOIS lookup to determine country
+ domain = url.replace("https://", "").replace("http://", "").split("/")[0]
+ whois_data = whois.whois(domain)
+ country = whois_data.get("country", "Unknown")
+
+ # Check for breaches using Have I Been Pwned API (requires API key)
+ hibp_api_url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{domain}"
+ headers = {"hibp-api-key": "YOUR_API_KEY_HERE"} # Replace with your HIBP API key
+ breaches = []
+
+ try:
+ breach_response = requests.get(hibp_api_url, headers=headers)
+ if breach_response.status_code == 200:
+ breaches = breach_response.json()
+ elif breach_response.status_code == 404:
+ breaches = "No breaches found."
+ else:
+ breaches = f"Error: {breach_response.status_code}"
+ except Exception as e:
+ breaches = f"Error fetching breaches: {e}"
+
+ return {
+ "country": country,
+ "breaches": breaches,
+ "whois_data": str(whois_data)
+ }
+ except Exception as e:
+ return {
+ "country": "Error determining country",
+ "breaches": f"Error fetching breaches: {e}",
+ "whois_data": f"Error: {e}"
+ }
+
+def analyze_headers(url):
+ """Perform a header analysis to identify security weaknesses."""
+ try:
+ response = requests.get(url)
+ headers = response.headers
+
+ # Security headers to check
+ security_headers = {
+ "Content-Security-Policy": "Missing or insufficient Content-Security-Policy",
+ "Strict-Transport-Security": "Missing Strict-Transport-Security",
+ "X-Content-Type-Options": "Missing X-Content-Type-Options",
+ "X-Frame-Options": "Missing X-Frame-Options",
+ "X-XSS-Protection": "Missing or insufficient X-XSS-Protection",
+ "Referrer-Policy": "Missing Referrer-Policy",
+ "Permissions-Policy": "Missing Permissions-Policy"
+ }
+
+ weaknesses = []
+ for header, issue in security_headers.items():
+ if header not in headers:
+ weaknesses.append(issue)
+
+ grade = "A" if not weaknesses else "C" if len(weaknesses) < 3 else "F"
+
+ return {
+ "grade": grade,
+ "weaknesses": weaknesses,
+ "headers": dict(headers)
+ }
+ except Exception as e:
+ return {
+ "grade": "Error",
+ "weaknesses": [f"Error analyzing headers: {e}"],
+ "headers": f"Error: {e}"
+ }
+
+def save_results_to_files(data, output_dir, domain):
+ """Save investigation results to docx, pdf, and markdown files."""
+ os.makedirs(output_dir, exist_ok=True)
+
+ # Generate unique filenames using timestamp and domain
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ # Ensure the domain is a valid filename
+ domain_safe = "".join(c if c.isalnum() else "_" for c in domain)
+ base_filename = f"{domain_safe}_{timestamp}"
+ docx_path = os.path.join(output_dir, f"{base_filename}.docx")
+ pdf_path = os.path.join(output_dir, f"{base_filename}.pdf")
+ md_path = os.path.join(output_dir, f"{base_filename}.md")
+
+ # Create a Word document
+ doc = Document()
+ doc.add_heading("Website Security Analysis", level=1)
+
+ doc.add_heading("1. Website Investigation", level=2)
+ doc.add_paragraph(f"Country of Operation: {data['investigation'].get('country', 'N/A')}")
+ doc.add_paragraph(f"Security Breaches: {data['investigation'].get('breaches', 'N/A')}")
+
+ doc.add_heading("2. Header Analysis", level=2)
+ doc.add_paragraph(f"Security Grade: {data['headers'].get('grade', 'N/A')}")
+ if data['headers'].get('weaknesses'):
+ doc.add_paragraph("Weaknesses:")
+ for weakness in data['headers']['weaknesses']:
+ doc.add_paragraph(f"- {weakness}", style="List Bullet")
+
+ # Add Appendix
+ doc.add_heading("Appendix", level=1)
+ doc.add_heading("Full WHOIS Data", level=2)
+ doc.add_paragraph(data['investigation'].get('whois_data', 'N/A'))
+
+ doc.add_heading("Full Header Analysis", level=2)
+ doc.add_paragraph(str(data['headers'].get('headers', 'N/A')))
+
+ doc.add_heading("Full Breach Data", level=2)
+ doc.add_paragraph(str(data['investigation'].get('breaches', 'N/A')))
+
+ doc.save(docx_path)
+
+ # Create a Markdown file
+ with open(md_path, "w") as md_file:
+ md_file.write("# Website Security Analysis\n\n")
+ md_file.write("## 1. Website Investigation\n")
+ md_file.write(f"- Country of Operation: {data['investigation'].get('country', 'N/A')}\n")
+ md_file.write(f"- Security Breaches: {data['investigation'].get('breaches', 'N/A')}\n\n")
+ md_file.write("## 2. Header Analysis\n")
+ md_file.write(f"- Security Grade: {data['headers'].get('grade', 'N/A')}\n")
+ if data['headers'].get('weaknesses'):
+ md_file.write("- Weaknesses:\n")
+ for weakness in data['headers']['weaknesses']:
+ md_file.write(f" - {weakness}\n")
+
+ md_file.write("\n# Appendix\n\n")
+ md_file.write("## Full WHOIS Data\n")
+ md_file.write(f"```\n{data['investigation'].get('whois_data', 'N/A')}\n```\n")
+
+ md_file.write("## Full Header Analysis\n")
+ md_file.write(f"```\n{data['headers'].get('headers', 'N/A')}\n```\n")
+
+ md_file.write("## Full Breach Data\n")
+ md_file.write(f"```\n{data['investigation'].get('breaches', 'N/A')}\n```\n")
+
+ # Create a PDF file using FPDF
+ pdf = FPDF()
+ pdf.set_auto_page_break(auto=True, margin=15)
+ pdf.add_page()
+ pdf.set_font("Arial", size=12)
+
+ pdf.cell(200, 10, txt="Website Security Analysis", ln=True, align='C')
+
+ pdf.ln(10)
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "1. Website Investigation", ln=True)
+
+ pdf.set_font("Arial", size=12)
+ pdf.multi_cell(0, 10, f"Country of Operation: {data['investigation'].get('country', 'N/A')}")
+ pdf.multi_cell(0, 10, f"Security Breaches: {data['investigation'].get('breaches', 'N/A')}")
+
+ pdf.ln(5)
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "2. Header Analysis", ln=True)
+
+ pdf.set_font("Arial", size=12)
+ pdf.multi_cell(0, 10, f"Security Grade: {data['headers'].get('grade', 'N/A')}")
+ if data['headers'].get('weaknesses'):
+ pdf.cell(0, 10, "Weaknesses:", ln=True)
+ for weakness in data['headers']['weaknesses']:
+ pdf.cell(0, 10, f"- {weakness}", ln=True)
+
+ pdf.add_page()
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Appendix", ln=True)
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full WHOIS Data", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, data['investigation'].get('whois_data', 'N/A'))
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full Header Analysis", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, str(data['headers'].get('headers', 'N/A')))
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full Breach Data", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, str(data['investigation'].get('breaches', 'N/A')))
+
+ pdf.output(pdf_path)
+
+ print(f"Results saved to: {docx_path}, {md_path}, {pdf_path}")
+
+if __name__ == "__main__":
+ # User inputs
+ website_url = input("Enter the website URL (including https://): ").strip()
+ default_directory = os.getcwd()
+ print(f"Default save directory: {default_directory}")
+ use_default = input("Do you want to use the default directory? (y/n): ").strip().lower()
+
+ if use_default == 'y':
+ output_directory = default_directory
+ else:
+ output_directory = input("Enter the directory to save results: ").strip()
+
+ # Extract domain name for unique file naming
+ domain = website_url.replace("https://", "").replace("http://", "").split("/")[0]
+
+ # Perform analysis
+ investigation_results = investigate_website(website_url)
+ header_analysis_results = analyze_headers(website_url)
+
+ # Combine results
+ results = {
+ "investigation": investigation_results,
+ "headers": header_analysis_results
+ }
+
+ # Save to files
+ save_results_to_files(results, output_directory, domain)
diff --git a/Security Stack/CompanyInvestigation/CompanyReview.txt b/Security Stack/CompanyInvestigation/CompanyReview.txt
new file mode 100644
index 0000000..fb93f75
--- /dev/null
+++ b/Security Stack/CompanyInvestigation/CompanyReview.txt
@@ -0,0 +1,220 @@
+import os
+import requests
+import whois
+from docx import Document
+from fpdf import FPDF
+import datetime
+
+def investigate_website(url):
+ """Investigate the website to find the country of operation and past security breaches."""
+ try:
+ # Perform WHOIS lookup to determine country
+ domain = url.replace("https://", "").replace("http://", "").split("/")[0]
+ whois_data = whois.whois(domain)
+ country = whois_data.get("country", "Unknown")
+
+ # Check for breaches using Have I Been Pwned API (requires API key)
+ hibp_api_url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{domain}"
+ headers = {"hibp-api-key": "YOUR_API_KEY_HERE"} # Replace with your HIBP API key
+ breaches = []
+
+ try:
+ breach_response = requests.get(hibp_api_url, headers=headers)
+ if breach_response.status_code == 200:
+ breaches = breach_response.json()
+ elif breach_response.status_code == 404:
+ breaches = "No breaches found."
+ else:
+ breaches = f"Error: {breach_response.status_code}"
+ except Exception as e:
+ breaches = f"Error fetching breaches: {e}"
+
+ return {
+ "country": country,
+ "breaches": breaches,
+ "whois_data": str(whois_data)
+ }
+ except Exception as e:
+ return {
+ "country": "Error determining country",
+ "breaches": f"Error fetching breaches: {e}",
+ "whois_data": f"Error: {e}"
+ }
+
+def analyze_headers(url):
+ """Perform a header analysis to identify security weaknesses."""
+ try:
+ response = requests.get(url)
+ headers = response.headers
+
+ # Security headers to check
+ security_headers = {
+ "Content-Security-Policy": "Missing or insufficient Content-Security-Policy",
+ "Strict-Transport-Security": "Missing Strict-Transport-Security",
+ "X-Content-Type-Options": "Missing X-Content-Type-Options",
+ "X-Frame-Options": "Missing X-Frame-Options",
+ "X-XSS-Protection": "Missing or insufficient X-XSS-Protection",
+ "Referrer-Policy": "Missing Referrer-Policy",
+ "Permissions-Policy": "Missing Permissions-Policy"
+ }
+
+ weaknesses = []
+ for header, issue in security_headers.items():
+ if header not in headers:
+ weaknesses.append(issue)
+
+ grade = "A" if not weaknesses else "C" if len(weaknesses) < 3 else "F"
+
+ return {
+ "grade": grade,
+ "weaknesses": weaknesses,
+ "headers": dict(headers)
+ }
+ except Exception as e:
+ return {
+ "grade": "Error",
+ "weaknesses": [f"Error analyzing headers: {e}"],
+ "headers": f"Error: {e}"
+ }
+
+def save_results_to_files(data, output_dir, domain):
+ """Save investigation results to docx, pdf, and markdown files."""
+ os.makedirs(output_dir, exist_ok=True)
+
+ # Generate unique filenames using timestamp and domain
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ # Ensure the domain is a valid filename
+ domain_safe = "".join(c if c.isalnum() else "_" for c in domain)
+ base_filename = f"{domain_safe}_{timestamp}"
+ docx_path = os.path.join(output_dir, f"{base_filename}.docx")
+ pdf_path = os.path.join(output_dir, f"{base_filename}.pdf")
+ md_path = os.path.join(output_dir, f"{base_filename}.md")
+
+ # Create a Word document
+ doc = Document()
+ doc.add_heading("Website Security Analysis", level=1)
+
+ doc.add_heading("1. Website Investigation", level=2)
+ doc.add_paragraph(f"Country of Operation: {data['investigation'].get('country', 'N/A')}")
+ doc.add_paragraph(f"Security Breaches: {data['investigation'].get('breaches', 'N/A')}")
+
+ doc.add_heading("2. Header Analysis", level=2)
+ doc.add_paragraph(f"Security Grade: {data['headers'].get('grade', 'N/A')}")
+ if data['headers'].get('weaknesses'):
+ doc.add_paragraph("Weaknesses:")
+ for weakness in data['headers']['weaknesses']:
+ doc.add_paragraph(f"- {weakness}", style="List Bullet")
+
+ # Add Appendix
+ doc.add_heading("Appendix", level=1)
+ doc.add_heading("Full WHOIS Data", level=2)
+ doc.add_paragraph(data['investigation'].get('whois_data', 'N/A'))
+
+ doc.add_heading("Full Header Analysis", level=2)
+ doc.add_paragraph(str(data['headers'].get('headers', 'N/A')))
+
+ doc.add_heading("Full Breach Data", level=2)
+ doc.add_paragraph(str(data['investigation'].get('breaches', 'N/A')))
+
+ doc.save(docx_path)
+
+ # Create a Markdown file
+ with open(md_path, "w") as md_file:
+ md_file.write("# Website Security Analysis\n\n")
+ md_file.write("## 1. Website Investigation\n")
+ md_file.write(f"- Country of Operation: {data['investigation'].get('country', 'N/A')}\n")
+ md_file.write(f"- Security Breaches: {data['investigation'].get('breaches', 'N/A')}\n\n")
+ md_file.write("## 2. Header Analysis\n")
+ md_file.write(f"- Security Grade: {data['headers'].get('grade', 'N/A')}\n")
+ if data['headers'].get('weaknesses'):
+ md_file.write("- Weaknesses:\n")
+ for weakness in data['headers']['weaknesses']:
+ md_file.write(f" - {weakness}\n")
+
+ md_file.write("\n# Appendix\n\n")
+ md_file.write("## Full WHOIS Data\n")
+ md_file.write(f"```\n{data['investigation'].get('whois_data', 'N/A')}\n```\n")
+
+ md_file.write("## Full Header Analysis\n")
+ md_file.write(f"```\n{data['headers'].get('headers', 'N/A')}\n```\n")
+
+ md_file.write("## Full Breach Data\n")
+ md_file.write(f"```\n{data['investigation'].get('breaches', 'N/A')}\n```\n")
+
+ # Create a PDF file using FPDF
+ pdf = FPDF()
+ pdf.set_auto_page_break(auto=True, margin=15)
+ pdf.add_page()
+ pdf.set_font("Arial", size=12)
+
+ pdf.cell(200, 10, txt="Website Security Analysis", ln=True, align='C')
+
+ pdf.ln(10)
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "1. Website Investigation", ln=True)
+
+ pdf.set_font("Arial", size=12)
+ pdf.multi_cell(0, 10, f"Country of Operation: {data['investigation'].get('country', 'N/A')}")
+ pdf.multi_cell(0, 10, f"Security Breaches: {data['investigation'].get('breaches', 'N/A')}")
+
+ pdf.ln(5)
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "2. Header Analysis", ln=True)
+
+ pdf.set_font("Arial", size=12)
+ pdf.multi_cell(0, 10, f"Security Grade: {data['headers'].get('grade', 'N/A')}")
+ if data['headers'].get('weaknesses'):
+ pdf.cell(0, 10, "Weaknesses:", ln=True)
+ for weakness in data['headers']['weaknesses']:
+ pdf.cell(0, 10, f"- {weakness}", ln=True)
+
+ pdf.add_page()
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Appendix", ln=True)
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full WHOIS Data", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, data['investigation'].get('whois_data', 'N/A'))
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full Header Analysis", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, str(data['headers'].get('headers', 'N/A')))
+
+ pdf.set_font("Arial", style="B", size=12)
+ pdf.cell(0, 10, "Full Breach Data", ln=True)
+ pdf.set_font("Arial", size=10)
+ pdf.multi_cell(0, 10, str(data['investigation'].get('breaches', 'N/A')))
+
+ pdf.output(pdf_path)
+
+ print(f"Results saved to: {docx_path}, {md_path}, {pdf_path}")
+
+if __name__ == "__main__":
+ # User inputs
+ website_url = input("Enter the website URL (including https://): ").strip()
+ default_directory = os.getcwd()
+ print(f"Default save directory: {default_directory}")
+ use_default = input("Do you want to use the default directory? (y/n): ").strip().lower()
+
+ if use_default == 'y':
+ output_directory = default_directory
+ else:
+ output_directory = input("Enter the directory to save results: ").strip()
+
+ # Extract domain name for unique file naming
+ domain = website_url.replace("https://", "").replace("http://", "").split("/")[0]
+
+ # Perform analysis
+ investigation_results = investigate_website(website_url)
+ header_analysis_results = analyze_headers(website_url)
+
+ # Combine results
+ results = {
+ "investigation": investigation_results,
+ "headers": header_analysis_results
+ }
+
+ # Save to files
+ save_results_to_files(results, output_directory, domain)
diff --git a/Security Stack/CompanyInvestigation/sitereview.py b/Security Stack/CompanyInvestigation/sitereview.py
new file mode 100644
index 0000000..0fb299f
--- /dev/null
+++ b/Security Stack/CompanyInvestigation/sitereview.py
@@ -0,0 +1,79 @@
+import pandas as pd
+import os
+import whois
+import socket
+import requests
+from ipwhois import IPWhois
+
+def aggregate_csv_to_excel(folder_path, output_excel):
+ with pd.ExcelWriter(output_excel, engine='xlsxwriter') as writer:
+ for filename in os.listdir(folder_path):
+ if filename.endswith('.csv'):
+ file_path = os.path.join(folder_path, filename)
+ df = pd.read_csv(file_path)
+
+ # Filter relevant columns
+ columns = ['timestamp', 'client_ip', 'server_ip', 'server_port', 'direction', 'total_bytes', 'country', 'flow_state']
+ if 'dns' in df.columns:
+ columns.append('dns')
+ df = df[columns]
+
+ # Investigate each server IP
+ df['whois_info'] = df['server_ip'].apply(get_whois_info)
+ df['geo_info'] = df['server_ip'].apply(get_geo_info)
+ df['virus_total'] = df['server_ip'].apply(check_virus_total)
+ df['dns_lookup'] = df['server_ip'].apply(dns_lookup)
+
+ # Write to Excel
+ sheet_name = os.path.splitext(filename)[0]
+ df.to_excel(writer, sheet_name=sheet_name, index=False)
+
+def get_whois_info(ip):
+ try:
+ w = whois.whois(ip)
+ return f"Registrar: {w.registrar}, Country: {w.country}"
+ except Exception as e:
+ return f"WHOIS Error: {str(e)}"
+
+def get_geo_info(ip):
+ try:
+ obj = IPWhois(ip)
+ res = obj.lookup_rdap()
+ return f"Country: {res['asn_country_code']}, ASN: {res['asn']}"
+ except Exception as e:
+ return f"Geo Error: {str(e)}"
+
+def check_virus_total(ip):
+ try:
+ url = f"https://www.virustotal.com/api/v3/ip_addresses/{ip}"
+ headers = {"x-apikey": "your-api-key-here"} # Replace with your VirusTotal API key
+ response = requests.get(url, headers=headers)
+ if response.status_code == 200:
+ data = response.json()
+ return f"Reputation: {data.get('data', {}).get('attributes', {}).get('reputation', 'N/A')}"
+ else:
+ return f"VT Error: {response.status_code}"
+ except Exception as e:
+ return f"VT Error: {str(e)}"
+
+def dns_lookup(ip):
+ try:
+ return socket.gethostbyaddr(ip)[0]
+ except socket.herror:
+ return "DNS Lookup Error"
+
+def main():
+ current_dir = os.getcwd()
+ user_input = input(f"Do you want to use the current directory ({current_dir}) to pull CSV files? (yes/no): ").strip().lower()
+
+ if user_input == 'yes':
+ folder_path = current_dir
+ else:
+ folder_path = input("Please enter the directory where the CSV files are located: ").strip()
+
+ output_excel = os.path.join(current_dir, 'aggregated_investigation.xlsx')
+ aggregate_csv_to_excel(folder_path, output_excel)
+ print(f"Aggregated investigation saved to {output_excel}")
+
+if __name__ == "__main__":
+ main()
diff --git a/Security Stack/SecurityReview/SoftwareSecurityReview.py b/Security Stack/SecurityReview/SoftwareSecurityReview.py
new file mode 100644
index 0000000..abb77df
--- /dev/null
+++ b/Security Stack/SecurityReview/SoftwareSecurityReview.py
@@ -0,0 +1,340 @@
+import datetime
+from docx import Document
+from docx2pdf import convert
+
+def create_assessment_doc(data):
+ """Creates a Word document from the assessment data."""
+
+ document = Document()
+ document.add_heading('Software/Vendor Security Assessment Request', 0)
+
+ # 1. Requestor Information
+ document.add_heading('1. Requestor Information', level=1)
+ document.add_paragraph(f"Name: {data['requestor_name']}")
+ document.add_paragraph(f"Department: {data['department']}")
+ document.add_paragraph(f"Email Address: {data['email']}")
+ document.add_paragraph(f"Phone Number: {data['phone']}")
+
+ # 2. Request Type
+ document.add_heading('2. Request Type', level=1)
+ document.add_paragraph(data['request_type'])
+
+ # 3. Deployment Model
+ document.add_heading('3. Deployment Model', level=1)
+ document.add_paragraph(data['deployment_model'])
+
+ # 4. Software/Vendor Information
+ document.add_heading('4. Software/Vendor Information', level=1)
+ document.add_paragraph(f"Software/Vendor Name: {data['vendor_name']}")
+ document.add_paragraph(f"Software Name: {data['software_name']}")
+ document.add_paragraph(f"Software/Vendor Website: {data['vendor_website']}")
+ document.add_paragraph(f"Software/Vendor Contact Information: {data['vendor_contact']}")
+ document.add_paragraph(f"Brief Description of Software/Vendor and its Purpose: {data['vendor_description']}")
+
+ # 5. Intended Use
+ document.add_heading('5. Intended Use', level=1)
+ document.add_paragraph(f"Describe how the software/vendor will be used within the company: {data['intended_use']}")
+ document.add_paragraph(f"Specify the departments or teams that will be using the software/vendor: {data['departments_teams']}")
+ document.add_paragraph(f"Identify any critical business processes or data that will be impacted by the software/vendor: {data['critical_processes_data']}")
+
+ # 6. Prerequisites
+ document.add_heading('6. Prerequisites', level=1)
+ document.add_paragraph(f"Operating System Requirements: {data['os_requirements']}")
+ document.add_paragraph(f"Hardware Requirements (e.g., RAM, storage space): {data['hardware_requirements']}")
+ document.add_paragraph(f"Network Connectivity (e.g., bandwidth, VPN): {data['network_connectivity']}")
+ document.add_paragraph(f"Software Dependencies (e.g., libraries, frameworks): {data['software_dependencies']}")
+ document.add_paragraph(f"User Access Requirements (e.g., specific roles, permissions): {data['user_access_requirements']}")
+ document.add_paragraph(f"Other Prerequisites: {data['other_prerequisites']}")
+
+ # 7. Technical Information
+ document.add_heading('7. Technical Information', level=1)
+ document.add_paragraph(f"Hosting Environment (e.g., cloud provider, on-premise data center): {data['hosting_environment']}")
+ document.add_paragraph(f"Network Requirements (e.g., ports, protocols, firewall rules): {data['network_requirements']}")
+ document.add_paragraph(f"Data Storage Requirements (e.g., location, encryption, data retention policies): {data['data_storage_requirements']}")
+ document.add_paragraph(f"Integration with Existing Systems (if any): {data['integration_existing_systems']}")
+ document.add_paragraph(f"API Information (if applicable): {data['api_information']}")
+
+ # 8. Security Requirements
+ document.add_heading('8. Security Requirements', level=1)
+ document.add_paragraph(f"Data Security - How will data be protected in transit and at rest?: {data['data_protection']}")
+ document.add_paragraph(f"Data Security - Does the software/vendor comply with relevant data privacy regulations (e.g., GDPR, CCPA)?: {data['data_privacy_compliance']}")
+ document.add_paragraph(f"Data Security - What data encryption methods are used?: {data['data_encryption_methods']}")
+ document.add_paragraph(f"Access Control - How will access to the software/vendor be granted and managed?: {data['access_control_management']}")
+ document.add_paragraph(f"Access Control - Does the software/vendor support multi-factor authentication?: {data['multi_factor_authentication']}")
+ document.add_paragraph(f"Access Control - What user roles and permissions are available?: {data['user_roles_permissions']}")
+ document.add_paragraph(f"Vulnerability Management - Does the software/vendor have a vulnerability disclosure program?: {data['vulnerability_disclosure']}")
+ document.add_paragraph(f"Vulnerability Management - How are security patches and updates managed?: {data['security_patch_management']}")
+ document.add_paragraph(f"Vulnerability Management - Are regular security assessments and penetration testing conducted?: {data['security_assessments']}")
+ document.add_paragraph(f"Compliance - Does the software/vendor comply with relevant industry security standards (e.g., ISO 27001, SOC 2)?: {data['industry_security_standards']}")
+ document.add_paragraph(f"Compliance - Are there any third-party security certifications or audits available?: {data['third_party_certifications']}")
+ document.add_paragraph(f"Business Continuity and Disaster Recovery - What are the software/vendor's business continuity and disaster recovery plans?: {data['business_continuity_plans']}")
+ document.add_paragraph(f"Business Continuity and Disaster Recovery - What are the recovery time objectives (RTOs) and recovery point objectives (RPOs)?: {data['rto_rpo']}")
+
+ # 9. Privacy Requirements
+ document.add_heading('9. Privacy Requirements', level=1)
+ document.add_paragraph(f"Data Collection - What personal data will be collected by the software/vendor?: {data['personal_data_collected']}")
+ document.add_paragraph(f"Data Collection - What is the purpose of collecting this data?: {data['data_collection_purpose']}")
+ document.add_paragraph(f"Data Collection - Will data be shared with third parties? If so, with whom and for what purpose?: {data['data_sharing']}")
+ document.add_paragraph(f"Data Subject Rights - How does the software/vendor ensure data subject rights (e.g., access, rectification, erasure)?: {data['data_subject_rights']}")
+ document.add_paragraph(f"Data Subject Rights - Does the software/vendor provide a privacy policy that is readily accessible to users?: {data['privacy_policy']}")
+
+ # 10. Legal and Contractual Requirements
+ document.add_heading('10. Legal and Contractual Requirements', level=1)
+ document.add_paragraph(f"Software License Agreement: {data['license_agreement']}")
+ document.add_paragraph(f"Service Level Agreement (SLA): {data['sla']}")
+ document.add_paragraph(f"Data Processing Agreement (DPA): {data['dpa']}")
+ document.add_paragraph(f"Vendor Security Policies: {data['vendor_security_policies']}")
+
+ # 11. Risk Assessment
+ document.add_heading('11. Risk Assessment', level=1)
+ document.add_paragraph(f"Identify any potential security or privacy risks associated with the use of the software/vendor: {data['potential_risks']}")
+ document.add_paragraph(f"Describe the risk mitigation measures that will be implemented: {data['risk_mitigation']}")
+
+ # 12. Approval (This section will likely be filled later)
+ document.add_heading('12. Approval', level=1)
+ document.add_paragraph("Security Analyst Review:")
+ document.add_paragraph("Date of Review: ")
+ document.add_paragraph("Security Analyst Name: ")
+ document.add_paragraph("Security Assessment Findings: ")
+ document.add_paragraph("Approval Status (Approved, Conditionally Approved, Rejected): ")
+ document.add_paragraph("Management Approval (if required):")
+ document.add_paragraph("Date of Approval: ")
+ document.add_paragraph("Approving Manager Name: ")
+
+ # 13. Supporting Documentation
+ document.add_heading('13. Supporting Documentation', level=1)
+ document.add_paragraph("Attach any relevant supporting documentation, such as:")
+ document.add_paragraph("Vendor security questionnaires")
+ document.add_paragraph("Penetration testing reports")
+ document.add_paragraph("Security audit reports")
+ document.add_paragraph("Privacy impact assessments")
+
+ return document
+
+def create_markdown_file(data, md_filename): # Receive filename as argument
+ """Creates a Markdown file from the assessment data."""
+
+ with open(md_filename, "w") as f: # Use the provided filename
+ # Add all the headings and data from the form in Markdown format
+ f.write("# Software/Vendor Security Assessment Request\n\n")
+
+ f.write("## 1. Requestor Information\n")
+ f.write(f"Name: {data['requestor_name']}\n")
+ f.write(f"Department: {data['department']}\n")
+ f.write(f"Email Address: {data['email']}\n")
+ f.write(f"Phone Number: {data['phone']}\n")
+
+ f.write("\n## 2. Request Type\n")
+ f.write(f"{data['request_type']}\n")
+
+ f.write("\n## 3. Deployment Model\n")
+ f.write(f"{data['deployment_model']}\n")
+
+ f.write("\n## 4. Software/Vendor Information\n")
+ f.write(f"Software/Vendor Name: {data['vendor_name']}\n")
+ f.write(f"Software Name: {data['software_name']}\n")
+ f.write(f"Software/Vendor Website: {data['vendor_website']}\n")
+ f.write(f"Software/Vendor Contact Information: {data['vendor_contact']}\n")
+ f.write(f"Brief Description of Software/Vendor and its Purpose: {data['vendor_description']}\n")
+
+ f.write("\n## 5. Intended Use\n")
+ f.write(f"Describe how the software/vendor will be used within the company: {data['intended_use']}\n")
+ f.write(f"Specify the departments or teams that will be using the software/vendor: {data['departments_teams']}\n")
+ f.write(f"Identify any critical business processes or data that will be impacted by the software/vendor: {data['critical_processes_data']}\n")
+
+ f.write("\n## 6. Prerequisites\n")
+ f.write(f"Operating System Requirements: {data['os_requirements']}\n")
+ f.write(f"Hardware Requirements (e.g., RAM, storage space): {data['hardware_requirements']}\n")
+ f.write(f"Network Connectivity (e.g., bandwidth, VPN): {data['network_connectivity']}\n")
+ f.write(f"Software Dependencies (e.g., libraries, frameworks): {data['software_dependencies']}\n")
+ f.write(f"User Access Requirements (e.g., specific roles, permissions): {data['user_access_requirements']}\n")
+ f.write(f"Other Prerequisites: {data['other_prerequisites']}\n")
+
+ f.write("\n## 7. Technical Information\n")
+ f.write(f"Hosting Environment (e.g., cloud provider, on-premise data center): {data['hosting_environment']}\n")
+ f.write(f"Network Requirements (e.g., ports, protocols, firewall rules): {data['network_requirements']}\n")
+ f.write(f"Data Storage Requirements (e.g., location, encryption, data retention policies): {data['data_storage_requirements']}\n")
+ f.write(f"Integration with Existing Systems (if any): {data['integration_existing_systems']}\n")
+ f.write(f"API Information (if applicable): {data['api_information']}\n")
+
+ f.write("\n## 8. Security Requirements\n")
+ f.write(f"Data Security - How will data be protected in transit and at rest?: {data['data_protection']}\n")
+ f.write(f"Data Security - Does the software/vendor comply with relevant data privacy regulations (e.g., GDPR, CCPA)?: {data['data_privacy_compliance']}\n")
+ f.write(f"Data Security - What data encryption methods are used?: {data['data_encryption_methods']}\n")
+ f.write(f"Access Control - How will access to the software/vendor be granted and managed?: {data['access_control_management']}\n")
+ f.write(f"Access Control - Does the software/vendor support multi-factor authentication?: {data['multi_factor_authentication']}\n")
+ f.write(f"Access Control - What user roles and permissions are available?: {data['user_roles_permissions']}\n")
+ f.write(f"Vulnerability Management - Does the software/vendor have a vulnerability disclosure program?: {data['vulnerability_disclosure']}\n")
+ f.write(f"Vulnerability Management - How are security patches and updates managed?: {data['security_patch_management']}\n")
+ f.write(f"Vulnerability Management - Are regular security assessments and penetration testing conducted?: {data['security_assessments']}\n")
+ f.write(f"Compliance - Does the software/vendor comply with relevant industry security standards (e.g., ISO 27001, SOC 2)?: {data['industry_security_standards']}\n")
+ f.write(f"Compliance - Are there any third-party security certifications or audits available?: {data['third_party_certifications']}\n")
+ f.write(f"Business Continuity and Disaster Recovery - What are the software/vendor's business continuity and disaster recovery plans?: {data['business_continuity_plans']}\n")
+ f.write(f"Business Continuity and Disaster Recovery - What are the recovery time objectives (RTOs) and recovery point objectives (RPOs)?: {data['rto_rpo']}\n")
+
+ f.write("\n## 9. Privacy Requirements\n")
+ f.write(f"Data Collection - What personal data will be collected by the software/vendor?: {data['personal_data_collected']}\n")
+ f.write(f"Data Collection - What is the purpose of collecting this data?: {data['data_collection_purpose']}\n")
+ f.write(f"Data Collection - Will data be shared with third parties? If so, with whom and for what purpose?: {data['data_sharing']}\n")
+ f.write(f"Data Subject Rights - How does the software/vendor ensure data subject rights (e.g., access, rectification, erasure)?: {data['data_subject_rights']}\n")
+ f.write(f"Data Subject Rights - Does the software/vendor provide a privacy policy that is readily accessible to users?: {data['privacy_policy']}\n")
+
+ f.write("\n## 10. Legal and Contractual Requirements\n")
+ f.write(f"Software License Agreement: {data['license_agreement']}\n")
+ f.write(f"Service Level Agreement (SLA): {data['sla']}\n")
+ f.write(f"Data Processing Agreement (DPA): {data['dpa']}\n")
+ f.write(f"Vendor Security Policies: {data['vendor_security_policies']}\n")
+
+ f.write("\n## 11. Risk Assessment\n")
+ f.write(f"Identify any potential security or privacy risks associated with the use of the software/vendor: {data['potential_risks']}\n")
+ f.write(f"Describe the risk mitigation measures that will be implemented: {data['risk_mitigation']}\n")
+
+ f.write("\n## 12. Approval\n")
+ f.write("Security Analyst Review:\n")
+ f.write("Date of Review: \n")
+ f.write("Security Analyst Name: \n")
+ f.write("Security Assessment Findings: \n")
+ f.write("Approval Status (Approved, Conditionally Approved, Rejected): \n")
+ f.write("Management Approval (if required):\n")
+ f.write("Date of Approval: \n")
+ f.write("Approving Manager Name: \n")
+
+ f.write("\n## 13. Supporting Documentation\n")
+ f.write("Attach any relevant supporting documentation, such as:\n")
+ f.write("- Vendor security questionnaires\n")
+ f.write("- Penetration testing reports\n")
+ f.write("- Security audit reports\n")
+ f.write("- Privacy impact assessments\n")
+
+
+def main():
+ """Collects assessment data from user input and generates documents."""
+
+ data = {}
+
+ # 1. Requestor Information
+ data['requestor_name'] = input("Enter your name: ")
+ data['department'] = input("Enter your department: ")
+ data['email'] = input("Enter your email address: ")
+ data['phone'] = input("Enter your phone number: ")
+
+ # 2. Request Type
+ print("\nRequest Type:")
+ print("1. New Software/Vendor")
+ print("2. Existing Software/Vendor (Renewal/Upgrade)")
+ print("3. Software/Vendor Decommissioning")
+ choice = input("Enter your choice (1-3): ")
+ request_types = {
+ "1": "New Software/Vendor",
+ "2": "Existing Software/Vendor (Renewal/Upgrade)",
+ "3": "Software/Vendor Decommissioning"
+ }
+ data['request_type'] = request_types.get(choice)
+
+ # 3. Deployment Model
+ print("\nDeployment Model:")
+ print("1. SaaS (Software as a Service)")
+ print("2. IaaS (Infrastructure as a Service)")
+ print("3. PaaS (Platform as a Service)")
+ print("4. On-Premises (Internally Hosted)")
+ print("5. Hybrid")
+ print("6. Cloud-Hosted")
+ print("7. Other (Please Specify)")
+ choice = input("Enter your choice (1-7): ")
+ if choice == "7":
+ data['deployment_model'] = input("Specify the deployment model: ")
+ else:
+ deployment_models = {
+ "1": "SaaS (Software as a Service)",
+ "2": "IaaS (Infrastructure as a Service)",
+ "3": "PaaS (Platform as a Service)",
+ "4": "On-Premises (Internally Hosted)",
+ "5": "Hybrid",
+ "6": "Cloud-Hosted"
+ }
+ data['deployment_model'] = deployment_models.get(choice)
+
+ # 4. Software/Vendor Information
+ data['vendor_name'] = input("Enter the Software/Vendor Name: ")
+ data['software_name'] = input("Enter the Software Name: ")
+ data['vendor_website'] = input("Enter the Software/Vendor Website: ")
+ data['vendor_contact'] = input("Enter the Software/Vendor Contact Information: ")
+ data['vendor_description'] = input("Enter a Brief Description of Software/Vendor and its Purpose: ")
+
+ # 5. Intended Use
+ data['intended_use'] = input("Describe how the software/vendor will be used within the company: ")
+ data['departments_teams'] = input("Specify the departments or teams that will be using the software/vendor: ")
+ data['critical_processes_data'] = input("Identify any critical business processes or data that will be impacted by the software/vendor: ")
+
+ # 6. Prerequisites
+ data['os_requirements'] = input("List any Operating System Requirements: ")
+ data['hardware_requirements'] = input("List any Hardware Requirements (e.g., RAM, storage space): ")
+ data['network_connectivity'] = input("List any Network Connectivity Requirements (e.g., bandwidth, VPN): ")
+ data['software_dependencies'] = input("List any Software Dependencies (e.g., libraries, frameworks): ")
+ data['user_access_requirements'] = input("List any User Access Requirements (e.g., specific roles, permissions): ")
+ data['other_prerequisites'] = input("List any Other Prerequisites: ")
+
+ # 7. Technical Information
+ data['hosting_environment'] = input("Provide the Hosting Environment (e.g., cloud provider, on-premise data center): ")
+ data['network_requirements'] = input("Provide the Network Requirements (e.g., ports, protocols, firewall rules): ")
+ data['data_storage_requirements'] = input("Provide the Data Storage Requirements (e.g., location, encryption, data retention policies): ")
+ data['integration_existing_systems'] = input("Describe the Integration with Existing Systems (if any): ")
+ data['api_information'] = input("Provide API Information (if applicable): ")
+
+ # 8. Security Requirements
+ data['data_protection'] = input("Data Security - How will data be protected in transit and at rest?: ")
+ data['data_privacy_compliance'] = input("Data Security - Does the software/vendor comply with relevant data privacy regulations (e.g., GDPR, CCPA)?: ")
+ data['data_encryption_methods'] = input("Data Security - What data encryption methods are used?: ")
+ data['access_control_management'] = input("Access Control - How will access to the software/vendor be granted and managed?: ")
+ data['multi_factor_authentication'] = input("Access Control - Does the software/vendor support multi-factor authentication?: ")
+ data['user_roles_permissions'] = input("Access Control - What user roles and permissions are available?: ")
+ data['vulnerability_disclosure'] = input("Vulnerability Management - Does the software/vendor have a vulnerability disclosure program?: ")
+ data['security_patch_management'] = input("Vulnerability Management - How are security patches and updates managed?: ")
+ data['security_assessments'] = input("Vulnerability Management - Are regular security assessments and penetration testing conducted?: ")
+ data['industry_security_standards'] = input("Compliance - Does the software/vendor comply with relevant industry security standards (e.g., ISO 27001, SOC 2)?: ")
+ data['third_party_certifications'] = input("Compliance - Are there any third-party security certifications or audits available?: ")
+ data['business_continuity_plans'] = input("Business Continuity and Disaster Recovery - What are the software/vendor's business continuity and disaster recovery plans?: ")
+ data['rto_rpo'] = input("Business Continuity and Disaster Recovery - What are the recovery time objectives (RTOs) and recovery point objectives (RPOs)?: ")
+
+ # 9. Privacy Requirements
+ data['personal_data_collected'] = input("Data Collection - What personal data will be collected by the software/vendor?: ")
+ data['data_collection_purpose'] = input("Data Collection - What is the purpose of collecting this data?: ")
+ data['data_sharing'] = input("Data Collection - Will data be shared with third parties? If so, with whom and for what purpose?: ")
+ data['data_subject_rights'] = input("Data Subject Rights - How does the software/vendor ensure data subject rights (e.g., access, rectification, erasure)?: ")
+ data['privacy_policy'] = input("Data Subject Rights - Does the software/vendor provide a privacy policy that is readily accessible to users?: ")
+
+ # 10. Legal and Contractual Requirements
+ data['license_agreement'] = input("Provide the Software License Agreement: ")
+ data['sla'] = input("Provide the Service Level Agreement (SLA): ")
+ data['dpa'] = input("Provide the Data Processing Agreement (DPA): ")
+ data['vendor_security_policies'] = input("Provide the Vendor Security Policies: ")
+
+ # 11. Risk Assessment
+ data['potential_risks'] = input("Identify any potential security or privacy risks associated with the use of the software/vendor: ")
+ data['risk_mitigation'] = input("Describe the risk mitigation measures that will be implemented: ")
+
+ # Generate a unique timestamp
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+
+ # Sanitize vendor/software name for filename
+ vendor_name = data['vendor_name'].replace(" ", "_").replace("/", "-")
+ software_name = data['software_name'].replace(" ", "_").replace("/", "-")
+
+ # Create and save the Word document with unique filename
+ doc_filename = f"{vendor_name}_{software_name}_assessment_{timestamp}.docx"
+ doc = create_assessment_doc(data)
+ doc.save(doc_filename)
+ print(f"Word document saved as {doc_filename}")
+
+ # Convert to PDF with unique filename
+ pdf_filename = f"{vendor_name}_{software_name}_assessment_{timestamp}.pdf"
+ convert(doc_filename, pdf_filename)
+ print(f"PDF document saved as {pdf_filename}")
+
+ # Create Markdown file with unique filename
+ md_filename = f"{vendor_name}_{software_name}_assessment_{timestamp}.md"
+ create_markdown_file(data, md_filename) # Pass filename to function
+ print(f"Markdown file saved as {md_filename}")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/Transcoded File Scripts/Find duplicates/findanddelete.py b/Transcoded File Scripts/Find duplicates/findanddelete.py
new file mode 100644
index 0000000..3749d31
--- /dev/null
+++ b/Transcoded File Scripts/Find duplicates/findanddelete.py
@@ -0,0 +1,70 @@
+import os
+import csv
+import shutil
+from collections import defaultdict
+
+def find_duplicate_basenames(root_directory, valid_extensions):
+ # Dictionary to store basenames and their corresponding file paths
+ basenames_to_paths = defaultdict(list)
+
+ # Traverse the directory tree
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ full_path = os.path.join(dirpath, filename)
+ basename, extension = os.path.splitext(filename)
+ if extension.lower() in valid_extensions:
+ basenames_to_paths[basename].append(full_path)
+
+ # Filter basenames with more than one file
+ duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
+
+ return duplicate_basenames
+
+def get_file_size(path):
+ try:
+ return os.path.getsize(path)
+ except OSError:
+ return 0
+
+def display_parent_levels(path, levels=2):
+ # Display 'levels' parent directories above the file path
+ components = path.split(os.sep)
+ return os.sep.join(components[-levels:])
+
+def prompt_user_for_selection(paths):
+ print("Choose which file to keep:")
+ for i, path in enumerate(paths, start=1):
+ size_mb = get_file_size(path) / (1024 * 1024)
+ print(f"{i}. {display_parent_levels(path)} ({size_mb:.2f} MB)")
+
+ while True:
+ try:
+ choice = int(input("Enter the number of the file to keep (or 0 to skip): "))
+ if 0 <= choice <= len(paths):
+ return choice
+ else:
+ print("Invalid choice. Please enter a valid number.")
+ except ValueError:
+ print("Invalid input. Please enter a number.")
+
+if __name__ == "__main__":
+ server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
+ video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
+ duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
+
+ if duplicate_files:
+ for basename, paths in duplicate_files.items():
+ print(f"\nBasename: {basename}")
+ choice = prompt_user_for_selection(paths)
+ if choice == 0:
+ print("Skipping this duplicate set.")
+ else:
+ # Keep the selected file and delete the others
+ keep_path = paths[choice - 1]
+ for path in paths:
+ if path != keep_path:
+ print(f"Deleting: {display_parent_levels(path)}")
+ os.remove(path)
+ print("\nDuplicate files processed.")
+ else:
+ print("No duplicate video files found.")
diff --git a/Transcoded File Scripts/Find duplicates/main.py b/Transcoded File Scripts/Find duplicates/main.py
new file mode 100644
index 0000000..c9f4b46
--- /dev/null
+++ b/Transcoded File Scripts/Find duplicates/main.py
@@ -0,0 +1,41 @@
+import os
+import csv
+from collections import defaultdict
+
+def find_duplicate_basenames(root_directory, valid_extensions):
+ # Dictionary to store basenames and their corresponding file paths
+ basenames_to_paths = defaultdict(list)
+
+ # Traverse the directory tree
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ full_path = os.path.join(dirpath, filename)
+ basename, extension = os.path.splitext(filename)
+ if extension.lower() in valid_extensions:
+ basenames_to_paths[basename].append(full_path)
+
+ # Filter basenames with more than one file
+ duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
+
+ return duplicate_basenames
+
+def export_to_csv(duplicate_files, output_csv):
+ with open(output_csv, "w", newline="") as csvfile:
+ fieldnames = ["Basename", "File Paths"]
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+ writer.writeheader()
+
+ for basename, paths in duplicate_files.items():
+ writer.writerow({"Basename": basename, "File Paths": ", ".join(paths)})
+
+if __name__ == "__main__":
+ server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
+ video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
+ duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
+
+ if duplicate_files:
+ output_csv_file = "duplicate_video_files.csv"
+ export_to_csv(duplicate_files, output_csv_file)
+ print(f"Duplicate video files exported to {output_csv_file}")
+ else:
+ print("No duplicate video files found.")
diff --git a/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/findanddelete.py b/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/findanddelete.py
new file mode 100644
index 0000000..3749d31
--- /dev/null
+++ b/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/findanddelete.py
@@ -0,0 +1,70 @@
+import os
+import csv
+import shutil
+from collections import defaultdict
+
+def find_duplicate_basenames(root_directory, valid_extensions):
+ # Dictionary to store basenames and their corresponding file paths
+ basenames_to_paths = defaultdict(list)
+
+ # Traverse the directory tree
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ full_path = os.path.join(dirpath, filename)
+ basename, extension = os.path.splitext(filename)
+ if extension.lower() in valid_extensions:
+ basenames_to_paths[basename].append(full_path)
+
+ # Filter basenames with more than one file
+ duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
+
+ return duplicate_basenames
+
+def get_file_size(path):
+ try:
+ return os.path.getsize(path)
+ except OSError:
+ return 0
+
+def display_parent_levels(path, levels=2):
+ # Display 'levels' parent directories above the file path
+ components = path.split(os.sep)
+ return os.sep.join(components[-levels:])
+
+def prompt_user_for_selection(paths):
+ print("Choose which file to keep:")
+ for i, path in enumerate(paths, start=1):
+ size_mb = get_file_size(path) / (1024 * 1024)
+ print(f"{i}. {display_parent_levels(path)} ({size_mb:.2f} MB)")
+
+ while True:
+ try:
+ choice = int(input("Enter the number of the file to keep (or 0 to skip): "))
+ if 0 <= choice <= len(paths):
+ return choice
+ else:
+ print("Invalid choice. Please enter a valid number.")
+ except ValueError:
+ print("Invalid input. Please enter a number.")
+
+if __name__ == "__main__":
+ server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
+ video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
+ duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
+
+ if duplicate_files:
+ for basename, paths in duplicate_files.items():
+ print(f"\nBasename: {basename}")
+ choice = prompt_user_for_selection(paths)
+ if choice == 0:
+ print("Skipping this duplicate set.")
+ else:
+ # Keep the selected file and delete the others
+ keep_path = paths[choice - 1]
+ for path in paths:
+ if path != keep_path:
+ print(f"Deleting: {display_parent_levels(path)}")
+ os.remove(path)
+ print("\nDuplicate files processed.")
+ else:
+ print("No duplicate video files found.")
diff --git a/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/main.py b/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/main.py
new file mode 100644
index 0000000..c9f4b46
--- /dev/null
+++ b/Transcoded File Scripts/RMF Transcoded File Scripts/Find duplicates/main.py
@@ -0,0 +1,41 @@
+import os
+import csv
+from collections import defaultdict
+
+def find_duplicate_basenames(root_directory, valid_extensions):
+ # Dictionary to store basenames and their corresponding file paths
+ basenames_to_paths = defaultdict(list)
+
+ # Traverse the directory tree
+ for dirpath, dirnames, filenames in os.walk(root_directory):
+ for filename in filenames:
+ full_path = os.path.join(dirpath, filename)
+ basename, extension = os.path.splitext(filename)
+ if extension.lower() in valid_extensions:
+ basenames_to_paths[basename].append(full_path)
+
+ # Filter basenames with more than one file
+ duplicate_basenames = {basename: paths for basename, paths in basenames_to_paths.items() if len(paths) > 1}
+
+ return duplicate_basenames
+
+def export_to_csv(duplicate_files, output_csv):
+ with open(output_csv, "w", newline="") as csvfile:
+ fieldnames = ["Basename", "File Paths"]
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+ writer.writeheader()
+
+ for basename, paths in duplicate_files.items():
+ writer.writerow({"Basename": basename, "File Paths": ", ".join(paths)})
+
+if __name__ == "__main__":
+ server_directory = r"\\truenas\isolation\videos" # Replace with your server directory
+ video_extensions = {".mp4", ".mkv", ".avi", ".mov"} # Add more video extensions if needed
+ duplicate_files = find_duplicate_basenames(server_directory, video_extensions)
+
+ if duplicate_files:
+ output_csv_file = "duplicate_video_files.csv"
+ export_to_csv(duplicate_files, output_csv_file)
+ print(f"Duplicate video files exported to {output_csv_file}")
+ else:
+ print("No duplicate video files found.")
diff --git a/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/did.py b/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/did.py
new file mode 100644
index 0000000..25e2a3b
--- /dev/null
+++ b/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/did.py
@@ -0,0 +1,182 @@
+import os
+import re
+import shutil
+from pymediainfo import MediaInfo # You might need to install this: pip install pymediainfo
+
+# Define the network locations - KEEP THESE UNCHANGED as per user's original request
+LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
+LOCATION2 = r"\\truenas\isolation\videos\redmilf"
+FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final" # This is the final destination for ALL copied files
+
+# Ensure the final destination directory exists
+os.makedirs(FINAL_LOCATION, exist_ok=True)
+
+def bytes_to_mb(bytes_value):
+ """Converts bytes to megabytes, rounded to two decimal places."""
+ return round(bytes_value / (1024 * 1024), 2)
+
+def extract_did_number(filename):
+ """
+ Extracts the DIDXXX or DIDXXXX number from a filename.
+ Returns the number as a string if found, otherwise None.
+ """
+ # Regex to match DID followed by 3 or 4 digits
+ match = re.search(r"DID(\d{3,4})", filename, re.IGNORECASE)
+ if match:
+ return match.group(1) # Return the matched number string (e.g., "001" or "1234")
+ return None
+
+def get_file_info(filepath):
+ """
+ Gets file size and attempts to get codec/bitrate using pymediainfo.
+ Returns a dictionary with 'size_bytes', 'size_mb', 'codec', 'bitrate'.
+ If mediainfo fails, 'codec' and 'bitrate' will be None.
+ """
+ try:
+ size_bytes = os.path.getsize(filepath)
+ size_mb = bytes_to_mb(size_bytes)
+ media_info = MediaInfo.parse(filepath)
+ codec = None
+ bitrate = 0 # Use 0 as a default for comparison
+
+ for track in media_info.tracks:
+ if track.track_type == 'Video':
+ if track.format:
+ codec = track.format
+ if track.bit_rate:
+ bitrate = track.bit_rate
+ break # Assuming one video track is sufficient
+
+ return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": codec, "bitrate": bitrate}
+ except Exception as e:
+ print(f"Warning: Could not get media info for {filepath}: {e}")
+ # Fallback if mediainfo fails
+ size_bytes = os.path.getsize(filepath)
+ size_mb = bytes_to_mb(size_bytes)
+ return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": None, "bitrate": 0}
+
+def main():
+ files_in_loc1 = {}
+ files_in_loc2 = {}
+
+ print("Welcome to the DID Video Comparison and Management Script!")
+ print("\nDo you want to enable 'Auto-Run' mode?")
+ print("In 'Auto-Run' mode, the script will automatically:")
+ print(" 1. Keep the file with the larger size.")
+ print(f" 2. Use the filename from '{os.path.basename(LOCATION2)}' for the final copy.")
+ print("If you decline, you will be prompted for each file manually.")
+
+ auto_run_choice = input("Enter 'yes' to enable Auto-Run, or 'no' to run manually (yes/no): ").strip().lower()
+ auto_run = (auto_run_choice == 'yes')
+
+ if auto_run:
+ print("\nAuto-Run mode enabled. Processing files automatically.")
+ else:
+ print("\nManual mode selected. You will be prompted for each file.")
+
+ # Objective 1: Identify files with the same naming convention
+ # Scan Location 1
+ for root, _, files in os.walk(LOCATION1):
+ for file in files:
+ did_num = extract_did_number(file) # Changed function call
+ if did_num:
+ if did_num not in files_in_loc1:
+ files_in_loc1[did_num] = []
+ files_in_loc1[did_num].append(os.path.join(root, file))
+
+ # Scan Location 2
+ for root, _, files in os.walk(LOCATION2):
+ for file in files:
+ did_num = extract_did_number(file) # Changed function call
+ if did_num:
+ if did_num not in files_in_loc2:
+ files_in_loc2[did_num] = []
+ files_in_loc2[did_num].append(os.path.join(root, file))
+
+ # Process matched files
+ matched_did_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
+
+ if not matched_did_numbers:
+ print(f"No matching DID files found in both '{LOCATION1}' and '{LOCATION2}'.")
+ return
+
+ for did_num in sorted(list(matched_did_numbers)):
+ # Assuming only one file per DIDXXX/XXXX number per location for simplicity.
+ path1 = files_in_loc1[did_num][0]
+ path2 = files_in_loc2[did_num][0]
+
+ file1_info = get_file_info(path1)
+ file2_info = get_file_info(path2)
+
+ print(f"\n--- Processing DID{did_num} ---")
+ print(f" File 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)} (Size: {file1_info['size_mb']} MB, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
+ print(f" File 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)} (Size: {file2_info['size_mb']} MB, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
+
+
+ file_to_keep_path = None
+ final_filename = None
+
+ if auto_run:
+ # Auto-run logic: keep larger size, use filename from Location 2
+ if file1_info['size_bytes'] >= file2_info['size_bytes']:
+ file_to_keep_path = path1
+ else:
+ file_to_keep_path = path2
+ final_filename = os.path.basename(path2) # Always use filename from Location 2
+ print(f" [Auto] Keeping: {os.path.basename(file_to_keep_path)}")
+ print(f" [Auto] Using filename: {final_filename}")
+
+ else: # Manual mode
+ # Original objective 2: Compare size, quality, and codec and list the better file
+ recommended_better_path = None
+ # Prioritize bitrate for recommendation
+ if file1_info['bitrate'] > file2_info['bitrate']:
+ recommended_better_path = path1
+ elif file2_info['bitrate'] > file1_info['bitrate']:
+ recommended_better_path = path2
+ else:
+ # If bitrates are similar or unavailable, use size as a proxy for recommendation
+ if file1_info['size_bytes'] >= file2_info['size_bytes']:
+ recommended_better_path = path1
+ else:
+ recommended_better_path = path2
+ print(f" Recommended to keep: {os.path.basename(recommended_better_path)}")
+
+
+ # Original objective 3: Prompt the user which one they would like to keep
+ while True:
+ choice = input("Which file would you like to keep? (1/2): ").strip()
+ if choice == '1':
+ file_to_keep_path = path1
+ break
+ elif choice == '2':
+ file_to_keep_path = path2
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Original objective 4: Prompt the user for each file, which one's file name will be used for the final copy
+ while True:
+ name_choice = input(f"Which file's name would you like to use for the final copy of DID{did_num}? (1/2): ").strip() # Changed prompt
+ if name_choice == '1':
+ final_filename = os.path.basename(path1)
+ break
+ elif name_choice == '2':
+ final_filename = os.path.basename(path2)
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Objective 5: Copy the selected file to the new location with the chosen name
+ destination_path = os.path.join(FINAL_LOCATION, final_filename)
+ try:
+ print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
+ shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
+ print("Copy successful!")
+ except Exception as e:
+ print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
+
+ print("\nComparison and copying process complete.")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/main.py b/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/main.py
new file mode 100644
index 0000000..edceed1
--- /dev/null
+++ b/Transcoded File Scripts/RMF Transcoded File Scripts/rmf_compare/main.py
@@ -0,0 +1,156 @@
+import os
+import re
+import shutil
+from pymediainfo import MediaInfo # You might need to install this: pip install python-mediainfo
+
+# Define the network locations
+LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
+LOCATION2 = r"\\truenas\isolation\videos\redmilf"
+FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final"
+
+# Ensure the final destination directory exists
+os.makedirs(FINAL_LOCATION, exist_ok=True)
+
+def extract_milf_number(filename):
+ """
+ Extracts the MILFXXX number from a filename.
+ Returns the number as a string if found, otherwise None.
+ """
+ match = re.search(r"MILF(\d{3})", filename, re.IGNORECASE)
+ if match:
+ return match.group(1)
+ return None
+
+def get_file_info(filepath):
+ """
+ Gets file size and attempts to get codec/bitrate using pymediainfo.
+ Returns a dictionary with 'size', 'codec', 'bitrate'.
+ If mediainfo fails, 'codec' and 'bitrate' will be None.
+ """
+ try:
+ size = os.path.getsize(filepath)
+ media_info = MediaInfo.parse(filepath)
+ codec = None
+ bitrate = 0 # Use 0 as a default for comparison
+
+ for track in media_info.tracks:
+ if track.track_type == 'Video':
+ if track.format:
+ codec = track.format
+ if track.bit_rate:
+ bitrate = track.bit_rate
+ break # Assuming one video track is sufficient
+
+ return {"size": size, "codec": codec, "bitrate": bitrate}
+ except Exception as e:
+ print(f"Warning: Could not get media info for {filepath}: {e}")
+ return {"size": os.path.getsize(filepath), "codec": None, "bitrate": 0}
+
+def determine_better_file(file1_info, file2_info, path1, path2):
+ """
+ Compares two file infos to determine which one is 'better'.
+ Prioritizes bitrate (quality), then size if bitrate is similar or unavailable.
+ """
+ print(f"\nComparing:")
+ print(f" File 1: {os.path.basename(path1)} (Size: {file1_info['size']} bytes, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
+ print(f" File 2: {os.path.basename(path2)} (Size: {file2_info['size']} bytes, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
+
+ # Prioritize bitrate for quality
+ if file1_info['bitrate'] > file2_info['bitrate']:
+ return path1
+ elif file2_info['bitrate'] > file1_info['bitrate']:
+ return path2
+ else:
+ # If bitrates are similar or unavailable, use size as a proxy
+ if file1_info['size'] >= file2_info['size']: # Greater or equal size usually means better quality
+ return path1
+ else:
+ return path2
+
+def main():
+ files_in_loc1 = {}
+ files_in_loc2 = {}
+
+ # Objective 1: Identify files with the same naming convention
+ # Scan Location 1
+ for root, _, files in os.walk(LOCATION1):
+ for file in files:
+ milf_num = extract_milf_number(file)
+ if milf_num:
+ if milf_num not in files_in_loc1:
+ files_in_loc1[milf_num] = []
+ files_in_loc1[milf_num].append(os.path.join(root, file))
+
+ # Scan Location 2
+ for root, _, files in os.walk(LOCATION2):
+ for file in files:
+ milf_num = extract_milf_number(file)
+ if milf_num:
+ if milf_num not in files_in_loc2:
+ files_in_loc2[milf_num] = []
+ files_in_loc2[milf_num].append(os.path.join(root, file))
+
+ # Process matched files
+ matched_milf_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
+
+ if not matched_milf_numbers:
+ print("No matching MILF files found in both locations.")
+ return
+
+ for milf_num in sorted(list(matched_milf_numbers)):
+ # Assuming only one file per MILFXXX number per location for simplicity.
+ # If multiple files with the same MILFXXX number exist in one location,
+ # this script will compare the first one it finds.
+ # For a more robust solution, you might need to handle this by comparing
+ # all permutations or selecting the "best" within each location first.
+ path1 = files_in_loc1[milf_num][0]
+ path2 = files_in_loc2[milf_num][0]
+
+ # Objective 2: Compare size, quality, and codec and list the better file
+ file1_info = get_file_info(path1)
+ file2_info = get_file_info(path2)
+
+ recommended_better_path = determine_better_file(file1_info, file2_info, path1, path2)
+
+ # Objective 3: Prompt the user which one they would like to keep
+ print(f"\nFor MILF{milf_num}:")
+ print(f" Option 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)}")
+ print(f" Option 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)}")
+ print(f" Recommended: {os.path.basename(recommended_better_path)}")
+
+ while True:
+ choice = input("Which file would you like to keep? (1/2): ").strip()
+ if choice == '1':
+ file_to_keep_path = path1
+ break
+ elif choice == '2':
+ file_to_keep_path = path2
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Objective 4: Prompt the user for each file, which one's file name will be used for the final copy
+ while True:
+ name_choice = input(f"Which file's name would you like to use for the final copy of MILF{milf_num}? (1/2): ").strip()
+ if name_choice == '1':
+ final_filename = os.path.basename(path1)
+ break
+ elif name_choice == '2':
+ final_filename = os.path.basename(path2)
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Objective 5: Copy the selected file to the new location with the chosen name
+ destination_path = os.path.join(FINAL_LOCATION, final_filename)
+ try:
+ print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
+ shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
+ print("Copy successful!")
+ except Exception as e:
+ print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
+
+ print("\nComparison and copying process complete.")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/Transcoded File Scripts/RMF Transcoded File Scripts/transcode replacements/MAIN.py b/Transcoded File Scripts/RMF Transcoded File Scripts/transcode replacements/MAIN.py
new file mode 100644
index 0000000..3f0cbc3
--- /dev/null
+++ b/Transcoded File Scripts/RMF Transcoded File Scripts/transcode replacements/MAIN.py
@@ -0,0 +1,182 @@
+import os
+import re
+import shutil
+from pymediainfo import MediaInfo # You might need to install this: pip install pymediainfo
+
+# Define the network locations
+LOCATION1 = r"\\truenas\isolation\videos\redmilf_refresh"
+LOCATION2 = r"\\truenas\isolation\videos\redmilf"
+FINAL_LOCATION = r"\\truenas\isolation\videos\redmilf_final"
+
+# Ensure the final destination directory exists
+os.makedirs(FINAL_LOCATION, exist_ok=True)
+
+def bytes_to_mb(bytes_value):
+ """Converts bytes to megabytes, rounded to two decimal places."""
+ return round(bytes_value / (1024 * 1024), 2)
+
+def extract_milf_number(filename):
+ """
+ Extracts the MILFXXX or MILFXXXX number from a filename.
+ Returns the number as a string if found, otherwise None.
+ """
+ # Updated regex to match MILF followed by 3 or 4 digits
+ match = re.search(r"MILF(\d{3,4})", filename, re.IGNORECASE)
+ if match:
+ return match.group(1) # Return the matched number string (e.g., "001" or "1234")
+ return None
+
+def get_file_info(filepath):
+ """
+ Gets file size and attempts to get codec/bitrate using pymediainfo.
+ Returns a dictionary with 'size_bytes', 'size_mb', 'codec', 'bitrate'.
+ If mediainfo fails, 'codec' and 'bitrate' will be None.
+ """
+ try:
+ size_bytes = os.path.getsize(filepath)
+ size_mb = bytes_to_mb(size_bytes)
+ media_info = MediaInfo.parse(filepath)
+ codec = None
+ bitrate = 0 # Use 0 as a default for comparison
+
+ for track in media_info.tracks:
+ if track.track_type == 'Video':
+ if track.format:
+ codec = track.format
+ if track.bit_rate:
+ bitrate = track.bit_rate
+ break # Assuming one video track is sufficient
+
+ return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": codec, "bitrate": bitrate}
+ except Exception as e:
+ print(f"Warning: Could not get media info for {filepath}: {e}")
+ # Fallback if mediainfo fails
+ size_bytes = os.path.getsize(filepath)
+ size_mb = bytes_to_mb(size_bytes)
+ return {"size_bytes": size_bytes, "size_mb": size_mb, "codec": None, "bitrate": 0}
+
+def main():
+ files_in_loc1 = {}
+ files_in_loc2 = {}
+
+ print("Welcome to the MILF Video Comparison and Management Script!")
+ print("\nDo you want to enable 'Auto-Run' mode?")
+ print("In 'Auto-Run' mode, the script will automatically:")
+ print(" 1. Keep the file with the larger size.")
+ print(f" 2. Use the filename from '{os.path.basename(LOCATION2)}' for the final copy.")
+ print("If you decline, you will be prompted for each file manually.")
+
+ auto_run_choice = input("Enter 'yes' to enable Auto-Run, or 'no' to run manually (yes/no): ").strip().lower()
+ auto_run = (auto_run_choice == 'yes')
+
+ if auto_run:
+ print("\nAuto-Run mode enabled. Processing files automatically.")
+ else:
+ print("\nManual mode selected. You will be prompted for each file.")
+
+ # Objective 1: Identify files with the same naming convention
+ # Scan Location 1
+ for root, _, files in os.walk(LOCATION1):
+ for file in files:
+ milf_num = extract_milf_number(file)
+ if milf_num:
+ if milf_num not in files_in_loc1:
+ files_in_loc1[milf_num] = []
+ files_in_loc1[milf_num].append(os.path.join(root, file))
+
+ # Scan Location 2
+ for root, _, files in os.walk(LOCATION2):
+ for file in files:
+ milf_num = extract_milf_number(file)
+ if milf_num:
+ if milf_num not in files_in_loc2:
+ files_in_loc2[milf_num] = []
+ files_in_loc2[milf_num].append(os.path.join(root, file))
+
+ # Process matched files
+ matched_milf_numbers = set(files_in_loc1.keys()).intersection(set(files_in_loc2.keys()))
+
+ if not matched_milf_numbers:
+ print("No matching MILF files found in both locations.")
+ return
+
+ for milf_num in sorted(list(matched_milf_numbers)):
+ # Assuming only one file per MILFXXX/XXXX number per location for simplicity.
+ path1 = files_in_loc1[milf_num][0]
+ path2 = files_in_loc2[milf_num][0]
+
+ file1_info = get_file_info(path1)
+ file2_info = get_file_info(path2)
+
+ print(f"\n--- Processing MILF{milf_num} ---")
+ print(f" File 1 (from '{os.path.basename(os.path.dirname(path1))}'): {os.path.basename(path1)} (Size: {file1_info['size_mb']} MB, Codec: {file1_info['codec']}, Bitrate: {file1_info['bitrate']} bps)")
+ print(f" File 2 (from '{os.path.basename(os.path.dirname(path2))}'): {os.path.basename(path2)} (Size: {file2_info['size_mb']} MB, Codec: {file2_info['codec']}, Bitrate: {file2_info['bitrate']} bps)")
+
+
+ file_to_keep_path = None
+ final_filename = None
+
+ if auto_run:
+ # Auto-run logic: keep larger size, use filename from Location 2
+ if file1_info['size_bytes'] >= file2_info['size_bytes']:
+ file_to_keep_path = path1
+ else:
+ file_to_keep_path = path2
+ final_filename = os.path.basename(path2) # Always use filename from Location 2
+ print(f" [Auto] Keeping: {os.path.basename(file_to_keep_path)}")
+ print(f" [Auto] Using filename: {final_filename}")
+
+ else: # Manual mode
+ # Original objective 2: Compare size, quality, and codec and list the better file
+ recommended_better_path = None
+ # Prioritize bitrate for recommendation
+ if file1_info['bitrate'] > file2_info['bitrate']:
+ recommended_better_path = path1
+ elif file2_info['bitrate'] > file1_info['bitrate']:
+ recommended_better_path = path2
+ else:
+ # If bitrates are similar or unavailable, use size as a proxy for recommendation
+ if file1_info['size_bytes'] >= file2_info['size_bytes']:
+ recommended_better_path = path1
+ else:
+ recommended_better_path = path2
+ print(f" Recommended to keep: {os.path.basename(recommended_better_path)}")
+
+
+ # Original objective 3: Prompt the user which one they would like to keep
+ while True:
+ choice = input("Which file would you like to keep? (1/2): ").strip()
+ if choice == '1':
+ file_to_keep_path = path1
+ break
+ elif choice == '2':
+ file_to_keep_path = path2
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Original objective 4: Prompt the user for each file, which one's file name will be used for the final copy
+ while True:
+ name_choice = input(f"Which file's name would you like to use for the final copy of MILF{milf_num}? (1/2): ").strip()
+ if name_choice == '1':
+ final_filename = os.path.basename(path1)
+ break
+ elif name_choice == '2':
+ final_filename = os.path.basename(path2)
+ break
+ else:
+ print("Invalid choice. Please enter '1' or '2'.")
+
+ # Objective 5: Copy the selected file to the new location with the chosen name
+ destination_path = os.path.join(FINAL_LOCATION, final_filename)
+ try:
+ print(f"Copying '{os.path.basename(file_to_keep_path)}' to '{destination_path}'...")
+ shutil.copy2(file_to_keep_path, destination_path) # copy2 preserves metadata
+ print("Copy successful!")
+ except Exception as e:
+ print(f"Error copying file {file_to_keep_path} to {destination_path}: {e}")
+
+ print("\nComparison and copying process complete.")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/Transcoded File Scripts/transcode replacements/MAIN.py b/Transcoded File Scripts/transcode replacements/MAIN.py
new file mode 100644
index 0000000..d9ffa02
--- /dev/null
+++ b/Transcoded File Scripts/transcode replacements/MAIN.py
@@ -0,0 +1,114 @@
+import sqlite3
+import csv
+import os
+from datetime import datetime
+
+# Function to convert Linux path to Windows path and replace specific substring
+def convert_path(path):
+ return path.replace("/", "\\").replace("\\data\\", "\\\\truenas\\isolation\\videos\\")
+
+# Fileshare path
+fileshare_path = r"\\truenas\isolation\stashapp\generated\transcodes"
+
+# SQLite database path
+db_path = r"\\servervm\main\appdata\stashapp\config\stash-go.sqlite"
+
+# Create a folder for the attempt with current date and time
+folder_name = "Attempt_" + datetime.now().strftime("%Y%m%d_%H%M%S")
+os.makedirs(folder_name)
+
+# Connect to SQLite database
+conn = sqlite3.connect(db_path)
+cursor = conn.cursor()
+
+# Error handling for gathering basenames
+try:
+ # List all file basenames (without extensions) from fileshare path
+ basenames = [os.path.splitext(name)[0] for name in os.listdir(fileshare_path)]
+except FileNotFoundError:
+ print(f"Error: Fileshare path '{fileshare_path}' not found.")
+ exit()
+except PermissionError:
+ print(f"Error: Permission denied accessing fileshare path '{fileshare_path}'.")
+ exit()
+
+# Check if there are no basenames
+if not basenames:
+ print("Error: No basenames found in the fileshare path.")
+ exit()
+
+# Construct the IN clause for the query
+placeholders = ",".join(['"{}"'.format(b) for b in basenames])
+
+# Construct the query
+query_template = """
+SELECT f.*, ff.*, fo.path
+FROM files AS f
+INNER JOIN files_fingerprints AS ff ON f.id = ff.file_id
+INNER JOIN folders AS fo ON f.parent_folder_id = fo.id
+WHERE ff.fingerprint IN ({});
+"""
+
+# Execute query
+cursor.execute(query_template.format(placeholders))
+rows = cursor.fetchall()
+
+# Check if there are any results
+if len(rows) == 0:
+ print("No results found.")
+ exit()
+
+# Export result to CSV
+csv_path = os.path.join(folder_name, "result.csv")
+with open(csv_path, "w", newline="", encoding="utf-8") as csv_file:
+ csv_writer = csv.writer(csv_file)
+ csv_writer.writerow([i[0] for i in cursor.description]) # Write header
+ csv_writer.writerows(rows)
+
+# Create new CSV with additional columns
+new_rows = []
+for row in rows:
+ converted_path = convert_path(row[-1])
+ full_file_path_video = os.path.join(converted_path, row[1]) # Construct full file path for videos
+
+ # Original filename from transcodes directory
+ original_filename = os.path.join(fileshare_path, str(row[10])) # Assuming the fingerprint ID is in the 11th column (index 10)
+
+ new_rows.append((full_file_path_video, original_filename + ".mp4")) # Append ".mp4" to the second column
+
+# Write new CSV
+new_csv_path = os.path.join(folder_name, "new_result.csv")
+with open(new_csv_path, "w", newline="", encoding="utf-8") as csv_file:
+ csv_writer = csv.writer(csv_file)
+ csv_writer.writerow(["Full File Path (Videos)", "Original File Path (Transcodes)"])
+ csv_writer.writerows(new_rows)
+
+# Confirm with the user
+print("CSV files created successfully in folder:", folder_name)
+print("Do you want to proceed to replace the files in column 1 with the files in column 2?")
+user_input = input("Enter 'yes' to proceed, or any other key to cancel: ")
+
+if user_input.lower() == 'yes':
+ print("Proceeding to replace the files...")
+
+ # Prepare to replace files
+ for original_file, new_file in new_rows:
+ original_basename = os.path.basename(original_file)
+ original_dirname = os.path.dirname(original_file)
+ new_basename = os.path.basename(new_file)
+ new_dirname = os.path.dirname(new_file)
+
+ # Remove existing file if exists
+ if os.path.exists(original_file):
+ os.remove(original_file)
+
+ # Move the new file to replace the original file
+ try:
+ os.rename(new_file, os.path.join(original_dirname, original_basename))
+ print(f"Replaced '{original_basename}' with '{new_basename}'.")
+ except FileNotFoundError:
+ print(f"Error: '{new_basename}' not found. Skipped replacement.")
+
+ print("Replacement completed.")
+else:
+ print("Replacement canceled.")
diff --git a/nginx/deluge.datdamdavid.com.conf b/nginx/deluge.datdamdavid.com.conf
new file mode 100644
index 0000000..3302a94
--- /dev/null
+++ b/nginx/deluge.datdamdavid.com.conf
@@ -0,0 +1,21 @@
+server {
+ listen 80;
+ server_name deluge.datdamdavid.com;
+
+ location / {
+ proxy_pass http://truenas:8112;
+ proxy_redirect off;
+ }
+}
+
+server {
+ listen 443;
+ server_name deluge.datdamdavid.com;
+ ssl_certificate /etc/nginx/ssl/deluge.datdamdavid.com.crt;
+ ssl_certificate_key /etc/nginx/ssl/deluge.datdamdavid.com.key;
+
+ location / {
+ proxy_pass http://truenas:8112;
+ proxy_redirect off;
+ }
+}
\ No newline at end of file
diff --git a/nginx/gitea b/nginx/gitea
new file mode 100644
index 0000000..0517f0d
--- /dev/null
+++ b/nginx/gitea
@@ -0,0 +1,15 @@
+server {
+ listen 80;
+ server_name git.datdamdavid.com; # Replace with your actual domain
+
+ location / {
+ proxy_pass http://192.168.1.254:30008; # Corrected IP address
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ # Additional headers if needed (e.g., for websockets)
+ # proxy_set_header Upgrade $upgrade;
+ # proxy_set_header Connection "upgrade";
+ }
+}
\ No newline at end of file
diff --git a/nginx/jellyseer.datdamdavid.com.conf b/nginx/jellyseer.datdamdavid.com.conf
new file mode 100644
index 0000000..b3d6e93
--- /dev/null
+++ b/nginx/jellyseer.datdamdavid.com.conf
@@ -0,0 +1,21 @@
+server {
+ listen 80;
+ server_name jellyseer.datdamdavid.com;
+
+ location / {
+ proxy_pass http://truenas:30042;
+ proxy_redirect off;
+ }
+}
+
+server {
+ listen 443;
+ server_name jellyseer.datdamdavid.com;
+ ssl_certificate /etc/nginx/ssl/jellyseer.datdamdavid.com.crt;
+ ssl_certificate_key /etc/nginx/ssl/jellyseer.datdamdavid.com.key;
+
+ location / {
+ proxy_pass http://truenas:30042;
+ proxy_redirect off;
+ }
+}
\ No newline at end of file