Source Code

;; dicom-handler - Clarity 4
;; DICOM medical imaging handler and metadata registry

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-IMAGE-NOT-FOUND (err u101))
(define-constant ERR-INVALID-FORMAT (err u102))
(define-constant ERR-ALREADY-EXISTS (err u103))

(define-map dicom-images uint
  {
    patient-id: (string-ascii 50),
    study-id: (string-ascii 100),
    series-id: (string-ascii 100),
    image-hash: (buff 64),
    modality: (string-ascii 10),
    upload-timestamp: uint,
    uploader: principal,
    is-verified: bool
  }
)

(define-map study-metadata (string-ascii 100)
  {
    patient-id: (string-ascii 50),
    study-description: (string-utf8 200),
    study-date: uint,
    referring-physician: (string-utf8 100),
    number-of-series: uint,
    number-of-images: uint
  }
)

(define-map series-metadata (string-ascii 100)
  {
    study-id: (string-ascii 100),
    series-description: (string-utf8 200),
    modality: (string-ascii 10),
    body-part: (string-ascii 50),
    number-of-images: uint
  }
)

(define-map image-access-logs uint
  {
    image-id: uint,
    accessor: principal,
    access-timestamp: uint,
    access-reason: (string-utf8 200),
    access-granted: bool
  }
)

(define-data-var image-counter uint u0)
(define-data-var access-log-counter uint u0)

(define-public (register-dicom-image
    (patient-id (string-ascii 50))
    (study-id (string-ascii 100))
    (series-id (string-ascii 100))
    (image-hash (buff 64))
    (modality (string-ascii 10)))
  (let ((image-id (+ (var-get image-counter) u1)))
    (map-set dicom-images image-id
      {
        patient-id: patient-id,
        study-id: study-id,
        series-id: series-id,
        image-hash: image-hash,
        modality: modality,
        upload-timestamp: stacks-block-time,
        uploader: tx-sender,
        is-verified: false
      })
    (update-series-count series-id)
    (var-set image-counter image-id)
    (ok image-id)))

(define-public (register-study-metadata
    (study-id (string-ascii 100))
    (patient-id (string-ascii 50))
    (study-description (string-utf8 200))
    (study-date uint)
    (referring-physician (string-utf8 100)))
  (ok (map-set study-metadata study-id
    {
      patient-id: patient-id,
      study-description: study-description,
      study-date: study-date,
      referring-physician: referring-physician,
      number-of-series: u0,
      number-of-images: u0
    })))

(define-public (register-series-metadata
    (series-id (string-ascii 100))
    (study-id (string-ascii 100))
    (series-description (string-utf8 200))
    (modality (string-ascii 10))
    (body-part (string-ascii 50)))
  (ok (map-set series-metadata series-id
    {
      study-id: study-id,
      series-description: series-description,
      modality: modality,
      body-part: body-part,
      number-of-images: u0
    })))

(define-public (verify-image (image-id uint))
  (let ((image (unwrap! (map-get? dicom-images image-id) ERR-IMAGE-NOT-FOUND)))
    (ok (map-set dicom-images image-id
      (merge image { is-verified: true })))))

(define-public (log-image-access
    (image-id uint)
    (access-reason (string-utf8 200))
    (access-granted bool))
  (let ((log-id (+ (var-get access-log-counter) u1)))
    (map-set image-access-logs log-id
      {
        image-id: image-id,
        accessor: tx-sender,
        access-timestamp: stacks-block-time,
        access-reason: access-reason,
        access-granted: access-granted
      })
    (var-set access-log-counter log-id)
    (ok log-id)))

(define-private (update-series-count (series-id (string-ascii 100)))
  (let ((series (default-to
                  { study-id: "", series-description: u"", modality: "", body-part: "", number-of-images: u0 }
                  (map-get? series-metadata series-id))))
    (map-set series-metadata series-id
      (merge series { number-of-images: (+ (get number-of-images series) u1) }))
    true))

(define-read-only (get-dicom-image (image-id uint))
  (ok (map-get? dicom-images image-id)))

(define-read-only (get-study-metadata (study-id (string-ascii 100)))
  (ok (map-get? study-metadata study-id)))

(define-read-only (get-series-metadata (series-id (string-ascii 100)))
  (ok (map-get? series-metadata series-id)))

(define-read-only (get-access-log (log-id uint))
  (ok (map-get? image-access-logs log-id)))

(define-read-only (validate-principal (p principal))
  (principal-destruct? p))

(define-read-only (format-image-id (image-id uint))
  (ok (int-to-ascii image-id)))

(define-read-only (parse-image-id (id-str (string-ascii 20)))
  (string-to-uint? id-str))

(define-read-only (get-bitcoin-block)
  (ok burn-block-height))

Functions (14)

FunctionAccessArgs
register-dicom-imagepublicpatient-id: (string-ascii 50
register-study-metadatapublicstudy-id: (string-ascii 100
register-series-metadatapublicseries-id: (string-ascii 100
verify-imagepublicimage-id: uint
log-image-accesspublicimage-id: uint, access-reason: (string-utf8 200
update-series-countprivateseries-id: (string-ascii 100
get-dicom-imageread-onlyimage-id: uint
get-study-metadataread-onlystudy-id: (string-ascii 100
get-series-metadataread-onlyseries-id: (string-ascii 100
get-access-logread-onlylog-id: uint
validate-principalread-onlyp: principal
format-image-idread-onlyimage-id: uint
parse-image-idread-onlyid-str: (string-ascii 20
get-bitcoin-blockread-only