code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="744.09448819"
   height="1052.3622047"
   id="svg2"
   version="1.1"
   inkscape:version="0.48.2 r9819"
   sodipodi:docname="tag_parents.svg">
  <defs
     id="defs4" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="1.979899"
     inkscape:cx="231.77811"
     inkscape:cy="465.57944"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:window-width="1300"
     inkscape:window-height="964"
     inkscape:window-x="1327"
     inkscape:window-y="858"
     inkscape:window-maximized="0" />
  <metadata
     id="metadata7">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect2985"
       width="374.87128"
       height="264.81741"
       x="15.099963"
       y="17.913582"
       inkscape:export-filename="C:\code\Hydrus\help\tag_parents_venn.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <path
       sodipodi:type="arc"
       style="color:#000000;fill:#00ff00;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2.64672065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="path2987"
       sodipodi:cx="194.95944"
       sodipodi:cy="191.71222"
       sodipodi:rx="147.48227"
       sodipodi:ry="147.48227"
       d="m 342.44171,191.71222 a 147.48227,147.48227 0 1 1 -294.964537,0 147.48227,147.48227 0 1 1 294.964537,0 z"
       transform="matrix(0.75581183,0,0,0.75549245,107.86342,5.4851574)" />
    <path
       sodipodi:type="arc"
       style="color:#000000;fill:#00ffff;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2.64672065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="path2989"
       sodipodi:cx="167.68532"
       sodipodi:cy="169.48886"
       sodipodi:rx="69.700523"
       sodipodi:ry="69.700523"
       d="m 237.38584,169.48886 a 69.700523,69.700523 0 1 1 -139.401045,0 69.700523,69.700523 0 1 1 139.401045,0 z"
       transform="matrix(0.75581183,0,0,0.75549245,93.738942,-7.8701796)" />
    <path
       sodipodi:type="arc"
       style="color:#000000;fill:#ff00ff;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2.64672065;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="path2991"
       sodipodi:cx="148.9975"
       sodipodi:cy="157.8721"
       sodipodi:rx="24.748737"
       sodipodi:ry="24.748737"
       d="m 173.74623,157.8721 a 24.748737,24.748737 0 1 1 -49.49747,0 24.748737,24.748737 0 1 1 49.49747,0 z"
       transform="matrix(0.75581183,0,0,0.75549245,95.265916,-9.3965047)" />
    <text
       xml:space="preserve"
       style="font-size:13.60173798px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="43.33976"
       y="107.22626"
       id="text3761"
       sodipodi:linespacing="125%"
       transform="scale(1.0002113,0.9997887)"><tspan
         sodipodi:role="line"
         id="tspan3763"
         x="43.33976"
         y="107.22626">ak-47</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:13.60173798px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="26.546631"
       y="144.62915"
       id="text3765"
       sodipodi:linespacing="125%"
       transform="scale(1.0002113,0.9997887)"><tspan
         sodipodi:role="line"
         id="tspan3767"
         x="26.546631"
         y="144.62915">assault rifle</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:13.60173798px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="35.706516"
       y="188.1386"
       id="text3769"
       sodipodi:linespacing="125%"
       transform="scale(1.0002113,0.9997887)"><tspan
         sodipodi:role="line"
         id="tspan3771"
         x="35.706516"
         y="188.1386">firearm</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:13.60173798px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="20.440039"
       y="256.0744"
       id="text3773"
       sodipodi:linespacing="125%"
       transform="scale(1.0002113,0.9997887)"><tspan
         sodipodi:role="line"
         id="tspan3775"
         x="20.440039"
         y="256.0744">all ak-47s are assault rifles</tspan><tspan
         sodipodi:role="line"
         x="20.440039"
         y="273.07657"
         id="tspan3777">all assault rifles are firearms</tspan></text>
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 202.15385,101.86147 -117.57673,0"
       id="path3779"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 187.64763,140.78277 -87.03732,0"
       id="path3781"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 175.43186,185.04619 -93.908681,0"
       id="path3783"
       inkscape:connector-curvature="0" />
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3785"
       width="413.79507"
       height="256.16187"
       x="16.270475"
       y="500.10254"
       inkscape:export-filename="C:\code\Hydrus\help\tag_parents_firearms.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="176.80841"
       y="516.01611"
       id="text3787"
       sodipodi:linespacing="125%"
       transform="scale(0.99990979,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3789"
         x="176.80841"
         y="516.01611">firearm</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="105.77372"
       y="578.70276"
       id="text3791"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3793"
         x="105.77372"
         y="578.70276">rifle</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="25.647068"
       y="616.71912"
       id="text3795"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3797"
         x="25.647068"
         y="616.71912">assault rifle</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="212.80423"
       y="575.19354"
       id="text3799"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3801"
         x="212.80423"
         y="575.19354">shotgun</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="308.13751"
       y="585.72113"
       id="text3803"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3805"
         x="308.13751"
         y="585.72113">handgun</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="23.307581"
       y="675.79053"
       id="text3815"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3817"
         x="23.307581"
         y="675.79053">ak-47</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="36.174656"
       y="703.86414"
       id="text3819"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3821"
         x="36.174656"
         y="703.86414">ak-74</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="76.530373"
       y="710.88257"
       id="text3823"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3825"
         x="76.530373"
         y="710.88257">m16</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="107.6714"
       y="698.70959"
       id="text3827"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3829"
         x="107.6714"
         y="698.70959">m4</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="191.16414"
       y="708.54297"
       id="text3831"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3833"
         x="191.16414"
         y="708.54297">remington model 870</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="116.88616"
       y="669.94189"
       id="text3835"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3837"
         x="116.88616"
         y="669.94189">ar-15</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="295.85522"
       y="675.79053"
       id="text3839"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3841"
         x="295.85522"
         y="675.79053">smith & wesson model 10</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="250.23575"
       y="598.5882"
       id="text3843"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3845"
         x="250.23575"
         y="598.5882">remington</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="338.55051"
       y="614.37964"
       id="text3847"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3849"
         x="338.55051"
         y="614.37964">smith and wesson</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="293.51581"
       y="636.60449"
       id="text3851"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3853"
         x="293.51581"
         y="636.60449">m1911</tspan></text>
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 54.885403,605.66122 51.463557,-21.642"
       id="path3855"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 123.89337,568.2264 66.66874,-47.37844"
       id="path3857"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 230.32944,564.13196 -38.59765,-43.284"
       id="path3859"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 328.57808,575.83035 194.65588,521.43289"
       id="path3861"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 310.44894,625.54846 325.65399,589.8684"
       id="path3863"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 351.38583,663.56817 c 0,-2.92456 -25.147,-72.52993 -25.147,-72.52993"
       id="path3865"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 374.19358,617.3596 -22.22291,45.62368"
       id="path3867"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 239.68643,695.73873 c -1.16957,-2.33967 -9.35699,-116.39886 -9.35699,-116.39886"
       id="path3869"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 276.52967,602.1517 -35.08872,94.17197"
       id="path3871"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 125.64778,620.86911 C 125.06306,618.52944 113.3667,582.84937 113.3667,582.84937"
       id="path3873"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 113.3667,689.3046 60.733545,621.45403"
       id="path3875"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 85.880569,699.83317 56.055026,624.37862"
       id="path3877"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 49.622089,692.22922 2.33926,-69.60535"
       id="path3879"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 36.756192,664.15312 49.622089,621.45403"
       id="path3881"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="134.43222"
       y="684.56348"
       id="text3883"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3885"
         x="134.43222"
         y="684.56348">winchester model 70</tspan></text>
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 184.71405,606.83105 117.46047,582.26446"
       id="path3887"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="75.360664"
       y="632.51044"
       id="text3889"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3891"
         x="75.360664"
         y="632.51044">semi-automatic rifle</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="153.14783"
       y="617.88885"
       id="text3893"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         id="tspan3895"
         x="153.14783"
         y="617.88885">bolt-action rifle</tspan></text>
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 125.06306,636.66193 2.33924,23.98168"
       id="path3897"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 187.0533,621.45403 -3.50893,52.05778"
       id="path3899"
       inkscape:connector-curvature="0" />
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:1.99999964;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3901"
       width="414.52017"
       height="155.0789"
       x="15.4862"
       y="304.98154"
       inkscape:export-filename="C:\code\Hydrus\help\tag_parents_got.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:10.45069885px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="170.32401"
       y="323.65546"
       id="text3903"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3905"
         x="170.32401"
         y="323.65546">series:game of thrones</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.45069885px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="26.047758"
       y="367.05563"
       id="text3907"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3909"
         x="26.047758"
         y="367.05563">house stark</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.45069885px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="233.73761"
       y="367.05563"
       id="text3911"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3913"
         x="233.73761"
         y="367.05563">house lannister</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.45069885px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="118.00807"
       y="367.05563"
       id="text3915"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3917"
         x="118.00807"
         y="367.05563">house baratheon</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.45069885px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="342.75174"
       y="366.01718"
       id="text3919"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3921"
         x="342.75174"
         y="366.01718">house targaryen</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="332.19489"
       y="414.56122"
       id="text3923"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3925"
         x="332.19489"
         y="414.56122">character:daenerys targaryen</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="19.009895"
       y="407.52341"
       id="text3927"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3929"
         x="19.009895"
         y="407.52341">character:eddard stark</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="39.53701"
       y="416.9072"
       id="text3931"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3933"
         x="39.53701"
         y="416.9072">character:sansa stark</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="55.372204"
       y="426.29102"
       id="text3935"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3937"
         x="55.372204"
         y="426.29102">character:bran stark</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="220.17557"
       y="408.10989"
       id="text3939"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3941"
         x="220.17557"
         y="408.10989">character:cersei lannister</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="242.46216"
       y="416.32071"
       id="text3943"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3945"
         x="242.46216"
         y="416.32071">character:jaime lannister</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:6.96713257px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="117.53999"
       y="407.52335"
       id="text3947"
       sodipodi:linespacing="125%"
       transform="scale(0.99969264,1.0003075)"><tspan
         sodipodi:role="line"
         id="tspan3949"
         x="117.53999"
         y="407.52335">character:robert baratheon</tspan></text>
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 54.768874,357.1951 C 59.459342,356.02176 220.6942,326.10165 220.6942,326.10165"
       id="path3951"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 161.47703,355.43509 59.80348,-28.74678"
       id="path3953"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 271.70305,357.78177 c -3.51785,-1.17333 -49.83623,-31.09346 -49.83623,-31.09346"
       id="path3955"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 384.2743,357.78177 223.62575,326.10165"
       id="path3957"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 41.870082,400.60863 55.355186,370.6885"
       id="path3959"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="cc" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 98.155705,409.99533 C 96.396789,407.06199 57.114114,370.10184 57.114114,370.10184"
       id="path3961"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 112.22711,417.62203 c 0,-2.93335 -51.595154,-48.10688 -51.595154,-48.10688"
       id="path3963"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 157.95918,399.43528 0,-28.16012"
       id="path3965"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 255.87272,399.43528 c 0,-4.10667 9.96725,-27.57345 9.96725,-27.57345"
       id="path3967"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="M 304.53633,407.06199 269.35782,371.86183"
       id="path3969"
       inkscape:connector-curvature="0" />
    <path
       style="fill:none;stroke:#000000;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 381.34275,403.54197 3.51786,-31.68014"
       id="path3971"
       inkscape:connector-curvature="0" />
    <text
       xml:space="preserve"
       style="font-size:9.28950977px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="224.76657"
       y="445.36945"
       id="text3973"
       sodipodi:linespacing="125%"
       transform="scale(0.99969262,1.0003075)"><tspan
         sodipodi:role="line"
         x="224.76657"
         y="445.36945"
         id="tspan3977">e.g. cersei lannister is a member of house lannister, which occurs in the series game of thrones</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10.4217844px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="217.65623"
       y="734.31873"
       id="text3979"
       sodipodi:linespacing="125%"
       transform="scale(0.99990977,1.0000902)"><tspan
         sodipodi:role="line"
         x="217.65623"
         y="734.31873"
         id="tspan3999">searching for any term should generally return all the files tagged with anything below</tspan><tspan
         sodipodi:role="line"
         x="217.65623"
         y="747.34595"
         id="tspan3993">note that a tag can have more than one parent</tspan></text>
  </g>
</svg>


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="744.09448819"
   height="1052.3622047"
   id="svg2"
   version="1.1"
   inkscape:version="0.48.2 r9819"
   sodipodi:docname="tag_siblings.svg">
  <defs
     id="defs4" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="1.979899"
     inkscape:cx="199.89168"
     inkscape:cy="212.99738"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:window-width="1038"
     inkscape:window-height="1015"
     inkscape:window-x="1989"
     inkscape:window-y="820"
     inkscape:window-maximized="0" />
  <metadata
     id="metadata7">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <path
       sodipodi:type="arc"
       style="color:#000000;fill:#ffff00;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="path2987"
       sodipodi:cx="319.71329"
       sodipodi:cy="175.04469"
       sodipodi:rx="107.58125"
       sodipodi:ry="107.58125"
       d="m 427.29454,175.04469 a 107.58125,107.58125 0 1 1 -215.16251,0 107.58125,107.58125 0 1 1 215.16251,0 z"
       transform="translate(-5.0507632,-36.870568)" />
    <path
       sodipodi:type="arc"
       style="color:#000000;fill:#0000ff;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="path2989"
       sodipodi:cx="124.24876"
       sodipodi:cy="148.27565"
       sodipodi:rx="85.862968"
       sodipodi:ry="85.862968"
       d="m 210.11172,148.27565 a 85.862968,85.862968 0 1 1 -171.725932,0 85.862968,85.862968 0 1 1 171.725932,0 z"
       transform="translate(59.599,-10.101525)" />
    <text
       xml:space="preserve"
       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="18.182747"
       y="39.17918"
       id="text3759"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3761"
         x="18.182747"
         y="39.17918"
         style="font-size:14px">character:rei ayanami, x</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="385.58438"
       y="40.696476"
       id="text3759-1"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3761-7"
         x="385.58438"
         y="40.696476"
         style="font-size:14px">character:ayanami rei, y</tspan></text>
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3784"
       width="553.5636"
       height="321.22849"
       x="1.0101523"
       y="0.79338306"
       inkscape:export-filename="C:\code\Hydrus\help\tag_siblings_venn_1.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:14px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="277.3613"
       y="286.66656"
       id="text3786"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3788"
         x="277.3613"
         y="286.66656">some files only have x, some only have y, some have both</tspan><tspan
         sodipodi:role="line"
         x="277.3613"
         y="305.54153"
         id="tspan3835">we can typically only get x∪y if we search for y after making y⊇x, rendering x pointless</tspan></text>
    <path
       style="color:#000000;fill:#ffff00;fill-opacity:0.24705882;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       d="M 314.65625 396.28125 C 282.09122 396.28125 252.9155 410.73365 233.1875 433.59375 C 219.22457 423.76775 202.21489 418 183.84375 418 C 136.42294 418 98 456.42294 98 503.84375 C 98 551.26456 136.42294 589.71875 183.84375 589.71875 C 202.21879 589.71875 219.22291 583.92355 233.1875 574.09375 C 252.9155 596.95385 282.09122 611.4375 314.65625 611.4375 C 374.07174 611.4375 422.25 563.25924 422.25 503.84375 C 422.25 444.42826 374.07174 396.28125 314.65625 396.28125 z "
       id="path2987-4" />
    <text
       xml:space="preserve"
       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="385.58435"
       y="406.3717"
       id="text3759-1-8"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3761-7-8"
         x="385.58435"
         y="406.3717"
         style="font-size:14px">character:ayanami rei, y</tspan></text>
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3784-2"
       width="553.5636"
       height="321.22849"
       x="1.0101514"
       y="366.46863"
       inkscape:export-filename="C:\code\Hydrus\help\tag_siblings_venn_2.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:14px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="256.71335"
       y="652.34174"
       id="text3786-4"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3788-5"
         x="256.71335"
         y="652.34174">all the files that used to have x or y now only have y</tspan><tspan
         sodipodi:role="line"
         x="256.71335"
         y="669.84174"
         id="tspan3833">via database magic, any queries for x return y</tspan></text>
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3837"
       width="262.13458"
       height="26.263969"
       x="13.417028"
       y="748.92218"
       inkscape:export-filename="C:\code\Hydrus\help\tag_siblings_usa.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <g
       id="g3957"
       transform="matrix(0.5,0,0,0.5,29.43228,358.76646)">
      <path
         inkscape:connector-curvature="0"
         id="path3866"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="19.47794"
       y="764.57959"
       id="text3962"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3964"
         x="19.47794"
         y="764.57959">murrikkka</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="88.865494"
       y="765.41455"
       id="text3966"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3968"
         x="88.865494"
         y="765.41455">united states</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="172.51604"
       y="765.46948"
       id="text3970"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3972"
         x="172.51604"
         y="765.46948">united states of america</tspan></text>
    <g
       transform="matrix(0.5,0,0,0.5,43.245636,403.47959)"
       id="g3957-2">
      <path
         inkscape:connector-curvature="0"
         id="path3866-1"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-6"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-8"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <g
       transform="matrix(0.5,0,0,0.5,126.89619,403.47959)"
       id="g3957-5">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3837-2"
       width="82.832489"
       height="62.124378"
       x="12.911961"
       y="790.84351"
       inkscape:export-filename="C:\code\Hydrus\help\tag_siblings_what_is_a_man.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="18.972874"
       y="806.50092"
       id="text3962-7"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3964-9"
         x="18.972874"
         y="806.50092">boy</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="18.968479"
       y="843.32935"
       id="text3966-5"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3968-4"
         x="18.968479"
         y="843.32935">man</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="66.862312"
       y="826.34912"
       id="text3970-3"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3972-1"
         x="66.862312"
         y="826.34912">male</tspan></text>
    <g
       transform="matrix(0.48296291,0.12940952,-0.12940952,0.48296291,114.52425,457.13986)"
       id="g3957-2-2">
      <path
         inkscape:connector-curvature="0"
         id="path3866-1-3"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-6-3"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-8-4"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <g
       transform="matrix(0.48296291,-0.12940952,0.12940952,0.48296291,-71.198483,495.61989)"
       id="g3957-5-1">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7-1"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61-3"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89-8"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <rect
       style="color:#000000;fill:none;stroke:#001d00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
       id="rect3837-2-7"
       width="347.49246"
       height="69.837944"
       x="118.97797"
       y="791.60114"
       inkscape:export-filename="C:\code\Hydrus\help\tag_siblings_yo_dawg.png"
       inkscape:export-xdpi="146.31"
       inkscape:export-ydpi="146.31" />
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="124.02876"
       y="812.30927"
       id="text3962-7-4"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3964-9-2"
         x="124.02876"
         y="812.30927">Calvin Broadus</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="333.09293"
       y="811.36224"
       id="text3966-5-7"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3968-4-7"
         x="333.09293"
         y="811.36224">snoop dogg</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="222.1778"
       y="811.36224"
       id="text3970-3-9"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan3972-1-3"
         x="222.1778"
         y="811.36224">snoop doggy dogg</tspan></text>
    <g
       transform="matrix(0.35355339,-0.35355339,0.35355339,0.35355339,128.03402,599.30536)"
       id="g3957-5-1-5">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7-1-0"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61-3-2"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89-8-8"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="416.97293"
       y="811.41498"
       id="text4134"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4136"
         x="416.97293"
         y="811.41498">snoop lion</tspan></text>
    <text
       xml:space="preserve"
       style="font-size:9px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
       x="335.1506"
       y="848.42224"
       id="text4138"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4140"
         x="335.1506"
         y="848.42224">dj snoopadelic</tspan></text>
    <g
       transform="matrix(0.5,0,0,0.5,368.973,450.32161)"
       id="g3957-5-6">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7-0"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61-2"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89-4"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <g
       transform="matrix(0.5,0,0,0.5,285.09299,450.32161)"
       id="g3957-5-8">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7-6"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61-5"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89-0"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
    <g
       transform="matrix(0.5,0,0,0.5,174.17783,450.32161)"
       id="g3957-5-9">
      <path
         inkscape:connector-curvature="0"
         id="path3866-7-00"
         d="m 40.47483,717.37932 35.197325,0"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-61-6"
         d="m 65.156091,706.09888 12.0982,12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
      <path
         inkscape:connector-curvature="0"
         id="path3868-1-89-1"
         d="m 65.025985,728.96696 12.0982,-12.09821"
         style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
    </g>
  </g>
</svg>

license: wtfpl


code:
		<title>tagging schema</title>
		<link href="hydrus.ico" rel="shortcut icon" />
		<link href="style.css" rel="stylesheet" type="text/css" />

This is for the public tag repository only! You can run your own tag repositories and do your own thing additionally or instead!

seriousness of schema

This is not all that important; it will just make searches easier if most of us can mostly agree to some guidelines.

We will never be able to easily and perfectly categorise every single image to everyone's satisfaction, so there is no point defining every possible rule for every possible situation. If you do something that doesn't fit, fixing mistakes is not difficult.

If you are still not confident, just lurk for a bit. See how other people have tagged the popular images and do more of that.

you can add pretty much whatever the hell you want, but don't screw around

The most important thing is: if your tag is your opinion, don't add it. 'beautiful' is an unhelpful tag because no one can agree on what it means. 'lingerie', 'blue eyes', and 'male' or 'female' are better since reasonable people can generally agree on what they mean. If someone thinks blue-eyed women are beautiful, they can search for that to find beautiful things.

You can start your own namespaces, categorisation systems, whatever. Just be aware that everyone else will see what you do.

If you are still unsure about the difference between objective and subjective, here's some more examples:

objective tags: (add these!)

firetruck

hors d'�uvre

high heels

character:jean-luc picard

person:okita anri

title:the tragical history of hamlet, prince of denmark

page:17

subjective tags: (don't add these!)

awesome

faggot level:super-gay

4 stars

this is boring, why did anyone upload this here

moran communist and ONE TERM PRESIDENT!!! SARAH PALIN 2012! FOR JESUS CHRIST

Of course, if you are tagging a picture of someone holding a sign that says 'beautiful', then go ahead and tag it that. Otherwise, please keep your opinions to yourself!

numbers

Numbers should be written '22', '1457 ce', and 'page:3', unless as part of an official title like 'ocean's eleven'. When the client parses numbers, it does so intelligently, so just use '1' where you might before have done '01' or '001'. I know it looks ugly sometimes to have '2 girls' or '1 cup', but the rules for writing numbers out in full are hazy for special cases.

(Numbers written as 123 are also readable by many different language-speakers, while 'tano', 'deux' and 'seven' are not.)

plurals

Nouns should generally be singular, not plural. 'chair' instead of 'chairs', 'cat' instead of 'cats', even if there are several of the thing in the image. If there really are many of the thing in the image, add a seperate 'multiple', 'comparison' or 'group' tag as apppropriate.

Ignore this when the thing is normally said in its plural (usually paired) form. Say 'blue eyes', not 'blue eye'; 'breasts', not 'breast', even if only one is pictured.

acronyms and synonyms

I prefer the full 'series:the lord of the rings' rather than 'lotr'. If you are an advanced user, please help out with tag siblings to help induce this.

character:anna (frozen)

I am not fond of putting a series name after a character because it looks unusual and is applied unreliably. It is done to separate same-named characters from each other (particularly when they have no canon surname), which is useful in places that search slowly or usually only deal in single-tag searches. I would prefer that namespaces say their namespace and nothing else. Some sites even say things like 'anna (disney)'. I don't really mind this stuff, but if you are adding a sibling to collapse these divergent tags into the 'proper' one, I'd prefer it all went to the simple and reliable 'character:anna'. Even better would be migrating towards a canon-ok unique name, like 'character:princess anna of arendelle'.

Including nicknames, like 'character:angela "mercy" ziegler' can be useful to establish uniqueness, but are not mandatory. 'character:harleen "harley quinn" frances quinzel' is probably overboard.

protip: rein in your spergitude

In developing hydrus, I have discovered two rules to happy tagging:

Don't try to be perfect.

Only add those tags you actually use in searches.

Tagging can be fun, but it can also be complicated, and the problem space is gigantic. There is always works to do, and it is easy to exhaust onesself or get lost in the bushes agonising over whether to use 'smile' or 'smiling' or 'smirk' or one of a million other split hairs. Problems are easy to fix, and this marathon will never finish, so do not try to sprint.

The sheer number of tags can also be overwhelming. Importing all the many tags from the boorus is totally fine, but if you are typing tags yourself, I suggest you try not to exhaustively tag everything in the image. You will save a lot of time and ultimately be much happier with your work. Anyone can see what is in an image just by looking at it--tags are primarily for finding things. Character, series and creator namespaces are a great place to start. After that, add what you are interested in, be that 'blue sky' or 'midriff'.

siblings and parents

Please do add siblings and parents! If it is something not obvious, please explain the relationship in your submitted reason. If it is something obvious (e.g. 'wings' is a parent of 'angel wings'), don't bother to put a reason in; I'll just approve it.

My general thoughts:

siblings

In general, the correctness of a thing is in how it would describe itself, or how its creator would describe it.

For shorthand, I will say 'a'->'b' to mean 'a' is replaced by 'b'.

For instance, japanese names are usually written surname first and western forename first, so let's go 'character:rei ayanami'->'character:ayanami rei' but leave 'person:emma watson' and other western names as they are.

Unless it is too obscure, replace the english version of a word with any more proper or original foreign name. But stick to something a westerner can read. Do things like 'series:the melancholy of haruhi suzumiya'->'series:haruhi suzumiya no yuuutsu' or 'series:princess mononoke'->'series:mononoke hime'. There's even an argument for things like 'series:harry potter and the sorcerer's stone'->'series:harry potter and the philosopher's stone'.

Accents and other unusual/unicode characters are great in tags if they reflect the official marketed name, and should be preferred, but make sure there's an ascii->unicode sibling to make it easy for most users to type. 'series:pokemon'->'series:pok�mon' is excellent, as it both reflects official branding and also helps anyone who can't easily produce '�' on their keyboard find it.

I don't care about popularity as much as accuracy. Given 'series:pretty cure' and 'series:precure', I would prefer 'series:pretty cure' because it is the 'full and proper' rendering, even though there are more instances of 'precure' on the boorus.

Do correct for common plural mistakes. ear->ears, women->female, and so on.

And feel free to replace any 'character (series)' booru artifacts as with the 'anna (frozen)' example above. 'character:anna (frozen)'->'character:princess anna of arendelle' is great wherever it makes sense.

But please do not go 'blah'->'character:blah' unless the name is popular and unique. No one is going to be confused by 'ayanami rei'->'character:ayanami rei', but going 'archer'->'character:archer' is going to create a lot of false positives. There's a similar problem with something like 'character:mercy'->'character:angela "mercy" ziegler'--although the left hand side is namespaced, there are still plenty of characters named 'mercy', so a sibling that converts all Mercys to Overwatch's Mercy is not appropriate.

If the character name is the same as the series name, make the unnamespaced version go to the series version. For instance, set 'harry potter'->'series:harry potter', since we don't know which one it is and 'character:harry potter' ⊂ 'series:harry potter'. (If a picture of just Hermione that for some reason was not providing namespace information had 'hermione granger' (the character) and 'harry potter' (the series), we wouldn't want to infer 'character:harry potter' by accident.

In general, swap out slang for proper terms. 'lube'->'lubricant', 'series:zelda'->'series:the legend of zelda'.

parents

Be shy about adding character:blah->series:whatever unless you are certain the character name is unique. 'character:harry potter'->'series:harry potter' seems fairly uncontroversial, for instance, but adding specific sub-series just to be completionist, such as 'character:miranda lawson->series:mass effect: redemption' is asking for trouble.

Remember that parents define a relationship that is always true. Don't add 'blonde hair' to 'character:elsa', even though it is true in most files--add 'animal ears' to 'cat ears', as cat ears are always animal ears, no matter what an artist can think up.

Also, tag parents are only worth something if the parent is useful for searching. Adding 'medium:blue background'->'blue' isn't useful since 'blue' itself is not very valuable, but 'fishnet stockings'->'stockings' is useful as both tags are common and used in searches by plenty of people.

You can create a complicated tree like the firearms diagram on my parents page, but if it only adds seven tags that you probably wouldn't ever use yourself, you probably wasted your time.


code:
		<title>running in wine</title>
		<link href="hydrus.ico" rel="shortcut icon" />
		<link href="style.css" rel="stylesheet" type="text/css" />

getting it to work on wine

Several Linux and OS X users have found success running hydrus with Wine. Here is a post from a Linux dude:

Some things I picked up on after extended use:

Wine is kinda retarded sometimes, do not try to close the window by pressing the red close button, while in fullscreen.

It will just "go through" it, and do whatever to whats behind it.

Flash do work, IF you download the internet explorer version, and install it through wine.

Hydrus is selfcontained, and portable. That means that one instance of hydrus do not know what another is doing. This is great if you want different installations for different things.

Some of the input fields behave a little wonky. Though that may just be standard Hydrus behavior.

Mostly everything else works fine. I was able to connect to the test server and view there. Only thing I need to test is the ability to host a server.

Installation process:

0. Get a standard Wine installation.

1. Download the latest hydrus .zip file.

2. Unpack it with your chosen zip file opener, in the chosen folder. Do not need to be in the wine folder.

3. Run it with wine, either though the file manager, or though the terminal.

4. For Flash support install the IE version through wine.

If you get the client running in Wine, please let me know how you get on!


code:
#!/usr/bin/env python3

# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.

import traceback, os
import lib.Hydrus.Exceptions

try:
	#### IMPORTING ####
	import locale, threading, argparser, wx

	from lib.Hydrus.Controller import ProcessStartingAction, ShutdownSiblingInstance
	from lib.Hydrus.Controller.Local import ClientController
	from lib.Hydrus.Controller.Remote import ServerController
	from lib.Hydrus.Data import Print, Logger, RestartProcess

	from lib.Test.Py2To3 import do_2to3_test

	import lib.Hydrus.Constants as HC
	import lib.Hydrus.Globals as HG

	try:
		from twisted.internet import reactor
	except Exception:
		HG.twisted_is_broke = True

	#### DEBUG ####
	try:
		locale.setlocale( locale.LC_ALL, '' )
	except Exception:
		pass

except ( lib.Hydrus.Exceptions.InsufficientCredentialsException, lib.Hydrus.Exceptions.ShutdownException ) as e:
	Print( e )

except Exception:
	print( traceback.format_exc() )

	if 'db_dir' in locals() and os.path.exists( db_dir ):
		dest_path = os.path.join( db_dir, 'crash.log' )
		with open( dest_path, 'w', encoding = 'utf-8' ) as f:
			f.write( traceback.format_exc() )

		print( 'Critical error occurred! Details written to crash.log!' )

#### FUNCTIONS ####
def run( program: str ):
	with Logger( db_dir, program ):
		try:
			if program == 'server':
				do_2to3_test()
				action = result.action
				action = ProcessStartingAction( db_dir, action )
				controller = ServerController( db_dir )

			if program == 'client':
				do_2to3_test( wx_error_display_callable = wx.SafeShowMessage )
				controller = ClientController( db_dir )

			if program == 'server' and action in ( 'stop', 'restart' ):
				ShutdownSiblingInstance( db_dir )

			if program == 'client' or ( program == 'server' and action in ( 'start', 'restart' ) ):
				Print( f'hydrus {program} started' )

				if not HG.twisted_is_broke:
					threading.Thread( target = reactor.run, name = 'twisted', kwargs = { 'installSignalHandlers' : 0 } ).start()

				controller.run()

		except Exception:
			Print( f'hydrus {program} failed' )
			Print( traceback.format_exc() )

		finally:
			HG.view_shutdown = True
			HG.model_shutdown = True

			try:
				controller.pubimmediate( 'wake_daemons' )
			except Exception:
				Print( traceback.format_exc() )

			reactor.callFromThread( reactor.stop )
			Print( f'hydrus {program} shut down' )

	HG.shutdown_complete = True

	if HG.restart:
		RestartProcess()

def SetupDirectories():
	if result.db_dir:
		db_dir = result.db_dir
	else:
		db_dir = HC.DEFAULT_DB_DIR

	if not lib.Hydrus.Paths.DirectoryIsWritable( db_dir ) or HC.RUNNING_FROM_OSX_APP:
		db_dir = HC.USERPATH_DB_DIR

	db_dir = lib.Hydrus.Paths.ConvertPortablePathToAbsPath( db_dir, HC.BASE_DIR )

	try:
		lib.Hydrus.Paths.MakeSureDirectoryExists( db_dir )
	except Exception:
		raise Exception( 'Could not ensure db path ' + db_dir + ' exists! Check the location is correct and that you have permission to write to it!' )

	if result.temp_dir is not None:
		if not os.path.exists( result.temp_dir ):
			raise Exception( 'The given temp directory, "{}", does not exist!'.format( result.temp_dir ) )

		if HC.PLATFORM_WINDOWS:
			os.environ[ 'TEMP' ] = result.temp_dir
			os.environ[ 'TMP' ] = result.temp_dir

		else:
			os.environ[ 'TMPDIR' ] = result.temp_dir

	return db_dir

def SetupArgParser():
	MainArgParser = argparser.ArgumentParser( description = 'hydrus network launcher', usage = '%(prog)s client|server [OPTIONS]')
	ArgSubParsers = MainArgParser.add_subparsers( dest = 'program' )

	ClientParser = ArgSubParsers.add_parser( 'client', help = 'client help' )

	ServerParser = ArgSubParsers.add_parser( 'server', help = 'server help' )
	ServerParser.add_argument( 'action', default = 'start', nargs = '?', choices = [ 'start', 'stop', 'restart' ], help = 'either start this server (default), or stop an existing server, or both' )

	MainArgParser.add_argument( '-d', '--db_dir', help = 'set an external db location' )
	MainArgParser.add_argument( '--no_daemons', action='store_true', help = 'run without background daemons' )
	MainArgParser.add_argument( '--no_wal', action='store_true', help = 'run without WAL db journalling' )
	MainArgParser.add_argument( '--no_db_temp_files', action='store_true', help = 'run the db entirely in memory' )
	MainArgParser.add_argument( '--temp_dir', help = 'override the program\'s temporary directory' )

	return argparser.parse_args()

try:
	#### EXECUTION ####
	result = SetupArgParser()
	db_dir = SetupDirectories()

	HG.no_daemons = result.no_daemons
	HG.no_wal = result.no_wal
	HG.no_db_temp_files = result.no_db_temp_files
	HG.lookups = PopulateLookups()

	run(result.program)

except ( lib.Hydrus.Exceptions.InsufficientCredentialsException, lib.Hydrus.Exceptions.ShutdownException ) as e:
	Print( e )

except Exception:
	print( traceback.format_exc() )

	if 'db_dir' in locals() and os.path.exists( db_dir ):
		dest_path = os.path.join( db_dir, 'crash.log' )
		with open( dest_path, 'w', encoding = 'utf-8' ) as f:
			f.write( traceback.format_exc() )

		print( 'Critical error occurred! Details written to crash.log!' )


code:
import os, re

from lib.Hydrus.Data import ContentUpdate, GetNow, Print, ShowException, ShowText, \
	SplitListIntoChunks, TimeHasPassed, ToHumanInt
from lib.Hydrus.Paths import ConvertPortablePathToAbsPath, DeletePath, MakeFileWritable, \
	MakeSureDirectoryExists, MirrorFile
from lib.Hydrus.Search import FileSearchContext
from lib.Hydrus.Serialisable import CreateFromSerialisableTuple, SerialisableBaseNamed
from lib.Hydrus.Tags import SplitTag
from lib.Hydrus.Threading import IsThreadShuttingDown

### CONSTANTS ###

import lib.Hydrus.Globals as HG
import lib.Hydrus.Constants as HC
import lib.Hydrus.Constants.Local as CC

from lib.Hydrus.Serialisable import SERIALISABLE_TYPE

MAX_PATH_LENGTH = 245 # bit of padding from 255 for .txt neigbouring and other surprises

### FUNCTIONS ###

def GenerateExportFilename( destination_directory, media, terms ):
	def clean_tag_text( t ):
		if HC.PLATFORM_WINDOWS:
			t = re.sub( r'\\', '_', t )

		else:
			t = re.sub( '/', '_', t )

		return t

	if len( destination_directory ) > ( MAX_PATH_LENGTH - 10 ):
		raise Exception( 'The destination directory is too long!' )

	filename = ''

	for ( term_type, term ) in terms:
		tags_manager = media.GetTagsManager()

		if term_type == 'string':
			filename += term

		elif term_type == 'namespace':
			tags = tags_manager.GetNamespaceSlice( ( term, ) )

			subtags = [ SplitTag( tag )[1] for tag in tags ]

			subtags.sort()

			filename += clean_tag_text( ', '.join( subtags ) )

		elif term_type == 'predicate':
			if term in ( 'tags', 'nn tags' ):
				current = tags_manager.GetCurrent()
				pending = tags_manager.GetPending()

				tags = list( current.union( pending ) )

				if term == 'nn tags':
					tags = [ tag for tag in tags if ':' not in tag ]

				else:
					tags = [ SplitTag( tag )[1] for tag in tags ]

				tags.sort()

				filename += clean_tag_text( ', '.join( tags ) )

			elif term == 'hash':
				hash = media.GetHash()

				filename += hash.hex()

		elif term_type == 'tag':
			tag = term

			( namespace, subtag ) = SplitTag( tag )

			if tags_manager.HasTag( subtag ):
				filename += clean_tag_text( subtag )

	if HC.PLATFORM_WINDOWS:
		# replace many consecutive backspace with single backspace
		filename = re.sub( r'\\+', r'\\', filename )

		# /, :, *, ?, ", <, >, |
		filename = re.sub( r'/|:|\*|\?|"|<|>|\|', '_', filename )

	else:
		filename = re.sub( '/', '_', filename )

	#

	mime = media.GetMime()

	ext = HC.mime_ext_lookup[ mime ]

	if filename.endswith( ext ):
		filename = filename[ : - len( ext ) ]

	example_dest_path = os.path.join( destination_directory, filename + ext )

	excess_chars = len( example_dest_path ) - MAX_PATH_LENGTH

	if excess_chars > 0:
		filename = filename[ : - excess_chars ]

	filename = filename + ext

	return filename

def GetExportPath():
	portable_path = HG.client_controller.options[ 'export_path' ]

	if portable_path is None:
		path = os.path.join( os.path.expanduser( '~' ), 'hydrus_export' )

		MakeSureDirectoryExists( path )

	else:
		path = ConvertPortablePathToAbsPath( portable_path )

	return path

def ParseExportPhrase( phrase ):
	try:
		terms = [ ( 'string', phrase ) ]

		new_terms = []

		for ( term_type, term ) in terms:
			if term_type == 'string':
				while '[' in term:
					( pre, term ) = term.split( '[', 1 )

					( namespace, term ) = term.split( ']', 1 )

					new_terms.append( ( 'string', pre ) )
					new_terms.append( ( 'namespace', namespace ) )

			new_terms.append( ( term_type, term ) )

		terms = new_terms

		new_terms = []

		for ( term_type, term ) in terms:
			if term_type == 'string':
				while '{' in term:
					( pre, term ) = term.split( '{', 1 )

					( predicate, term ) = term.split( '}', 1 )

					new_terms.append( ( 'string', pre ) )
					new_terms.append( ( 'predicate', predicate ) )

			new_terms.append( ( term_type, term ) )

		terms = new_terms

		new_terms = []

		for ( term_type, term ) in terms:
			if term_type == 'string':
				while '(' in term:
					( pre, term ) = term.split( '(', 1 )

					( tag, term ) = term.split( ')', 1 )

					new_terms.append( ( 'string', pre ) )
					new_terms.append( ( 'tag', tag ) )

			new_terms.append( ( term_type, term ) )

		terms = new_terms

	except Exception as e:
		raise Exception( 'Could not parse that phrase: ' + str( e ) )

	return terms

class ExportFolder( SerialisableBaseNamed ):
	SERIALISABLE_TYPE_NUM = HG.SerialisableType["EXPORT_FOLDER"]
	SERIALISABLE_NAME = 'Export Folder'
	SERIALISABLE_VERSION = 4

	def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, delete_from_client_after_export = False, file_search_context = None, run_regularly = True, period = 3600, phrase = None, last_checked = 0, paused = False, run_now = False ):
		SerialisableBaseNamed.__init__( self, name )

		if export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
			delete_from_client_after_export = False

		if file_search_context is None:
			file_search_context = FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )

		if phrase is None:
			phrase = HG.client_controller.new_options.GetString( 'export_phrase' )

		self._path = path
		self._export_type = export_type
		self._delete_from_client_after_export = delete_from_client_after_export
		self._file_search_context = file_search_context
		self._run_regularly = run_regularly
		self._period = period
		self._phrase = phrase
		self._last_checked = last_checked
		self._paused = paused and not run_now
		self._run_now = run_now

	def _GetSerialisableInfo( self ):
		serialisable_file_search_context = self._file_search_context.GetSerialisableTuple()

		return ( self._path, self._export_type, self._delete_from_client_after_export, serialisable_file_search_context, self._run_regularly, self._period, self._phrase, self._last_checked, self._paused, self._run_now )

	def _InitialiseFromSerialisableInfo( self, serialisable_info ):
		( self._path, self._export_type, self._delete_from_client_after_export, serialisable_file_search_context, self._run_regularly, self._period, self._phrase, self._last_checked, self._paused, self._run_now ) = serialisable_info
		if self._export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
			self._delete_from_client_after_export = False

		self._file_search_context = CreateFromSerialisableTuple( serialisable_file_search_context )

	def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
		if version == 1:
			( export_type, serialisable_file_search_context, period, phrase, last_checked ) = old_serialisable_info

			path = self._name

			new_serialisable_info = ( path, export_type, serialisable_file_search_context, period, phrase, last_checked )

			return ( 2, new_serialisable_info )

		if version == 2:
			( path, export_type, serialisable_file_search_context, period, phrase, last_checked ) = old_serialisable_info

			delete_from_client_after_export = False

			new_serialisable_info = ( path, export_type, delete_from_client_after_export, serialisable_file_search_context, period, phrase, last_checked )

			return ( 3, new_serialisable_info )

		if version == 3:
			( path, export_type, delete_from_client_after_export, serialisable_file_search_context, period, phrase, last_checked ) = old_serialisable_info

			run_regularly = True
			paused = False
			run_now = False

			new_serialisable_info = ( path, export_type, delete_from_client_after_export, serialisable_file_search_context, run_regularly, period, phrase, last_checked, paused, run_now )

			return ( 4, new_serialisable_info )

	def DoWork( self ):
		try:
			regular_run_due = self._run_regularly and TimeHasPassed( self._last_checked + self._period )

			good_to_go = ( regular_run_due or self._run_now ) and not self._paused

			if not good_to_go:
				return

			if self._path != '' and os.path.exists( self._path ) and os.path.isdir( self._path ):
				query_hash_ids = HG.client_controller.Read( 'file_query_ids', self._file_search_context )

				media_results = []

				i = 0

				base = 256

				while i < len( query_hash_ids ):
					if HC.options[ 'pause_export_folders_sync' ] or IsThreadShuttingDown():
						return

					if i == 0:
						( last_i, i ) = ( 0, base )
					else:
						( last_i, i ) = ( i, i + base )

					sub_query_hash_ids = query_hash_ids[ last_i : i ]

					more_media_results = HG.client_controller.Read( 'media_results_from_ids', sub_query_hash_ids )

					media_results.extend( more_media_results )

				media_results.sort( key = lambda mr: mr.GetHashId() )

				#

				terms = ParseExportPhrase( self._phrase )

				previous_paths = set()

				for ( root, dirnames, filenames ) in os.walk( self._path ):
					previous_paths.update( ( os.path.join( root, filename ) for filename in filenames ) )

				sync_paths = set()

				client_files_manager = HG.client_controller.client_files_manager

				num_copied = 0

				for media_result in media_results:
					if HC.options[ 'pause_export_folders_sync' ] or IsThreadShuttingDown():
						return

					hash = media_result.GetHash()
					mime = media_result.GetMime()
					# size = media_result.GetSize()

					source_path = client_files_manager.GetFilePath( hash, mime )

					filename = GenerateExportFilename( self._path, media_result, terms )

					dest_path = os.path.normpath( os.path.join( self._path, filename ) )

					if not dest_path.startswith( self._path ):
						raise Exception( 'It seems a destination path for export folder "{}" was above the main export directory! The file was "{}" and its destination path was "{}".'.format( self._path, hash.hex(), dest_path ) )

					dest_path_dir = os.path.dirname( dest_path )

					MakeSureDirectoryExists( dest_path_dir )

					if dest_path not in sync_paths:
						copied = MirrorFile( source_path, dest_path )

						if copied:
							num_copied += 1

							MakeFileWritable( dest_path )

					sync_paths.add( dest_path )

				if num_copied > 0:
					Print( 'Export folder ' + self._name + ' exported ' + ToHumanInt( num_copied ) + ' files.' )

				if self._export_type == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
					deletee_paths = previous_paths.difference( sync_paths )

					for deletee_path in deletee_paths:
						DeletePath( deletee_path )

					deletee_dirs = set()

					for ( root, dirnames, filenames ) in os.walk( self._path, topdown = False ):
						if root == self._path:
							continue

						no_files = len( filenames ) == 0

						useful_dirnames = [ dirname for dirname in dirnames if os.path.join( root, dirname ) not in deletee_dirs ]

						no_useful_dirs = len( useful_dirnames ) == 0

						if no_useful_dirs and no_files:
							deletee_dirs.add( root )

					for deletee_dir in deletee_dirs:
						if os.path.exists( deletee_dir ):
							DeletePath( deletee_dir )

					if len( deletee_paths ) > 0:
						Print( 'Export folder {} deleted {} files and {} folders.'.format( self._name, ToHumanInt( len( deletee_paths ) ), ToHumanInt( len( deletee_dirs ) ) ) )

				if self._delete_from_client_after_export:
					deletee_hashes = { media_result.GetHash() for media_result in media_results }

					chunks_of_hashes = SplitListIntoChunks( deletee_hashes, 64 )

					reason = 'Deleted after export to Export Folder "{}".'.format( self._path )

					content_updates = [ ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, chunk_of_hashes, reason = reason ) for chunk_of_hashes in chunks_of_hashes ]

					for content_update in content_updates:
						HG.client_controller.WriteSynchronous( 'content_updates', { CC.LOCAL_FILE_SERVICE_KEY : [ content_update ] } )

			self._last_checked = GetNow()
			self._run_now = False

		except Exception as e:
			self.paused = True

			ShowText( 'The export folder "' + self._name + '" encountered an error! The error will follow! All export folders have now been paused. Please check the folder\'s settings and maybe report to hydrus dev if the error is complicated!' )

			ShowException( e )

		HG.client_controller.WriteSynchronous( 'serialisable', self )

	def RunNow( self ):
		self._paused = False
		self._run_now = True

	def ToTuple( self ):
		return ( self._name, self._path, self._export_type, self._delete_from_client_after_export, self._file_search_context, self._period, self._phrase )

HG.LookupTables[ "SERIALISABLE_TYPES_TO_OBJECT_TYPES" ][ HG.SerialisableType["EXPORT_FOLDER"] ] = ExportFolder