Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
28 changes: 28 additions & 0 deletions dicom_server/dicom_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# dicom_server/dicom_parser.py
import logging
import pydicom

class DICOMParser:
def __init__(self, file_path):
self.ds = pydicom.dcmread(file_path)

def get_patient_info(self):
return {
'PatientID': getattr(self.ds, 'PatientID', 'N/A'),
'PatientName': str(getattr(self.ds, 'PatientName', 'N/A')),
'PatientBirthDate': getattr(self.ds, 'PatientBirthDate', 'N/A')
}

def get_study_info(self):
return {
'StudyInstanceUID': self.ds.StudyInstanceUID,
'StudyDate': self.ds.StudyDate,
'StudyDescription': getattr(self.ds, 'StudyDescription', 'N/A')
}

def get_series_info(self):
return {
'SeriesInstanceUID': self.ds.SeriesInstanceUID,
'SeriesDescription': getattr(self.ds, 'SeriesDescription', 'N/A'),
'Modality': self.ds.Modality
}
19 changes: 19 additions & 0 deletions dicom_server/dicomhawk.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
import time
import random


from pynetdicom import AE, evt
from pynetdicom.sop_class import VerificationSOPClass
from dicom_parser import DICOMParser
import logging


# Set up logging
log_directory = '/app/logs'
simplified_log_directory = '/app/simplified_logs'
Expand Down Expand Up @@ -230,6 +237,17 @@ def handle_store(event):
assoc_id = assoc_sessions.get(event.assoc, str(int(time.time() * 1000000)))
store_id = str(int(time.time() * 1000000))
detailed_logger.info(f"C-STORE request received: {event.dataset}")
parser = DICOMParser(event.file_meta.filename)

patient_info = parser.get_patient_info()
study_info = parser.get_study_info()
series_info = parser.get_series_info()

logging.info(f"Received file: {event.file_meta.filename}")
logging.info(f"Patient Info: {patient_info}")
logging.info(f"Study Info: {study_info}")
logging.info(f"Series Info: {series_info}")

log_simplified_message({
"session_id": assoc_id,
"ID": store_id,
Expand Down Expand Up @@ -339,4 +357,5 @@ def start_dicom_server():




start_dicom_server()
34 changes: 34 additions & 0 deletions dicom_server/test_dicom_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

import unittest
from dicom_parser import DICOMParser
import os

class TestDICOMParser(unittest.TestCase):
def setUp(self):
self.test_file = '../dicom_files/test_file.dcm'
self.parser = DICOMParser(self.test_file)

def test_get_patient_info(self):
patient_info = self.parser.get_patient_info()
self.assertIn('PatientID', patient_info)
self.assertIn('PatientName', patient_info)
self.assertIn('PatientBirthDate', patient_info)
# Check that values are either strings or 'N/A'
for value in patient_info.values():
self.assertTrue(isinstance(value, str))


def test_get_study_info(self):
study_info = self.parser.get_study_info()
self.assertIn('StudyInstanceUID', study_info)
self.assertIn('StudyDate', study_info)
self.assertIn('StudyDescription', study_info)

def test_get_series_info(self):
series_info = self.parser.get_series_info()
self.assertIn('SeriesInstanceUID', series_info)
self.assertIn('SeriesDescription', series_info)
self.assertIn('Modality', series_info)

if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions flask_logging_server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ COPY . .

# Set the working directory in the container
WORKDIR /.
COPY requirements.txt .
RUN pip install --user -r requirements.txt

RUN pip install flask

Expand Down
22 changes: 11 additions & 11 deletions flask_logging_server/logserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ def setup_logger(name, log_file, level=logging.INFO, when="midnight", interval=1

app = Flask(__name__)

def get_server_status():

@app.route('/clear_logs', methods=['POST'])
def clear_logs():
try:
return {
"status": "running",
"last_updated": datetime.now().isoformat()
}
with open(log_file_path, 'w') as f:
f.write('')
with open(simplified_log_file_path, 'w') as f:
f.write('')
return jsonify({"success": True})
except Exception as e:
exception_logger.error(f"Error getting server status: {e}")
return {
"status": "error",
"last_updated": None,
"error": str(e)
}
exception_logger.error(f"Error clearing logs: {e}")
return jsonify({"success": False, "error": str(e)}), 500

@app.route('/')
def landing_page():

Expand Down
6 changes: 6 additions & 0 deletions flask_logging_server/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Flask==2.3.2
gunicorn==20.1.0
pydicom==2.4.4
pynetdicom==2.0.2
numpy==1.23.5

43 changes: 43 additions & 0 deletions flask_logging_server/static/all_logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,46 @@ document.addEventListener("DOMContentLoaded", function () {
// Set interval to fetch logs every 5 seconds
setInterval(fetchLogs, 5000);
});


document.addEventListener("DOMContentLoaded", function () {
const refreshButton = document.getElementById('refresh-logs');
const clearButton = document.getElementById('clear-logs');

// Function to fetch logs
function fetchLogs() {
fetch('/logs/all')
.then(response => response.text())
.then(data => {
document.getElementById('logs').innerHTML = data;
})
.catch(error => console.error('Error fetching logs:', error));
}

// Refresh logs
if (refreshButton) {
refreshButton.addEventListener('click', function () {
fetchLogs();
});
}

// Clear logs
if (clearButton) {
clearButton.addEventListener('click', function () {
fetch('/clear_logs', { method: 'POST' })
.then(response => response.json())
.then(data => {
if (data.success) {
fetchLogs();
}
})
.catch(error => console.error('Error clearing logs:', error));
});
}

// Fetch logs initially
fetchLogs();

// Set interval to fetch logs every 5 seconds
setInterval(fetchLogs, 5000);
});
73 changes: 73 additions & 0 deletions flask_logging_server/static/logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,76 @@ document.addEventListener("DOMContentLoaded", function () {
// Set interval to fetch logs every 5 seconds
setInterval(fetchLogs, 5000);
});


document.addEventListener("DOMContentLoaded", function () {
const themeToggle = document.getElementById('theme-toggle');

if (themeToggle) {
// Load saved theme preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-mode');
themeToggle.innerHTML = '<span class="icon">☀️</span> Toggle Light Mode';
}

// Add event listener to the button
themeToggle.addEventListener('click', function () {
document.body.classList.toggle('dark-mode');
const isDarkMode = document.body.classList.contains('dark-mode');
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');
themeToggle.innerHTML = isDarkMode ? '<span class="icon">☀️</span> Toggle Light Mode' : '<span class="icon">🌙</span> Toggle Dark Mode';
});
}
});

document.addEventListener("DOMContentLoaded", function () {
// Session ID Search Functionality
const searchButton = document.getElementById('search-button');
const sessionIdInput = document.getElementById('session-id-search');

searchButton.addEventListener('click', function() {
const searchTerm = sessionIdInput.value.trim().toLowerCase();
const rows = document.querySelectorAll("#logs-table tbody tr");
let hasMatches = false;

rows.forEach(row => {
const sessionCell = row.querySelector("td:nth-child(1)"); // Adjust index if needed
if (sessionCell) {
const sessionId = sessionCell.textContent.toLowerCase();
if (sessionId.includes(searchTerm)) {
row.style.display = "";
row.classList.add('highlight-match');
hasMatches = true;
} else {
row.style.display = "none";
row.classList.remove('highlight-match');
}
}
});

if (!hasMatches && searchTerm) {
showToast('No matching sessions found!', 'warning');
}
});

// Clear Search
sessionIdInput.addEventListener('input', function(e) {
if (e.target.value === '') {
document.querySelectorAll("#logs-table tbody tr").forEach(row => {
row.style.display = "";
row.classList.remove('highlight-match');
});
}
});
});

// Toast Notification System
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;

document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
Loading