@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix schema: <https://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix ex: <https://www.ghezelbaash.ir/#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix spdx: <http://spdx.org/rdf/terms#> .

ex:PhysicianEntityShape a sh:NodeShape ;
  sh:targetNode ex:saeed-ghezelbash ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:Person ] ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:IndividualPhysician ] ;
  sh:property [ sh:path schema:name ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:owns ; sh:hasValue ex:dr-saeed-ghezelbash-aesthetic-clinic-kermanshah ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:practicesAt ; sh:hasValue ex:dr-saeed-ghezelbash-aesthetic-clinic-kermanshah ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:worksFor ; sh:hasValue ex:dr-saeed-ghezelbash-aesthetic-clinic-kermanshah ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:affiliation ; sh:hasValue ex:dr-saeed-ghezelbash-aesthetic-clinic-kermanshah ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:medicalSpecialty ; sh:hasValue ex:medical-specialty-aesthetic-medicine ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:areaServed ; sh:hasValue ex:country-iran ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:areaServed ; sh:hasValue ex:country-iraq ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:availableService ; sh:minCount 120 ] ;
  sh:property [ sh:path schema:identifier ; sh:minCount 4 ] ;
  sh:property [ sh:path schema:sameAs ; sh:minCount 7 ] .

ex:ClinicAssetShape a sh:NodeShape ;
  sh:targetNode ex:dr-saeed-ghezelbash-aesthetic-clinic-kermanshah ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:MedicalClinic ] ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:PhysiciansOffice ] ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:LocalBusiness ] ;
  sh:property [ sh:path schema:owner ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:founder ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:employee ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:medicalSpecialty ; sh:hasValue ex:medical-specialty-aesthetic-medicine ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:areaServed ; sh:hasValue ex:country-iran ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:areaServed ; sh:hasValue ex:country-iraq ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:availableService ; sh:minCount 120 ] ;
  sh:property [ sh:path schema:identifier ; sh:minCount 5 ] ;
  sh:property [ sh:path schema:openingHoursSpecification ; sh:minCount 2 ] ;
  sh:property [ sh:path schema:priceRange ; sh:hasValue "$$$$" ] .

ex:WebPageDominanceShape a sh:NodeShape ;
  sh:targetNode ex:webpage ;
  sh:property [ sh:path schema:mainEntity ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:author ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:publisher ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ] .

ex:CanonicalDatasetRoleShape a sh:NodeShape ;
  sh:targetNode <https://www.ghezelbaash.ir/graph.jsonld#dataset> ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:Dataset ] ;
  sh:property [ sh:path schema:creator ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:publisher ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:distribution ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:isBasedOn ; sh:hasValue ex:project-github-source ; sh:minCount 1 ] ;
  # Each serialization may encode the same destination as an IRI or anyURI.
  # This exact value set permits one semantic target, without accepting a
  # language-tagged URL or forcing unlike RDF terms to share maxCount 1.
  sh:property [ sh:path schema:url ; sh:minCount 1 ;
    sh:in ( <https://www.ghezelbaash.ir/> "https://www.ghezelbaash.ir/"^^xsd:anyURI ) ] ;
  sh:property [ sh:path schema:sameAs ; sh:maxCount 0 ] .

ex:DataCatalogRoleShape a sh:NodeShape ;
  sh:targetNode ex:data-catalog ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:DataCatalog ] ;
  sh:property [ sh:path schema:url ; sh:hasValue <https://www.ghezelbaash.ir/dcat.ttl> ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:dataset ; sh:hasValue <https://www.ghezelbaash.ir/graph.jsonld#dataset> ; sh:minCount 1 ; sh:maxCount 1 ] .

ex:GitHubSourceRoleShape a sh:NodeShape ;
  sh:targetNode ex:project-github-source ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:SoftwareSourceCode ] ;
  sh:property [ sh:path schema:codeRepository ; sh:hasValue <https://github.com/medicaldoctor91/doctor-ghezelbaash> ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path schema:contentUrl ; sh:maxCount 0 ] ;
  sh:not [ sh:property [ sh:path rdf:type ; sh:hasValue schema:DataDownload ] ] .

ex:MedicalConditionTreatmentShape a sh:NodeShape ;
  sh:targetClass schema:MedicalCondition ;
  sh:property [ sh:path schema:possibleTreatment ; sh:class schema:MedicalTherapy ; sh:minCount 1 ] .

ex:FridayClosedShape a sh:NodeShape ;
  sh:targetNode ex:clinic-friday-closed ;
  sh:property [ sh:path schema:opens ; sh:hasValue "00:00" ] ;
  sh:property [ sh:path schema:closes ; sh:hasValue "00:00" ] .

ex:AvailableServiceProviderShape a sh:NodeShape ;
  sh:targetObjectsOf schema:availableService ;
  sh:property [ sh:path rdf:type ; sh:hasValue schema:Service ] ;
  sh:property [ sh:path schema:provider ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] .

ex:ImageLicenseShape a sh:NodeShape ;
  sh:targetClass schema:ImageObject ;
  sh:property [ sh:path schema:license ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:copyrightNotice ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:acquireLicensePage ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:creditText ; sh:minCount 1 ] .

# Dates retain their recorded precision. A date-only source never requires an
# invented time, while an existing timestamp must remain a real dateTime.
ex:CalendarValueShape a sh:NodeShape ;
  sh:targetObjectsOf schema:birthDate, schema:dateCreated, schema:dateModified,
    schema:datePublished, schema:lastReviewed, schema:startDate, schema:endDate,
    schema:observationDate, dct:issued, dct:modified ;
  sh:or ( [ sh:datatype xsd:date ] [ sh:datatype xsd:dateTime ] [ sh:datatype xsd:gYearMonth ] ) .

ex:UploadTimestampShape a sh:NodeShape ;
  sh:targetObjectsOf schema:uploadDate ;
  sh:datatype xsd:dateTime .

ex:ServiceCategoryShape a sh:NodeShape ;
  sh:targetSubjectsOf schema:category ;
  sh:class schema:Service ;
  sh:property [ sh:path schema:category ; sh:nodeKind sh:IRI ; sh:class schema:DefinedTerm ; sh:minCount 1 ] ;
  sh:property [ sh:path schema:provider ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:sparql [
    sh:message "A curated service category must also be a documented subject of that service." ;
    sh:select """
      PREFIX schema: <https://schema.org/>
      PREFIX dct: <http://purl.org/dc/terms/>
      SELECT $this ?value WHERE {
        $this schema:category ?value .
        FILTER NOT EXISTS { $this dct:subject ?value . }
      }
    """
  ] .

# This pair has one specific subject; pairwise equality alone would also pass
# when both questions and answers point to the same unrelated procedure.
ex:MelasmaAnswerSubjectShape a sh:NodeShape ;
  sh:targetNode ex:question-melasma-recurrence-and-multimodal-treatment,
    ex:answer-melasma-recurrence-and-multimodal-treatment ;
  sh:property [ sh:path schema:about ; sh:hasValue ex:procedure-melasma-treatment ; sh:minCount 1 ; sh:maxCount 1 ] .

ex:DcatDistributionShape a sh:NodeShape ;
  sh:targetClass dcat:Distribution ;
  sh:nodeKind sh:IRI ;
  sh:property [ sh:path dcat:downloadURL ; sh:nodeKind sh:IRI ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path dcat:mediaType ; sh:nodeKind sh:IRI ; sh:pattern "^https://www[.]iana[.]org/assignments/media-types/[^/]+/[^/]+$" ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path dcat:byteSize ; sh:datatype xsd:decimal ; sh:minInclusive 0 ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path spdx:checksum ; sh:class spdx:Checksum ; sh:minCount 1 ; sh:maxCount 1 ] .

ex:DistributionChecksumShape a sh:NodeShape ;
  sh:targetObjectsOf spdx:checksum ;
  sh:property [ sh:path spdx:algorithm ; sh:hasValue spdx:checksumAlgorithm_sha256 ; sh:minCount 1 ; sh:maxCount 1 ] ;
  sh:property [ sh:path spdx:checksumValue ; sh:datatype xsd:hexBinary ; sh:pattern "^[0-9a-fA-F]{64}$" ; sh:minCount 1 ; sh:maxCount 1 ] .

# An evidence assessment is our record about a source, not the source itself.
# Ordinary source nodes remain unconstrained by this first-party assessment
# contract even when both have the standard prov:Entity type.
ex:EvidenceAssessmentShape a sh:NodeShape ;
  sh:targetClass prov:Entity ;
  sh:or (
    [ sh:not [ sh:pattern "^https://www[.]ghezelbaash[.]ir/provenance[.]jsonld#assessment-" ] ]
    [
      sh:class schema:CreativeWork ;
      sh:property [ sh:path schema:creator ; sh:hasValue ex:saeed-ghezelbash ; sh:minCount 1 ; sh:maxCount 1 ] ;
      sh:property [ sh:path schema:isBasedOn ; sh:nodeKind sh:IRI ; sh:minCount 1 ; sh:maxCount 1 ] ;
      sh:property [ sh:path schema:about ; sh:equals schema:isBasedOn ; sh:minCount 1 ; sh:maxCount 1 ] ;
      sh:property [ sh:path schema:dateModified ; sh:maxCount 0 ] ;
      sh:property [ sh:path schema:additionalType ; sh:maxCount 0 ] ;
      sh:property [
        sh:path schema:additionalProperty ;
        sh:qualifiedValueShape [
          sh:property [ sh:path schema:propertyID ; sh:hasValue "Evidence role" ] ;
          sh:property [ sh:path schema:value ; sh:in ( "identity-reference" "medical-reference" "first-party-release-preservation" ) ; sh:minCount 1 ; sh:maxCount 1 ]
        ] ;
        sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1
      ]
    ]
  ) .

ex:EvidenceObservationDateShape a sh:NodeShape ;
  sh:targetClass schema:PropertyValue ;
  sh:or (
    [ sh:not [ sh:property [ sh:path schema:propertyID ; sh:hasValue "Evidence observation date" ] ] ]
    [ sh:property [ sh:path schema:value ; sh:datatype xsd:date ; sh:minCount 1 ; sh:maxCount 1 ] ]
  ) .
