diff --git a/.github/workflows/typing-check.yml b/.github/workflows/typing-check.yml index 6d22bf3b7..940fb9fc4 100644 --- a/.github/workflows/typing-check.yml +++ b/.github/workflows/typing-check.yml @@ -30,7 +30,7 @@ jobs: # working-directory: ./${{ matrix.package-name }} run: | python -m pip install --upgrade pip - python -m pip install mypy ruff types-PyYAML + python -m pip install mypy ruff types-PyYAML types-pytz - name: Typing check with mypy # working-directory: ./${{ matrix.package-name }} diff --git a/geos-trame/pyproject.toml b/geos-trame/pyproject.toml index 4e98f6ce1..a98132b64 100644 --- a/geos-trame/pyproject.toml +++ b/geos-trame/pyproject.toml @@ -8,7 +8,8 @@ version = "1.0.0" description = "Geos Simulation Modeler" authors = [{name = "GEOS Contributors" }] maintainers = [{name = "Alexandre Benedicto", email = "alexandre.benedicto@external.totalenergies.com" }, - {name = "Paloma Martinez", email = "paloma.martinez@external.totalenergies.com" }] + {name = "Paloma Martinez", email = "paloma.martinez@external.totalenergies.com" }, + {name = "Jacques Franc", email= "jacques.franc@external.totalenergies.com" }] license = {text = "Apache-2.0"} classifiers = [ "Development Status :: 4 - Beta", @@ -40,7 +41,6 @@ dependencies = [ "matplotlib==3.9.4", "trame-matplotlib==2.0.3", "trame-components==2.4.2", - "trame-gantt==0.1.5", "mpld3<0.5.11", "xsdata==24.5", "xsdata-pydantic[lxml]==24.5", @@ -48,6 +48,7 @@ dependencies = [ "dpath==2.2.0", "colorcet==3.1.0", "funcy==2.0", + "pytz==2025.2", "typing_inspect==0.9.0", "typing_extensions>=4.12", "PyYAML", diff --git a/geos-trame/src/geos/trame/app/core.py b/geos-trame/src/geos/trame/app/core.py index 0a8f40973..3baf7d387 100644 --- a/geos-trame/src/geos/trame/app/core.py +++ b/geos-trame/src/geos/trame/app/core.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Lionel Untereiner +# SPDX-FileContributor: Lionel Untereiner, Jacques Franc from trame.ui.vuetify3 import VAppLayout from trame.decorators import TrameApp @@ -111,7 +111,7 @@ def deck_ui( self ) -> None: cols=10, order=2, ): - self.timelineEditor = TimelineEditor( source=self.tree, classes="ma-2", style="height: 40%" ) + self.timelineEditor = TimelineEditor( source=self.tree, classes="ma-2", style="height: 60%" ) with vuetify.VRow( classes="mb-6 fill-height", ): with vuetify.VCol( @@ -121,7 +121,7 @@ def deck_ui( self ) -> None: self.deckEditor = DeckEditor( source=self.tree, classes="ma-2", - style="flex: 1; height: 100%;", + style="height: 100%;", ) with vuetify.VCol( @@ -133,13 +133,13 @@ def deck_ui( self ) -> None: region_viewer=self.region_viewer, well_viewer=self.well_viewer, classes="ma-2", - style="flex: 1; height: 60%; width: 100%;", + style="height: 60%; width: 100%;", ) self.deckPlotting = DeckPlotting( source=self.tree, classes="ma-2", - style="flex: 1; height: 40%; width: 100%;", + style="height: 40%; width: 100%;", ) def build_ui( self ) -> None: diff --git a/geos-trame/src/geos/trame/app/deck/tree.py b/geos-trame/src/geos/trame/app/deck/tree.py index 56eb3c287..06b1944ec 100644 --- a/geos-trame/src/geos/trame/app/deck/tree.py +++ b/geos-trame/src/geos/trame/app/deck/tree.py @@ -4,6 +4,8 @@ import os from collections import defaultdict from typing import Any +from datetime import timedelta, datetime + import dpath import funcy from pydantic import BaseModel @@ -11,7 +13,7 @@ from xsdata.formats.dataclass.parsers.config import ParserConfig from xsdata.formats.dataclass.serializers.config import SerializerConfig from xsdata.utils import text -from xsdata_pydantic.bindings import DictDecoder, XmlContext, XmlSerializer +from xsdata_pydantic.bindings import DictDecoder, XmlContext, XmlSerializer, DictEncoder from trame_server.controller import Controller from trame_simput import get_simput_manager @@ -21,6 +23,12 @@ from geos.trame.schema_generated.schema_mod import Problem, Included, File, Functions from geos.trame.app.utils.file_utils import normalize_path, format_xml +import logging + +date_fmt = "%Y-%m-%d" +logger = logging.getLogger( "tree" ) +logger.setLevel( logging.ERROR ) + class DeckTree( object ): """A tree that represents a deck file along with all the available blocks and parameters.""" @@ -36,6 +44,8 @@ def __init__( self, sm_id: str | None = None, ctrl: Controller = None, **kwargs: self.input_has_errors = False self._sm_id = sm_id self._ctrl = ctrl + self.world_origin_time = datetime( 1924, 3, 28 ).strftime( date_fmt ) # Total start date !! + self.registered_targets: dict = {} def set_input_file( self, input_filename: str ) -> None: """Set a new input file. @@ -79,6 +89,12 @@ def update( self, path: str, key: str, value: Any ) -> None: assert self.input_file is not None and self.input_file.pb_dict is not None self.input_file.pb_dict = funcy.set_in( self.input_file.pb_dict, new_path, value ) + def drop( self, path: str ) -> None: + """Remove in the tree.""" + new_path = [ int( x ) if x.isdigit() else x for x in path.split( "/" ) ] + assert self.input_file is not None and self.input_file.pb_dict is not None + self.input_file.pb_dict = funcy.del_in( self.input_file.pb_dict, new_path ) + def _search( self, path: str ) -> list | None: new_path = path.split( "/" ) if self.input_file is None: @@ -99,6 +115,17 @@ def decode( self, path: str ) -> BaseModel | None: decoder = DictDecoder( context=context, config=ParserConfig() ) return decoder.decode( data[ 0 ] ) + @staticmethod + def encode_data( data: BaseModel ) -> dict: + """Convert a data to a xml serializable file.""" + context = XmlContext( + element_name_generator=text.pascal_case, + attribute_name_generator=text.camel_case, + ) + encoder = DictEncoder( context=context, config=SerializerConfig( indent=" " ) ) + nodeDict: dict = encoder.encode( data ) + return nodeDict + @staticmethod def decode_data( data: dict ) -> Problem: """Convert a data to a xml serializable file.""" @@ -133,12 +160,28 @@ def timeline( self ) -> list[ dict ] | None: timeline = [] # list root events global_id = 0 - for e in self.input_file.problem.events[ 0 ].periodic_event: + all_periodic_events = self.input_file.problem.events[ 0 ].periodic_event + max_time = self.input_file.problem.events[ 0 ].max_time + for e in all_periodic_events: + self.registered_targets[ e.target.split( '/' )[ -1 ] ] = e.target + e.end_time = max_time if float( e.end_time ) > float( max_time ) else e.end_time + #note here float conversion is used to correctly interpret scientific format item: dict[ str, str | int ] = { - "id": global_id, - "summary": e.name, - "start_date": e.begin_time, + "id": + global_id, + "name": + e.name, + "start": ( datetime.strptime( self.world_origin_time, date_fmt ) + + timedelta( seconds=float( e.begin_time ) ) ).strftime( date_fmt ), + "end": ( datetime.strptime( self.world_origin_time, date_fmt ) + + timedelta( seconds=float( e.end_time ) ) ).strftime( date_fmt ), + "duration": + str( timedelta( seconds=( float( e.end_time ) - float( e.begin_time ) ) ).days ), + "category": + e.target.split( '/' )[ -1 ], } + if ( int( float( e.time_frequency ) ) > 0 ): + item[ "freq" ] = timedelta( seconds=float( e.time_frequency ) ).days timeline.append( item ) global_id = global_id + 1 diff --git a/geos-trame/src/geos/trame/app/gantt_chart/__init__.py b/geos-trame/src/geos/trame/app/gantt_chart/__init__.py new file mode 100644 index 000000000..5f383b7c5 --- /dev/null +++ b/geos-trame/src/geos/trame/app/gantt_chart/__init__.py @@ -0,0 +1,7 @@ +from trame_client.utils.version import get_version + +__version__ = get_version( "gantt-chart" ) + +__all__ = [ + "__version__", +] diff --git a/geos-trame/src/geos/trame/app/gantt_chart/module/__init__.py b/geos-trame/src/geos/trame/app/gantt_chart/module/__init__.py new file mode 100644 index 000000000..3818bbefd --- /dev/null +++ b/geos-trame/src/geos/trame/app/gantt_chart/module/__init__.py @@ -0,0 +1,25 @@ +from pathlib import Path + +# Compute local path to serve +serve_path = str( Path( __file__ ).with_name( "serve" ).resolve() ) + +# Serve directory for JS/CSS files +serve = { "__gantt_chart": serve_path } + +# List of JS files to load (usually from the serve path above) +scripts = [ "__gantt_chart/gantt-chart.umd.js" ] + +# List of CSS files to load (usually from the serve path above) +# styles = ["__geos_trame/style.css"] + +# List of Vue plugins to install/load +vue_use = [ "GanttLib" ] + +# Uncomment to add entries to the shared state +# state = {} + + +# Optional if you want to execute custom initialization at module load +def setup( app, **kwargs ): # noqa + """Method called at initialization with possibly some custom keyword arguments.""" + pass diff --git a/geos-trame/src/geos/trame/app/gantt_chart/module/serve/gantt-chart.umd.js b/geos-trame/src/geos/trame/app/gantt_chart/module/serve/gantt-chart.umd.js new file mode 100644 index 000000000..796907960 --- /dev/null +++ b/geos-trame/src/geos/trame/app/gantt_chart/module/serve/gantt-chart.umd.js @@ -0,0 +1 @@ +(function(h,wt){typeof exports=="object"&&typeof module<"u"?module.exports=wt(require("vue")):typeof define=="function"&&define.amd?define(["vue"],wt):(h=typeof globalThis<"u"?globalThis:h||self,h.GanttLib=wt(h.Vue))})(this,function(h){"use strict";function wt(t,e){return t==null||e==null?NaN:te?1:t>=e?0:NaN}function Br(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function Ie(t){let e,n,r;t.length!==2?(e=wt,n=(u,s)=>wt(t(u),s),r=(u,s)=>t(u)-s):(e=t===wt||t===Br?t:Xr,n=t,r=t);function i(u,s,l=0,c=u.length){if(l>>1;n(u[d],s)<0?l=d+1:c=d}while(l>>1;n(u[d],s)<=0?l=d+1:c=d}while(ll&&r(u[d-1],s)>-r(u[d],s)?d-1:d}return{left:i,center:o,right:a}}function Xr(){return 0}function Gr(t){return t===null?NaN:+t}const Zr=Ie(wt).right;Ie(Gr).center;class Mn extends Map{constructor(e,n=Kr){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),e!=null)for(const[r,i]of e)this.set(r,i)}get(e){return super.get(Dn(this,e))}has(e){return super.has(Dn(this,e))}set(e,n){return super.set(Qr(this,e),n)}delete(e){return super.delete(Jr(this,e))}}function Dn({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):n}function Qr({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):(t.set(r,n),n)}function Jr({_intern:t,_key:e},n){const r=e(n);return t.has(r)&&(n=t.get(r),t.delete(r)),n}function Kr(t){return t!==null&&typeof t=="object"?t.valueOf():t}const jr=Math.sqrt(50),ti=Math.sqrt(10),ei=Math.sqrt(2);function Cn(t,e,n){const r=(e-t)/Math.max(0,n),i=Math.floor(Math.log10(r)),a=r/Math.pow(10,i),o=a>=jr?10:a>=ti?5:a>=ei?2:1;let u,s,l;return i<0?(l=Math.pow(10,-i)/o,u=Math.round(t*l),s=Math.round(e*l),u/le&&--s,l=-l):(l=Math.pow(10,i)*o,u=Math.round(t/l),s=Math.round(e/l),u*le&&--s),s+t(e)}function ui(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),n=>+t(n)+e}function si(){return!this.__axis}function En(t,e){var n=[],r=null,i=null,a=6,o=6,u=3,s=typeof window<"u"&&window.devicePixelRatio>1?0:.5,l=t===Ye||t===Jt?-1:1,c=t===Jt||t===He?"x":"y",d=t===Ye||t===Le?ii:ai;function f(g){var C=r??(e.ticks?e.ticks.apply(e,n):e.domain()),N=i??(e.tickFormat?e.tickFormat.apply(e,n):ri),F=Math.max(a,0)+u,b=e.range(),I=+b[0]+s,$=+b[b.length-1]+s,v=(e.bandwidth?ui:oi)(e.copy(),s),k=g.selection?g.selection():g,_=k.selectAll(".domain").data([null]),T=k.selectAll(".tick").data(C,e).order(),V=T.exit(),Y=T.enter().append("g").attr("class","tick"),W=T.select("line"),H=T.select("text");_=_.merge(_.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),T=T.merge(Y),W=W.merge(Y.append("line").attr("stroke","currentColor").attr(c+"2",l*a)),H=H.merge(Y.append("text").attr("fill","currentColor").attr(c,l*F).attr("dy",t===Ye?"0em":t===Le?"0.71em":"0.32em")),g!==k&&(_=_.transition(g),T=T.transition(g),W=W.transition(g),H=H.transition(g),V=V.transition(g).attr("opacity",Sn).attr("transform",function(Z){return isFinite(Z=v(Z))?d(Z+s):this.getAttribute("transform")}),Y.attr("opacity",Sn).attr("transform",function(Z){var ot=this.parentNode.__axis;return d((ot&&isFinite(ot=ot(Z))?ot:v(Z))+s)})),V.remove(),_.attr("d",t===Jt||t===He?o?"M"+l*o+","+I+"H"+s+"V"+$+"H"+l*o:"M"+s+","+I+"V"+$:o?"M"+I+","+l*o+"V"+s+"H"+$+"V"+l*o:"M"+I+","+s+"H"+$),T.attr("opacity",1).attr("transform",function(Z){return d(v(Z)+s)}),W.attr(c+"2",l*a),H.attr(c,l*F).text(N),k.filter(si).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===He?"start":t===Jt?"end":"middle"),k.each(function(){this.__axis=v})}return f.scale=function(g){return arguments.length?(e=g,f):e},f.ticks=function(){return n=Array.from(arguments),f},f.tickArguments=function(g){return arguments.length?(n=g==null?[]:Array.from(g),f):n.slice()},f.tickValues=function(g){return arguments.length?(r=g==null?null:Array.from(g),f):r&&r.slice()},f.tickFormat=function(g){return arguments.length?(i=g,f):i},f.tickSize=function(g){return arguments.length?(a=o=+g,f):a},f.tickSizeInner=function(g){return arguments.length?(a=+g,f):a},f.tickSizeOuter=function(g){return arguments.length?(o=+g,f):o},f.tickPadding=function(g){return arguments.length?(u=+g,f):u},f.offset=function(g){return arguments.length?(s=+g,f):s},f}function li(t){return En(Le,t)}function ci(t){return En(Jt,t)}var fi={value:()=>{}};function Re(){for(var t=0,e=arguments.length,n={},r;t=0&&(r=n.slice(i+1),n=n.slice(0,i)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:r}})}he.prototype=Re.prototype={constructor:he,on:function(t,e){var n=this._,r=hi(t+"",n),i,a=-1,o=r.length;if(arguments.length<2){for(;++a0)for(var n=new Array(i),r=0,i,a;r=0&&(e=t.slice(0,n))!=="xmlns"&&(t=t.slice(n+1)),Un.hasOwnProperty(e)?{space:Un[e],local:t}:t}function gi(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===Oe&&e.documentElement.namespaceURI===Oe?e.createElement(t):e.createElementNS(n,t)}}function mi(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Fn(t){var e=de(t);return(e.local?mi:gi)(e)}function pi(){}function qe(t){return t==null?pi:function(){return this.querySelector(t)}}function yi(t){typeof t!="function"&&(t=qe(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i=$&&($=I+1);!(k=F[$])&&++$=0;)(o=r[i])&&(a&&o.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(o,a),a=o);return this}function qi(t){t||(t=Pi);function e(d,f){return d&&f?t(d.__data__,f.__data__):!d-!f}for(var n=this._groups,r=n.length,i=new Array(r),a=0;ae?1:t>=e?0:NaN}function Wi(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function zi(){return Array.from(this)}function Bi(){for(var t=this._groups,e=0,n=t.length;e1?this.each((e==null?ra:typeof e=="function"?aa:ia)(t,e,n??"")):Ht(this.node(),t)}function Ht(t,e){return t.style.getPropertyValue(e)||Hn(t).getComputedStyle(t,null).getPropertyValue(e)}function ua(t){return function(){delete this[t]}}function sa(t,e){return function(){this[t]=e}}function la(t,e){return function(){var n=e.apply(this,arguments);n==null?delete this[t]:this[t]=n}}function ca(t,e){return arguments.length>1?this.each((e==null?ua:typeof e=="function"?la:sa)(t,e)):this.node()[t]}function Ln(t){return t.trim().split(/^|\s+/)}function Pe(t){return t.classList||new Rn(t)}function Rn(t){this._node=t,this._names=Ln(t.getAttribute("class")||"")}Rn.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function On(t,e){for(var n=Pe(t),r=-1,i=e.length;++r=0&&(n=e.slice(r+1),e=e.slice(0,r)),{type:e,name:n}})}function Ha(t){return function(){var e=this.__on;if(e){for(var n=0,r=-1,i=e.length,a;n()=>t;function ze(t,{sourceEvent:e,subject:n,target:r,identifier:i,active:a,x:o,y:u,dx:s,dy:l,dispatch:c}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:a,enumerable:!0,configurable:!0},x:{value:o,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:s,enumerable:!0,configurable:!0},dy:{value:l,enumerable:!0,configurable:!0},_:{value:c}})}ze.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Qa(t){return!t.ctrlKey&&!t.button}function Ja(){return this.parentNode}function Ka(t,e){return e??{x:t.x,y:t.y}}function ja(){return navigator.maxTouchPoints||"ontouchstart"in this}function to(){var t=Qa,e=Ja,n=Ka,r=ja,i={},a=Re("start","drag","end"),o=0,u,s,l,c,d=0;function f(v){v.on("mousedown.drag",g).filter(r).on("touchstart.drag",F).on("touchmove.drag",b,Xa).on("touchend.drag touchcancel.drag",I).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function g(v,k){if(!(c||!t.call(this,v,k))){var _=$(this,e.call(this,v,k),v,k,"mouse");_&&(Q(v.view).on("mousemove.drag",C,jt).on("mouseup.drag",N,jt),Ga(v.view),We(v),l=!1,u=v.clientX,s=v.clientY,_("start",v))}}function C(v){if(Lt(v),!l){var k=v.clientX-u,_=v.clientY-s;l=k*k+_*_>d}i.mouse("drag",v)}function N(v){Q(v.view).on("mousemove.drag mouseup.drag",null),Za(v.view,l),Lt(v),i.mouse("end",v)}function F(v,k){if(t.call(this,v,k)){var _=v.changedTouches,T=e.call(this,v,k),V=_.length,Y,W;for(Y=0;Y>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):n===8?ye(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):n===4?ye(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=no.exec(t))?new J(e[1],e[2],e[3],1):(e=ro.exec(t))?new J(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=io.exec(t))?ye(e[1],e[2],e[3],e[4]):(e=ao.exec(t))?ye(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=oo.exec(t))?jn(e[1],e[2]/100,e[3]/100,1):(e=uo.exec(t))?jn(e[1],e[2]/100,e[3]/100,e[4]):Xn.hasOwnProperty(t)?Qn(Xn[t]):t==="transparent"?new J(NaN,NaN,NaN,0):null}function Qn(t){return new J(t>>16&255,t>>8&255,t&255,1)}function ye(t,e,n,r){return r<=0&&(t=e=n=NaN),new J(t,e,n,r)}function co(t){return t instanceof te||(t=Dt(t)),t?(t=t.rgb(),new J(t.r,t.g,t.b,t.opacity)):new J}function Xe(t,e,n,r){return arguments.length===1?co(t):new J(t,e,n,r??1)}function J(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}Be(J,Xe,Bn(te,{brighter(t){return t=t==null?pe:Math.pow(pe,t),new J(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?ee:Math.pow(ee,t),new J(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new J(Ct(this.r),Ct(this.g),Ct(this.b),xe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Jn,formatHex:Jn,formatHex8:fo,formatRgb:Kn,toString:Kn}));function Jn(){return`#${kt(this.r)}${kt(this.g)}${kt(this.b)}`}function fo(){return`#${kt(this.r)}${kt(this.g)}${kt(this.b)}${kt((isNaN(this.opacity)?1:this.opacity)*255)}`}function Kn(){const t=xe(this.opacity);return`${t===1?"rgb(":"rgba("}${Ct(this.r)}, ${Ct(this.g)}, ${Ct(this.b)}${t===1?")":`, ${t})`}`}function xe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Ct(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function kt(t){return t=Ct(t),(t<16?"0":"")+t.toString(16)}function jn(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new rt(t,e,n,r)}function tr(t){if(t instanceof rt)return new rt(t.h,t.s,t.l,t.opacity);if(t instanceof te||(t=Dt(t)),!t)return new rt;if(t instanceof rt)return t;t=t.rgb();var e=t.r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,u=a-i,s=(a+i)/2;return u?(e===a?o=(n-r)/u+(n0&&s<1?0:o,new rt(o,u,s,t.opacity)}function ho(t,e,n,r){return arguments.length===1?tr(t):new rt(t,e,n,r??1)}function rt(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}Be(rt,ho,Bn(te,{brighter(t){return t=t==null?pe:Math.pow(pe,t),new rt(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?ee:Math.pow(ee,t),new rt(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new J(Ge(t>=240?t-240:t+120,i,r),Ge(t,i,r),Ge(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new rt(er(this.h),ve(this.s),ve(this.l),xe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=xe(this.opacity);return`${t===1?"hsl(":"hsla("}${er(this.h)}, ${ve(this.s)*100}%, ${ve(this.l)*100}%${t===1?")":`, ${t})`}`}}));function er(t){return t=(t||0)%360,t<0?t+360:t}function ve(t){return Math.max(0,Math.min(1,t||0))}function Ge(t,e,n){return(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)*255}const Ze=t=>()=>t;function go(t,e){return function(n){return t+n*e}}function mo(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}function po(t){return(t=+t)==1?nr:function(e,n){return n-e?mo(e,n,t):Ze(isNaN(e)?n:e)}}function nr(t,e){var n=e-t;return n?go(t,n):Ze(isNaN(t)?e:t)}const we=function t(e){var n=po(e);function r(i,a){var o=n((i=Xe(i)).r,(a=Xe(a)).r),u=n(i.g,a.g),s=n(i.b,a.b),l=nr(i.opacity,a.opacity);return function(c){return i.r=o(c),i.g=u(c),i.b=s(c),i.opacity=l(c),i+""}}return r.gamma=t,r}(1);function yo(t,e){e||(e=[]);var n=t?Math.min(e.length,t.length):0,r=e.slice(),i;return function(a){for(i=0;in&&(a=e.slice(n,a),u[o]?u[o]+=a:u[++o]=a),(r=r[0])===(i=i[0])?u[o]?u[o]+=i:u[++o]=i:(u[++o]=null,s.push({i:o,x:it(r,i)})),n=Je.lastIndex;return n180?c+=360:c-l>180&&(l+=360),f.push({i:d.push(i(d)+"rotate(",null,r)-2,x:it(l,c)})):c&&d.push(i(d)+"rotate("+c+r)}function u(l,c,d,f){l!==c?f.push({i:d.push(i(d)+"skewX(",null,r)-2,x:it(l,c)}):c&&d.push(i(d)+"skewX("+c+r)}function s(l,c,d,f,g,C){if(l!==d||c!==f){var N=g.push(i(g)+"scale(",null,",",null,")");C.push({i:N-4,x:it(l,d)},{i:N-2,x:it(c,f)})}else(d!==1||f!==1)&&g.push(i(g)+"scale("+d+","+f+")")}return function(l,c){var d=[],f=[];return l=t(l),c=t(c),a(l.translateX,l.translateY,c.translateX,c.translateY,d,f),o(l.rotate,c.rotate,d,f),u(l.skewX,c.skewX,d,f),s(l.scaleX,l.scaleY,c.scaleX,c.scaleY,d,f),l=c=null,function(g){for(var C=-1,N=f.length,F;++C=0&&t._call.call(void 0,e),t=t._next;--Ot}function cr(){Nt=(Te=oe.now())+Me,Ot=re=0;try{Eo()}finally{Ot=0,Uo(),Nt=0}}function Ao(){var t=oe.now(),e=t-Te;e>ur&&(Me-=e,Te=t)}function Uo(){for(var t,e=be,n,r=1/0;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:be=n);ae=t,en(r)}function en(t){if(!Ot){re&&(re=clearTimeout(re));var e=t-Nt;e>24?(t<1/0&&(re=setTimeout(cr,t-oe.now()-Me)),ie&&(ie=clearInterval(ie))):(ie||(Te=oe.now(),ie=setInterval(Ao,ur)),Ot=1,sr(cr))}}function fr(t,e,n){var r=new De;return e=e==null?0:+e,r.restart(i=>{r.stop(),t(i+e)},e,n),r}var Fo=Re("start","end","cancel","interrupt"),$o=[],hr=0,dr=1,nn=2,Ce=3,gr=4,rn=5,ke=6;function Ne(t,e,n,r,i,a){var o=t.__transition;if(!o)t.__transition={};else if(n in o)return;Vo(t,n,{name:e,index:r,group:i,on:Fo,tween:$o,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:hr})}function an(t,e){var n=at(t,e);if(n.state>hr)throw new Error("too late; already scheduled");return n}function lt(t,e){var n=at(t,e);if(n.state>Ce)throw new Error("too late; already running");return n}function at(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function Vo(t,e,n){var r=t.__transition,i;r[e]=n,n.timer=lr(a,0,n.time);function a(l){n.state=dr,n.timer.restart(o,n.delay,n.time),n.delay<=l&&o(l-n.delay)}function o(l){var c,d,f,g;if(n.state!==dr)return s();for(c in r)if(g=r[c],g.name===n.name){if(g.state===Ce)return fr(o);g.state===gr?(g.state=ke,g.timer.stop(),g.on.call("interrupt",t,t.__data__,g.index,g.group),delete r[c]):+cnn&&r.state=0&&(e=e.slice(0,n)),!e||e==="start"})}function hu(t,e,n){var r,i,a=fu(e)?an:lt;return function(){var o=a(this,t),u=o.on;u!==r&&(i=(r=u).copy()).on(e,n),o.on=i}}function du(t,e){var n=this._id;return arguments.length<2?at(this.node(),n).on.on(t):this.each(hu(n,t,e))}function gu(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}function mu(){return this.on("end.remove",gu(this._id))}function pu(t){var e=this._name,n=this._id;typeof t!="function"&&(t=qe(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;oe&&(n=t,t=e,e=n),function(r){return Math.max(t,Math.min(e,r))}}function Wu(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i2?zu:Wu,s=l=null,d}function d(f){return f==null||isNaN(f=+f)?a:(s||(s=u(t.map(r),e,n)))(r(o(f)))}return d.invert=function(f){return o(i((l||(l=u(e,t.map(r),it)))(f)))},d.domain=function(f){return arguments.length?(t=Array.from(f,qu),c()):t.slice()},d.range=function(f){return arguments.length?(e=Array.from(f),c()):e.slice()},d.rangeRound=function(f){return e=Array.from(f),n=Mo,c()},d.clamp=function(f){return arguments.length?(o=f?!0:qt,c()):o!==qt},d.interpolate=function(f){return arguments.length?(n=f,c()):n},d.unknown=function(f){return arguments.length?(a=f,d):a},function(f,g){return r=f,i=g,c()}}function Gu(){return Xu()(qt,qt)}function Zu(t,e){t=t.slice();var n=0,r=t.length-1,i=t[n],a=t[r],o;return a(t(a=new Date(+a)),a),i.ceil=a=>(t(a=new Date(a-1)),e(a,1),t(a),a),i.round=a=>{const o=i(a),u=i.ceil(a);return a-o(e(a=new Date(+a),o==null?1:Math.floor(o)),a),i.range=(a,o,u)=>{const s=[];if(a=i.ceil(a),u=u==null?1:Math.floor(u),!(a0))return s;let l;do s.push(l=new Date(+a)),e(a,u),t(a);while(lL(o=>{if(o>=o)for(;t(o),!a(o);)o.setTime(o-1)},(o,u)=>{if(o>=o)if(u<0)for(;++u<=0;)for(;e(o,-1),!a(o););else for(;--u>=0;)for(;e(o,1),!a(o););}),n&&(i.count=(a,o)=>(ln.setTime(+a),cn.setTime(+o),t(ln),t(cn),Math.floor(n(ln,cn))),i.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?i.filter(r?o=>r(o)%a===0:o=>i.count(0,o)%a===0):i)),i}const Se=L(()=>{},(t,e)=>{t.setTime(+t+e)},(t,e)=>e-t);Se.every=t=>(t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?L(e=>{e.setTime(Math.floor(e/t)*t)},(e,n)=>{e.setTime(+e+n*t)},(e,n)=>(n-e)/t):Se),Se.range;const dt=1e3,et=dt*60,gt=et*60,mt=gt*24,fn=mt*7,br=mt*30,hn=mt*365,Pt=L(t=>{t.setTime(t-t.getMilliseconds())},(t,e)=>{t.setTime(+t+e*dt)},(t,e)=>(e-t)/dt,t=>t.getUTCSeconds());Pt.range;const dn=L(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*dt)},(t,e)=>{t.setTime(+t+e*et)},(t,e)=>(e-t)/et,t=>t.getMinutes());dn.range,L(t=>{t.setUTCSeconds(0,0)},(t,e)=>{t.setTime(+t+e*et)},(t,e)=>(e-t)/et,t=>t.getUTCMinutes()).range;const gn=L(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*dt-t.getMinutes()*et)},(t,e)=>{t.setTime(+t+e*gt)},(t,e)=>(e-t)/gt,t=>t.getHours());gn.range,L(t=>{t.setUTCMinutes(0,0,0)},(t,e)=>{t.setTime(+t+e*gt)},(t,e)=>(e-t)/gt,t=>t.getUTCHours()).range;const Wt=L(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*et)/mt,t=>t.getDate()-1);Wt.range;const mn=L(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/mt,t=>t.getUTCDate()-1);mn.range,L(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/mt,t=>Math.floor(t/mt)).range;function St(t){return L(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,n)=>{e.setDate(e.getDate()+n*7)},(e,n)=>(n-e-(n.getTimezoneOffset()-e.getTimezoneOffset())*et)/fn)}const Ee=St(0),Ae=St(1),Qu=St(2),Ju=St(3),zt=St(4),Ku=St(5),ju=St(6);Ee.range,Ae.range,Qu.range,Ju.range,zt.range,Ku.range,ju.range;function Et(t){return L(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCDate(e.getUTCDate()+n*7)},(e,n)=>(n-e)/fn)}const Tr=Et(0),Ue=Et(1),ts=Et(2),es=Et(3),Bt=Et(4),ns=Et(5),rs=Et(6);Tr.range,Ue.range,ts.range,es.range,Bt.range,ns.range,rs.range;const pn=L(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth());pn.range,L(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()).range;const pt=L(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());pt.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:L(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,n)=>{e.setFullYear(e.getFullYear()+n*t)}),pt.range;const At=L(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());At.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:L(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCFullYear(e.getUTCFullYear()+n*t)}),At.range;function is(t,e,n,r,i,a){const o=[[Pt,1,dt],[Pt,5,5*dt],[Pt,15,15*dt],[Pt,30,30*dt],[a,1,et],[a,5,5*et],[a,15,15*et],[a,30,30*et],[i,1,gt],[i,3,3*gt],[i,6,6*gt],[i,12,12*gt],[r,1,mt],[r,2,2*mt],[n,1,fn],[e,1,br],[e,3,3*br],[t,1,hn]];function u(l,c,d){const f=cF).right(o,f);if(g===o.length)return t.every(Nn(l/hn,c/hn,d));if(g===0)return Se.every(Math.max(Nn(l,c,d),1));const[C,N]=o[f/o[g-1][2]53)return null;"w"in p||(p.w=1),"Z"in p?(E=xn(ue(p.y,0,1)),O=E.getUTCDay(),E=O>4||O===0?Ue.ceil(E):Ue(E),E=mn.offset(E,(p.V-1)*7),p.y=E.getUTCFullYear(),p.m=E.getUTCMonth(),p.d=E.getUTCDate()+(p.w+6)%7):(E=yn(ue(p.y,0,1)),O=E.getDay(),E=O>4||O===0?Ae.ceil(E):Ae(E),E=Wt.offset(E,(p.V-1)*7),p.y=E.getFullYear(),p.m=E.getMonth(),p.d=E.getDate()+(p.w+6)%7)}else("W"in p||"U"in p)&&("w"in p||(p.w="u"in p?p.u%7:"W"in p?1:0),O="Z"in p?xn(ue(p.y,0,1)).getUTCDay():yn(ue(p.y,0,1)).getDay(),p.m=0,p.d="W"in p?(p.w+6)%7+p.W*7-(O+5)%7:p.w+p.U*7-(O+6)%7);return"Z"in p?(p.H+=p.Z/100|0,p.M+=p.Z%100,xn(p)):yn(p)}}function V(y,M,D,p){for(var R=0,E=M.length,O=D.length,q,K;R=O)return-1;if(q=M.charCodeAt(R++),q===37){if(q=M.charAt(R++),K=k[q in Mr?M.charAt(R++):q],!K||(p=K(y,D,p))<0)return-1}else if(q!=D.charCodeAt(p++))return-1}return p}function Y(y,M,D){var p=l.exec(M.slice(D));return p?(y.p=c.get(p[0].toLowerCase()),D+p[0].length):-1}function W(y,M,D){var p=g.exec(M.slice(D));return p?(y.w=C.get(p[0].toLowerCase()),D+p[0].length):-1}function H(y,M,D){var p=d.exec(M.slice(D));return p?(y.w=f.get(p[0].toLowerCase()),D+p[0].length):-1}function Z(y,M,D){var p=b.exec(M.slice(D));return p?(y.m=I.get(p[0].toLowerCase()),D+p[0].length):-1}function ot(y,M,D){var p=N.exec(M.slice(D));return p?(y.m=F.get(p[0].toLowerCase()),D+p[0].length):-1}function bt(y,M,D){return V(y,e,M,D)}function fe(y,M,D){return V(y,n,M,D)}function Ut(y,M,D){return V(y,r,M,D)}function z(y){return o[y.getDay()]}function nt(y){return a[y.getDay()]}function ut(y){return s[y.getMonth()]}function Tt(y){return u[y.getMonth()]}function vn(y){return i[+(y.getHours()>=12)]}function wn(y){return 1+~~(y.getMonth()/3)}function _n(y){return o[y.getUTCDay()]}function yt(y){return a[y.getUTCDay()]}function x(y){return s[y.getUTCMonth()]}function m(y){return u[y.getUTCMonth()]}function w(y){return i[+(y.getUTCHours()>=12)]}function S(y){return 1+~~(y.getUTCMonth()/3)}return{format:function(y){var M=_(y+="",$);return M.toString=function(){return y},M},parse:function(y){var M=T(y+="",!1);return M.toString=function(){return y},M},utcFormat:function(y){var M=_(y+="",v);return M.toString=function(){return y},M},utcParse:function(y){var M=T(y+="",!0);return M.toString=function(){return y},M}}}var Mr={"-":"",_:" ",0:"0"},P=/^\s*\d+/,ss=/^%/,ls=/[\\^$*+?|[\]().{}]/g;function A(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a[e.toLowerCase(),n]))}function fs(t,e,n){var r=P.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function hs(t,e,n){var r=P.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function ds(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function gs(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function ms(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function Dr(t,e,n){var r=P.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Cr(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function ps(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function ys(t,e,n){var r=P.exec(e.slice(n,n+1));return r?(t.q=r[0]*3-3,n+r[0].length):-1}function xs(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function kr(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function vs(t,e,n){var r=P.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function Nr(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function ws(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function _s(t,e,n){var r=P.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function bs(t,e,n){var r=P.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function Ts(t,e,n){var r=P.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function Ms(t,e,n){var r=ss.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function Ds(t,e,n){var r=P.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function Cs(t,e,n){var r=P.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function Sr(t,e){return A(t.getDate(),e,2)}function ks(t,e){return A(t.getHours(),e,2)}function Ns(t,e){return A(t.getHours()%12||12,e,2)}function Ss(t,e){return A(1+Wt.count(pt(t),t),e,3)}function Er(t,e){return A(t.getMilliseconds(),e,3)}function Es(t,e){return Er(t,e)+"000"}function As(t,e){return A(t.getMonth()+1,e,2)}function Us(t,e){return A(t.getMinutes(),e,2)}function Fs(t,e){return A(t.getSeconds(),e,2)}function $s(t){var e=t.getDay();return e===0?7:e}function Vs(t,e){return A(Ee.count(pt(t)-1,t),e,2)}function Ar(t){var e=t.getDay();return e>=4||e===0?zt(t):zt.ceil(t)}function Is(t,e){return t=Ar(t),A(zt.count(pt(t),t)+(pt(t).getDay()===4),e,2)}function Ys(t){return t.getDay()}function Hs(t,e){return A(Ae.count(pt(t)-1,t),e,2)}function Ls(t,e){return A(t.getFullYear()%100,e,2)}function Rs(t,e){return t=Ar(t),A(t.getFullYear()%100,e,2)}function Os(t,e){return A(t.getFullYear()%1e4,e,4)}function qs(t,e){var n=t.getDay();return t=n>=4||n===0?zt(t):zt.ceil(t),A(t.getFullYear()%1e4,e,4)}function Ps(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+A(e/60|0,"0",2)+A(e%60,"0",2)}function Ur(t,e){return A(t.getUTCDate(),e,2)}function Ws(t,e){return A(t.getUTCHours(),e,2)}function zs(t,e){return A(t.getUTCHours()%12||12,e,2)}function Bs(t,e){return A(1+mn.count(At(t),t),e,3)}function Fr(t,e){return A(t.getUTCMilliseconds(),e,3)}function Xs(t,e){return Fr(t,e)+"000"}function Gs(t,e){return A(t.getUTCMonth()+1,e,2)}function Zs(t,e){return A(t.getUTCMinutes(),e,2)}function Qs(t,e){return A(t.getUTCSeconds(),e,2)}function Js(t){var e=t.getUTCDay();return e===0?7:e}function Ks(t,e){return A(Tr.count(At(t)-1,t),e,2)}function $r(t){var e=t.getUTCDay();return e>=4||e===0?Bt(t):Bt.ceil(t)}function js(t,e){return t=$r(t),A(Bt.count(At(t),t)+(At(t).getUTCDay()===4),e,2)}function tl(t){return t.getUTCDay()}function el(t,e){return A(Ue.count(At(t)-1,t),e,2)}function nl(t,e){return A(t.getUTCFullYear()%100,e,2)}function rl(t,e){return t=$r(t),A(t.getUTCFullYear()%100,e,2)}function il(t,e){return A(t.getUTCFullYear()%1e4,e,4)}function al(t,e){var n=t.getUTCDay();return t=n>=4||n===0?Bt(t):Bt.ceil(t),A(t.getUTCFullYear()%1e4,e,4)}function ol(){return"+0000"}function Vr(){return"%"}function Ir(t){return+t}function Yr(t){return Math.floor(+t/1e3)}var Xt,_t;ul({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function ul(t){return Xt=us(t),_t=Xt.format,Xt.parse,Xt.utcFormat,Xt.utcParse,Xt}function sl(t){return new Date(t)}function ll(t){return t instanceof Date?+t:+new Date(+t)}function Hr(t,e,n,r,i,a,o,u,s,l){var c=Gu(),d=c.invert,f=c.domain,g=l(".%L"),C=l(":%S"),N=l("%I:%M"),F=l("%I %p"),b=l("%a %d"),I=l("%b %d"),$=l("%B"),v=l("%Y");function k(_){return(s(_)<_?g:u(_)<_?C:o(_)<_?N:a(_)<_?F:r(_)<_?i(_)<_?b:I:n(_)<_?$:v)(_)}return c.invert=function(_){return new Date(d(_))},c.domain=function(_){return arguments.length?f(Array.from(_,ll)):f().map(sl)},c.ticks=function(_){var T=f();return t(T[0],T[T.length-1],_??10)},c.tickFormat=function(_,T){return T==null?k:l(T)},c.nice=function(_){var T=f();return(!_||typeof _.range!="function")&&(_=e(T[0],T[T.length-1],_??10)),_?f(Zu(T,_)):c},c.copy=function(){return Bu(c,Hr(t,e,n,r,i,a,o,u,s,l))},c}function cl(){return un.apply(Hr(as,os,pt,pn,Ee,Wt,gn,dn,Pt,_t).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}function ce(t,e,n){this.k=t,this.x=e,this.y=n}ce.prototype={constructor:ce,scale:function(t){return t===1?this:new ce(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new ce(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}},ce.prototype;const fl=(t,e)=>{const n=t.__vccOpts||t;for(const[r,i]of e)n[r]=i;return n},hl={class:"gantt-wrapper relative w-full"},dl={class:"absolute top-0 right-0 z-10 flex space-x-4 items-center mr-2"},gl={class:"flex items-center space-x-2"},ml=["value"],pl=["disabled"],yl={key:0,class:"absolute inset-0 flex items-center justify-center bg-gray-50/50 z-20"},xl={class:"text-xl font-semibold text-gray-600 p-4 bg-white rounded-lg shadow-lg"},vl={key:1,class:"absolute inset-0 flex items-center justify-center bg-gray-50/50 z-20"},wl={class:"text-xs text-gray-500 mt-2"},_l={class:"text-xs font-medium text-gray-600"},bl={class:"text-xs font-medium text-gray-600"},Tl={class:"flex justify-between items-center mb-1"},Ml=["value"],Dl={class:"space-y-1"},Cl={class:"flex items-center justify-between text-xs font-medium text-gray-600"},kl={for:"enable-freq",class:"flex items-center space-x-2 cursor-pointer"},Nl={key:0,class:"text-xs font-medium text-gray-600"},Sl={class:"text-xs font-medium text-gray-600"},El={class:"text-xs font-medium text-gray-600"},Al={class:"text-xs font-medium text-gray-600"},Ul={class:"mt-3 flex justify-between items-center"},Fl={class:"text-xs font-bold text-blue-600"},$l=5,Vl=.2,Il=fl({__name:"GanttChart",props:{tasks:{type:Array,default:()=>[]},startDate:{type:String,default:"2012-12-12"},endDate:{type:String,default:"2013-12-13"},availableCategoriesList:{type:Array,default:()=>[]}},emits:["taskUpdated"],setup(t,{emit:e}){const n=e,r=t,i=h.ref([]),a=h.ref("All"),o=["#4F46E5","#10B981","#F59E0B","#EF4444","#6366F1","#EC4899","#06B6D4"],u=h.ref({}),s=x=>{if(x.length===0)return 1;const m=[...x].sort((S,y)=>S-y),w=Math.floor(m.length/2);return m.length%2===0?(m[w-1]+m[w])/2:m[w]},l=x=>{const m=new Set;x.forEach(y=>{y.category&&y.category.trim()&&m.add(y.category)}),r.availableCategoriesList.forEach(y=>{y&&y.trim()&&m.add(y)});let w={...u.value},S=0;m.forEach(y=>{if(!w[y]){let M=o[S%o.length];w[y]=M}S++}),u.value=w},c=x=>!x||x==="Uncategorized"?"#9CA3AF":u.value[x]||"#9CA3AF",d=h.computed(()=>{const x=r.availableCategoriesList.filter(m=>m&&m.trim()).sort();return x.length===0?["Uncategorized"]:x}),f=h.computed(()=>{const x=new Set(["All"]);return r.availableCategoriesList.forEach(m=>{m&&m.trim()&&x.add(m)}),Array.from(x).sort()}),g=h.ref(null),C=h.ref(0),N={top:40,right:20,bottom:30,left:50},F=h.ref(!1),b=h.ref(null),I=(x,m)=>{if(!x||!m)return 1;const w=new Date(x),y=new Date(m).getTime()-w.getTime();if(y<0)return 1;const M=Math.ceil(y/(1e3*60*60*24))+1;return Math.max(1,M)},$=x=>I(x.start,x.end),v=h.computed(()=>{if(i.value.length===0)return 1;const x=i.value.filter(m=>m.start&&m.end).map($);return Math.max(1,s(x))}),k=(x,m)=>{if(!x||m<=0)return x;const w=new Date(x);return w.setDate(w.getDate()+m-1),_t("%Y-%m-%d")(w)},_=x=>x.toISOString().split("T")[0],T=h.computed(()=>a.value==="All"?i.value:i.value.filter(x=>x.category===a.value)),V=h.computed(()=>Math.max(150,T.value.length*40+N.top+N.bottom)),Y=h.computed(()=>{if(i.value.length===0)return new Date(r.startDate);const x=i.value.map(m=>new Date(m.start));return new Date(Math.min(...x))}),W=h.computed(()=>{if(i.value.length===0)return new Date(r.endDate);const x=i.value.map(w=>new Date(w.end)),m=new Date(Math.max(...x));return m.setDate(m.getDate()+10),m}),H=()=>{const m=(i.value.length>0?Math.max(...i.value.map(y=>y.id)):0)+1;let w,S="Planning";if(a.value!=="All"?S=a.value:i.value.length>0&&(S=i.value[i.value.length-1].category||"Planning"),i.value.length>0){const y=i.value[i.value.length-1],M=new Date(y.end).getTime()-new Date(y.start).getTime(),D=new Date(y.end);D.setDate(D.getDate()+1);const p=new Date(D.getTime()+M);w={id:m,name:`Tâche Copiée ${m}`,start:_(D),end:_(p),category:S,isNew:!0}}else{const y=new Date,M=new Date;M.setDate(M.getDate()+10),w={id:m,name:`Première Tâche ${m}`,start:_(y),end:_(M),category:S,isNew:!0}}i.value.push(w),l(i.value),n("taskUpdated",i.value)},Z=()=>{if(i.value.length===0)return;let x=i.value,m=-1;if(a.value!=="All"&&(x=i.value.filter(w=>w.category===a.value)),x.length>0)m=Math.max(...x.map(w=>w.id));else return;if(m!==-1){const w=i.value.filter(S=>S.id!==m);i.value=w,n("taskUpdated",i.value)}},ot=()=>{const x=[...i.value].sort((m,w)=>{const S=new Date(m.start),y=new Date(w.start);return Sy?1:m.id-w.id});i.value=x,n("taskUpdated",i.value)},bt=x=>{const m=i.value.findIndex(w=>w.id===x.id);if(m!==-1){const w={...i.value[m],start:x.newStart,end:x.newEnd,category:i.value[m].category,name:x.name||i.value[m].name},S=[...i.value];S[m]=w,i.value=S,n("taskUpdated",i.value)}},fe=h.computed(()=>b.value?I(b.value.start,b.value.end):0),Ut=h.computed(()=>!b.value||!b.value.y||!g.value?{}:{position:"absolute",left:`${g.value.clientWidth/2}px`,top:`${b.value.y+N.top+5}px`,transform:"translateX(-50%)",zIndex:30,minWidth:"320px"}),z=h.ref(null),nt=h.ref(null),ut=h.ref(!1),Tt=x=>{if(F.value=!1,b.value&&b.value.id===x.id){b.value=null,ut.value=!1;return}if(!z.value||!nt.value)return;const m=0,w=0;b.value={id:x.id,name:x.name,start:x.start,end:x.end,durationDays:I(x.start,x.end),x:m,y:w,category:x.category||"Uncategorized",newCategory:null,freq:x.freq||null},ut.value=!!x.freq,h.nextTick(()=>{const S=document.querySelector(".gantt-edit-form input");S&&S.focus()})};h.watch(()=>{var x;return(x=b.value)==null?void 0:x.durationDays},(x,m)=>{if(b.value&&x){x!==m&&(b.value.end=k(b.value.start,x));const w=Math.max(1,x);b.value.durationDays=w,b.value.end=k(b.value.start,w)}}),h.watch(()=>{var x;return(x=b.value)==null?void 0:x.start},(x,m)=>{b.value&&x&&(b.value.end=k(x,Math.max(1,b.value.durationDays)),x!==m&&(b.value.durationDays=I(x,b.value.end)))});const vn=()=>{const{id:x,name:m,start:w,end:S,freq:y}=b.value,M=i.value.findIndex(Gt=>Gt.id===x);if(M===-1)return;const D=i.value[M];let{category:p}=b.value;const R=b.value.freq>0?b.value.freq:null,E=new Date(w),O=new Date(S);if(E.getTime()>=O.getTime()){console.error("La tâche doit avoir une durée d'au moins un jour (date de début < date de fin)."),console.warn("Erreur: La date de début doit être strictement antérieure à la date de fin pour garantir une durée positive. Opération annulée.");return}const q={...D,name:m,start:w,end:S,category:p,freq:R},K=[...i.value];K[M]=q,i.value=K,n("taskUpdated",i.value),b.value=null,ut.value=!1},wn=h.computed(()=>b.value?c(b.value.category):"#9CA3AF"),_n=x=>{const m=Y.value,w=W.value;z.value=cl().domain([m,w]).range([0,x-N.left-N.right]),nt.value=wr().domain(Array.isArray(T.value)?T.value.map(S=>S.name):[]).range([0,T.value.length*40]).paddingInner(.1)},yt=()=>{if(!g.value)return;C.value=g.value.clientWidth;const x=C.value;_n(x);const m=Q(g.value);m.selectAll("*").remove();const S=m.append("svg").attr("width",x).attr("height",V.value).append("g").attr("transform",`translate(${N.left}, ${N.top})`);if(S.append("g").attr("transform",`translate(0, ${T.value.length*40||50})`).call(li(z.value)),S.append("g").attr("class","y-axis-g").call(ci(nt.value)).selectAll(".tick text").style("cursor","pointer").on("contextmenu",function(U,B){U.preventDefault(),U.stopPropagation();const X=B,G=T.value.find(ct=>ct.name===X);G&&Tt(G)}),!T.value.length)return;const D=v.value*$l,p=Wt.offset(Y.value,1),R=z.value(p)-z.value(Y.value);let E=!1,O,q,K=-1;const Gt=nt.value.step(),Fe=a.value!=="All",Yl=to().on("start",function(U,B){if(b.value){Q(this).style("cursor","pointer"),U.sourceEvent.stopPropagation();return}F.value=!1,E=!1,O=U.x,q=U.y,K=T.value.findIndex(X=>X.id===B.id),Q(this).raise().classed("dragging",!0).style("cursor","grabbing")}).on("drag",function(U,B){if(b.value)return;const X=U.x-O,G=U.y-q;if(Math.abs(G)>5&&!Fe)E=!0,Q(this).attr("transform",`translate(0, ${G})`);else if(Math.abs(X)>5||Fe){F.value=!0,E=!1;const ct=z.value(new Date(B.start))+X;Q(this).select("rect").attr("x",ct),Q(this).select("text").attr("x",ct+5),Q(this).attr("transform",null)}}).on("end",function(U,B){if(Q(this).style("cursor","grab"),b.value){Q(this).classed("dragging",!1).attr("transform",null);return}if(Q(this).classed("dragging",!1).attr("transform",null),E&&!Fe){const X=nt.value(B.name)+(U.y-q),G=Math.round(X/Gt),ct=Math.max(0,Math.min(T.value.length-1,G));if(ct!==K){const Ft=T.value[K].id,$t=i.value.findIndex(It=>It.id===Ft),Mt=T.value[ct].id,Vt=i.value.findIndex(It=>It.id===Mt),xt=[...i.value],[Zt]=xt.splice($t,1);xt.splice(Vt,0,Zt),i.value=xt,n("taskUpdated",i.value)}else yt()}else if(F.value){const X=z.value(new Date(B.start))+(U.x-O),G=z.value.invert(X),ct=new Date(B.end).getTime()-new Date(B.start).getTime(),Ft=new Date(G.getTime()+ct),$t=_t("%Y-%m-%d")(G),Mt=_t("%Y-%m-%d")(Ft);bt({id:B.id,name:B.name,newStart:$t,newEnd:Mt})}else yt();F.value=!1,E=!1});T.value.forEach(U=>{const B=z.value(new Date(U.start)),X=nt.value(U.name),G=nt.value.bandwidth();if(U.freq&&U.freq>0){const tt=U.freq,Yt=new Date(U.start),Qt=new Date(U.end);let vt=[],$e=new Date(Yt);for(;$e.getTime()<=Qt.getTime();)vt.push(new Date($e)),$e.setDate($e.getDate()+tt);if(vt.length===0)return;const bn=S.append("g").datum(U).attr("class","task-group task-freq"),Rr=c(U.category),Or=z.value(vt[0]),Ll=z.value(vt[vt.length-1]),qr=X+G/10;bn.append("line").attr("x1",Or).attr("y1",qr).attr("x2",Ll).attr("y2",qr).attr("stroke",Rr).attr("stroke-width",2).attr("stroke-dasharray","5, 5").attr("opacity",.6);const Ve=G/5,Tn=Ve/2;vt.forEach(Pr=>{_t("%Y-%m-%d")(Pr);const Wr=z.value(Pr)-Tn,zr=X;bn.append("rect").attr("x",Wr).attr("y",zr).attr("width",Ve).attr("height",Ve).attr("transform",`rotate(45, ${Wr+Tn}, ${zr+Tn})`).attr("rx",2).attr("fill",Rr).attr("stroke","black").attr("stroke-width",1)}),bn.append("text").text(`🔁: ${U.name}`).attr("x",Or+Ve+5).attr("y",X+G/2+5).attr("fill","black").style("pointer-events","none").style("font-size","12px").style("font-weight","bold");return}const Ft=$(U);let $t,Mt=!1;const Vt=Ft<=2;if(!Vt&&Ft>D){Mt=!0;const tt=D*R,Qt=(Ft-D)*Vl*R;$t=tt+Qt}else Vt||($t=z.value(new Date(U.end))-B);const xt=S.append("g").datum(U).attr("class","task-group").on("contextmenu",function(tt){tt.preventDefault(),tt.sourceEvent?tt.sourceEvent.stopPropagation():tt.stopPropagation(),Tt(U)});let Zt,It;if(Vt){const tt=G/5,Yt=tt/2,Qt=B-Yt,vt=X;Zt=xt.append("rect").attr("x",Qt).attr("y",vt).attr("width",tt).attr("height",tt).attr("transform",`rotate(45, ${Qt+Yt}, ${vt+Yt})`).attr("rx",2),It=B+Yt+5}else Zt=xt.append("rect").attr("x",B).attr("y",X).attr("width",$t).attr("height",G).attr("rx",4),It=B+5;Zt.attr("fill",c(U.category)).style("cursor","grab"),Mt&&Zt.attr("stroke","black").attr("stroke-width",2),Yl(xt);const Lr=Mt?`📐: ${U.name}`:U.name,Hl=Vt?`◆: ${Lr}`:Lr;xt.append("text").text(Hl).attr("x",It).attr("y",X+G/2+5).attr("fill","black").style("pointer-events","none").style("font-size","12px").style("font-weight",Mt||Vt?"bold":"normal"),Fe&&xt.append("text").text("🚫").attr("x",-45).attr("y",X+nt.value.bandwidth()/2+5).attr("fill","gray").style("font-size","14px").style("cursor","help").attr("title","Réorganisation désactivée en mode filtré")})};return h.watch(()=>r.tasks,x=>{Array.isArray(x)?(i.value=x.map(m=>{const{color:w,...S}=m;return{...S}}),l(i.value),yt()):(i.value=[],u.value={},yt())},{immediate:!0,deep:!0}),h.onMounted(()=>{yt(),window.addEventListener("resize",yt)}),h.watch([()=>i.value,Y,W,a],yt,{deep:!0}),(x,m)=>(h.openBlock(),h.createElementBlock("div",hl,[h.createElementVNode("div",dl,[h.createElementVNode("div",gl,[m[9]||(m[9]=h.createElementVNode("label",{for:"categoryFilter",class:"text-sm font-medium text-gray-700"},"Filtrer par:",-1)),h.withDirectives(h.createElementVNode("select",{id:"categoryFilter","onUpdate:modelValue":m[0]||(m[0]=w=>a.value=w),class:"p-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 text-sm"},[(h.openBlock(!0),h.createElementBlock(h.Fragment,null,h.renderList(f.value,w=>(h.openBlock(),h.createElementBlock("option",{key:w,value:w,style:h.normalizeStyle({color:w==="All"?"inherit":c(w),fontWeight:"bold"})},h.toDisplayString(w),13,ml))),128))],512),[[h.vModelSelect,a.value]])]),h.createElementVNode("button",{onClick:H,class:"bg-green-500 hover:bg-green-600 text-gray-500 font-bold py-1 px-2 rounded-full shadow-lg transition duration-150 text-lg leading-none w-8 h-8 flex items-center justify-center",title:"Ajouter une nouvelle tâche (copie de la dernière)"}," + "),h.createElementVNode("button",{onClick:ot,class:"bg-blue-500 hover:bg-blue-600 text-gray-500 font-bold py-1 px-2 rounded-full shadow-lg transition duration-150 text-sm leading-none w-8 h-8 flex items-center justify-center",title:"Trier par date de début"}," ⇅ "),h.createElementVNode("button",{onClick:Z,disabled:i.value.length===0,class:"bg-red-500 hover:bg-red-600 disabled:bg-gray-400 text-gray-500 font-bold py-1 px-2 rounded-full shadow-lg transition duration-150 text-lg leading-none w-8 h-8 flex items-center justify-center",title:"Supprimer la dernière tâche (ID max)"}," − ",8,pl)]),h.createElementVNode("div",{ref_key:"ganttContainer",ref:g,class:"relative w-full overflow-x-auto"},[T.value.length===0&&i.value.length>0?(h.openBlock(),h.createElementBlock("div",yl,[h.createElementVNode("p",xl," Aucune tâche ne correspond au filtre '"+h.toDisplayString(a.value)+"'. ",1)])):i.value.length===0?(h.openBlock(),h.createElementBlock("div",vl,[...m[10]||(m[10]=[h.createElementVNode("p",{class:"text-xl font-semibold text-gray-600 p-4 bg-white rounded-lg shadow-lg"}," Le tableau de tâches est vide. Utilisez '+' pour commencer ! ",-1)])])):h.createCommentVNode("",!0)],512),h.createElementVNode("div",null,[h.createElementVNode("p",wl," Time period: "+h.toDisplayString(_t("%Y-%m-%d")(Y.value))+" to "+h.toDisplayString(_t("%Y-%m-%d")(W.value)),1)]),b.value?(h.openBlock(),h.createElementBlock("div",{key:0,style:h.normalizeStyle(Ut.value),class:"gantt-edit-form p-4 border border-blue-400 rounded-lg shadow-xl flex flex-col space-y-2 z-50"},[m[18]||(m[18]=h.createElementVNode("div",{class:"form-title text-sm font-bold text-gray-800 mb-2"},"Edit",-1)),h.createElementVNode("label",_l,[m[11]||(m[11]=h.createTextVNode(" Name: ",-1)),h.withDirectives(h.createElementVNode("input",{type:"text","onUpdate:modelValue":m[1]||(m[1]=w=>b.value.name=w),class:"mt-1 p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500"},null,512),[[h.vModelText,b.value.name]])]),h.createElementVNode("label",bl,[h.createElementVNode("div",Tl,[m[12]||(m[12]=h.createElementVNode("span",null,"Category:",-1)),h.createElementVNode("span",{style:h.normalizeStyle({backgroundColor:wn.value}),class:"w-6 h-6 rounded-full border border-gray-400 inline-block"},null,4),h.withDirectives(h.createElementVNode("select",{"onUpdate:modelValue":m[2]||(m[2]=w=>b.value.category=w),class:"p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500 flex-grow"},[(h.openBlock(!0),h.createElementBlock(h.Fragment,null,h.renderList(d.value,w=>(h.openBlock(),h.createElementBlock("option",{key:w,value:w},h.toDisplayString(w),9,Ml))),128))],512),[[h.vModelSelect,b.value.category]])])]),h.createElementVNode("div",Dl,[h.createElementVNode("div",Cl,[h.createElementVNode("label",kl,[h.withDirectives(h.createElementVNode("input",{type:"checkbox",id:"enable-freq","onUpdate:modelValue":m[3]||(m[3]=w=>ut.value=w),class:"rounded text-blue-600 focus:ring-blue-500 h-4 w-4"},null,512),[[h.vModelCheckbox,ut.value]]),m[13]||(m[13]=h.createElementVNode("span",null,"Make recursive event",-1))])]),ut.value?(h.openBlock(),h.createElementBlock("label",Nl,[m[14]||(m[14]=h.createTextVNode(" Frequency (recurring in days): ",-1)),h.withDirectives(h.createElementVNode("input",{type:"number","onUpdate:modelValue":m[4]||(m[4]=w=>b.value.freq=w),placeholder:"ex: 7 pour hebdomadaire",min:"1",class:"mt-1 p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500"},null,512),[[h.vModelText,b.value.freq,void 0,{number:!0}]])])):h.createCommentVNode("",!0)]),m[19]||(m[19]=h.createElementVNode("hr",{class:"border-gray-200 my-1"},null,-1)),h.createElementVNode("label",Sl,[m[15]||(m[15]=h.createTextVNode(" Duration (in days): ",-1)),h.withDirectives(h.createElementVNode("input",{type:"number","onUpdate:modelValue":m[5]||(m[5]=w=>b.value.durationDays=w),min:"1",class:"mt-1 p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500"},null,512),[[h.vModelText,b.value.durationDays,void 0,{number:!0}]])]),h.createElementVNode("label",El,[m[16]||(m[16]=h.createTextVNode(" Start: ",-1)),h.withDirectives(h.createElementVNode("input",{type:"date","onUpdate:modelValue":m[6]||(m[6]=w=>b.value.start=w),class:"mt-1 p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500"},null,512),[[h.vModelText,b.value.start]])]),h.createElementVNode("label",Al,[m[17]||(m[17]=h.createTextVNode(" End: ",-1)),h.withDirectives(h.createElementVNode("input",{type:"date","onUpdate:modelValue":m[7]||(m[7]=w=>b.value.end=w),class:"mt-1 p-1 border rounded-md w-full text-sm focus:ring-blue-500 focus:border-blue-500"},null,512),[[h.vModelText,b.value.end]])]),h.createElementVNode("div",Ul,[h.createElementVNode("span",Fl," Computed duration: "+h.toDisplayString(fe.value)+" d. ",1)]),h.createElementVNode("button",{onClick:vn,class:"bg-blue-600 hover:bg-blue-700 text-gray-500 text-xs font-bold py-1 px-3 rounded-md transition duration-150"}," Validate "),h.createElementVNode("button",{onClick:m[8]||(m[8]=w=>b.value=null),class:"absolute top-1 right-1 text-gray-500 hover:text-gray-800 text-xs"}," × ")],4)):h.createCommentVNode("",!0)]))}},[["__scopeId","data-v-a0ba9fd3"]]);return{install(t){t.component("GanttChart",Il)}}}); diff --git a/geos-trame/src/geos/trame/app/gantt_chart/module/serve/style.css b/geos-trame/src/geos/trame/app/gantt_chart/module/serve/style.css new file mode 100644 index 000000000..5c549cc51 --- /dev/null +++ b/geos-trame/src/geos/trame/app/gantt_chart/module/serve/style.css @@ -0,0 +1 @@ +.dragging[data-v-a0ba9fd3]{opacity:.7;filter:brightness(1.2)}.gantt-edit-form[data-v-a0ba9fd3]{box-sizing:border-box;background-color:#ffffffe6;position:fixed;z-index:900;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;min-width:300px;max-width:90vw}.form-title[data-v-a0ba9fd3]{font-size:1.5rem;color:#333;margin-top:0;padding-bottom:5px;border-bottom:2px solid #ddd;background-color:#7a7a7ae6}.g text[data-v-a0ba9fd3]{font-size:10px}.task-group[data-v-a0ba9fd3]{transform-origin:0 0} diff --git a/geos-trame/src/geos/trame/app/gantt_chart/widgets/gantt_chart.py b/geos-trame/src/geos/trame/app/gantt_chart/widgets/gantt_chart.py new file mode 100644 index 000000000..36d2ec9f6 --- /dev/null +++ b/geos-trame/src/geos/trame/app/gantt_chart/widgets/gantt_chart.py @@ -0,0 +1,35 @@ +from trame_client.widgets.core import AbstractElement +from .. import module + +__all__ = [ + "Gantt", +] + + +#will eventually be a dependency, so we'll skip some type checks +class HtmlElement( AbstractElement ): + + def __init__( self, _elem_name, children=None, **kwargs ) -> None: # noqa + super().__init__( _elem_name, children, **kwargs ) # noqa + if self.server: + self.server.enable_module( module ) + + +class Gantt( HtmlElement ): + """Gantt Editor component. + + Properties: + tasks + availableCategoriesList + + Emit: + taskUpdated. + """ + + def __init__( self, **kwargs ) -> None: #noqa + super().__init__( + "GanttChart", + **kwargs, + ) + self._attr_names += [ "tasks", "availableCategoriesList" ] + self._event_names += [ "taskUpdated" ] diff --git a/geos-trame/src/geos/trame/app/ui/inspector.py b/geos-trame/src/geos/trame/app/ui/inspector.py index fc7a70c09..aea563ea4 100644 --- a/geos-trame/src/geos/trame/app/ui/inspector.py +++ b/geos-trame/src/geos/trame/app/ui/inspector.py @@ -63,7 +63,6 @@ def _on_change( topic: str, ids: list | None = None ) -> None: if ids is not None and topic == "changed": for obj_id in ids: proxy = self.simput_manager.proxymanager.get( obj_id ) - #self.tree.decode( obj_id ) # if const function and return not used why ?? to decode context ?? for prop in proxy.edited_property_names: self.tree.update( obj_id, text.camel_case( prop ), proxy.get_property( prop ) ) diff --git a/geos-trame/src/geos/trame/app/ui/timeline.py b/geos-trame/src/geos/trame/app/ui/timeline.py index d6961c0ed..459780b4c 100644 --- a/geos-trame/src/geos/trame/app/ui/timeline.py +++ b/geos-trame/src/geos/trame/app/ui/timeline.py @@ -1,14 +1,22 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Lionel Untereiner +# SPDX-FileContributor: Lionel Untereiner, Jacques Franc from typing import Any +from datetime import datetime, timedelta +import pytz +import logging -from trame.widgets import gantt +from geos.trame.app.gantt_chart.widgets.gantt_chart import Gantt from trame.widgets import vuetify3 as vuetify from trame_simput import get_simput_manager +from geos.trame.schema_generated.schema_mod import PeriodicEvent from geos.trame.app.deck.tree import DeckTree +date_fmt = "%Y-%m-%d" +logger = logging.getLogger( "timeline" ) +logger.setLevel( logging.ERROR ) + class TimelineEditor( vuetify.VCard ): @@ -18,73 +26,113 @@ def __init__( self, source: DeckTree, **kwargs: Any ) -> None: self.tree = source self.simput_manager = get_simput_manager( id=self.state.sm_id ) + self.state.sdate = None #Timestamp(self.tree.world_origin_time) + self.state.change( "sdate" )( self._updated_sdate ) + self.state.tasks = self.tree.timeline() + self.state.regList = list( self.tree.registered_targets.keys() ) - items = self.tree.timeline() - - fields = [ { - "summary": { - "label": "Summary", - "component": "gantt-text", - "width": 300, - "placeholder": "Add a new task...", - }, - "start_date": { - "label": "Start", - "component": "gantt-date", - "width": 75, - "placeholder": "Start", - "sort": "date", - }, - "end_date": { - "label": "End", - "component": "gantt-date", - "width": 75, - "placeholder": "End", - "sort": "date", - }, - "duration": { - "label": "Days", - "component": "gantt-number", - "width": 50, - "placeholder": "0", - }, - } ] - - with self: - vuetify.VCardTitle( "Events View" ) - vuetify.VDateInput( - label="Select starting simulation date", - prepend_icon="", - prepend_inner_icon="$calendar", - placeholder="09/18/2024", - ) - vuetify.VDivider() - with ( - vuetify.VContainer( "Events timeline" ), - vuetify.VTimeline( - direction="horizontal", - truncate_line="both", - align="center", - side="end", - ), - vuetify.VTimelineItem( v_for=( f"item in {items}", ), key="i", value="item", size="small" ), - ): - vuetify.VAlert( "{{ item.summary }}" ) - vuetify.Template( "{{ item.start_date }}", raw_attrs=[ "v-slot:opposite" ] ) - - with vuetify.VContainer( "Events chart" ): - gantt.Gantt( - canEdit=True, - dateLimit=30, - startDate="2024-11-01 00:00", - endDate="2024-12-01 00:00", - # title='Gantt-pre-test', - fields=fields, - update=( self.update_from_js, "items" ), - items=( "items", items ), + with self: #noqa + with vuetify.VContainer( "Events chart" ): #noqa + vuetify.VDateInput( + label="Select starting simulation date", + prepend_icon="", + prepend_inner_icon="$calendar", + v_model=( "sdate", ), + ) + vuetify.VDivider() + Gantt( + tasks=( "tasks", ), + availableCategoriesList=( "regList", ), + taskUpdated=( self._updated_tasks, "$event" ), classes="fill_height", ) - def update_from_js( self, *items: tuple ) -> None: - """Update method called from javascript.""" - self.state.items = list( items ) + #use to refect change in simput to gantt + # def _on_change( topic: str, ids: list | None = None ) -> None: + # if ids is not None and topic == "changed": + # print("blablabla") + + # self.simput_manager.proxymanager.on( _on_change ) + + def _updated_tasks( self, *tasks: Any, **_: Any ) -> None: + + rm_list = ( { t_id + for t in self.state.tasks if ( t_id := t.get( "id" ) ) is not None } - + { t_id + for t in tasks if ( t_id := t.get( "id" ) ) is not None } ) + + self.state.tasks = tasks + former_origin_time: datetime = datetime.strptime( + min( self.state.tasks, key=lambda d: datetime.strptime( d.get( "start" ), date_fmt ) ).get( "start" ), + date_fmt ) + #update and erase + for t in self.state.tasks: + start_time = ( datetime.strptime( t[ "start" ], date_fmt ) - former_origin_time ).total_seconds() + end_time = ( datetime.strptime( t[ "end" ], date_fmt ) - former_origin_time ).total_seconds() + + #negative events + if ( start_time < 0 or end_time < 0 ): + continue + + event = { + "begin_time": f"{ start_time: .6e}", + "end_time": f"{ end_time: .6e}", + "name": t[ "name" ], + "category": t[ "category" ] + } + + #if added Event then + if not self.tree._search( f'Problem/Events/0/PeriodicEvent/{t["id"]}' ): + self.tree.input_file.pb_dict[ 'Problem' ][ 'Events' ][ 0 ][ 'PeriodicEvent' ].append( + self.tree.encode_data( PeriodicEvent( name="test" ) ) ) + proxy = self.simput_manager.proxymanager.create( proxy_type='PeriodicEvent', + proxy_id=f'Problem/Events/0/PeriodicEvent/{t["id"]}', + initial_values=self.tree.encode_data( + PeriodicEvent( name="test" ) ) ) + else: + proxy = self.simput_manager.proxymanager.get( f'Problem/Events/0/PeriodicEvent/{t["id"]}' ) + + self.tree.update( f'Problem/Events/0/PeriodicEvent/{t["id"]}', 'beginTime', event[ 'begin_time' ] ) + proxy.set_property( "begin_time", event[ 'begin_time' ] ) + self.tree.update( f'Problem/Events/0/PeriodicEvent/{t["id"]}', 'endTime', event[ 'end_time' ] ) + proxy.set_property( "end_time", event[ 'end_time' ] ) + self.tree.update( f'Problem/Events/0/PeriodicEvent/{t["id"]}', 'name', event[ 'name' ] ) + proxy.set_property( "name", event[ 'name' ] ) + self.tree.update( f'Problem/Events/0/PeriodicEvent/{t["id"]}', 'target', + self.tree.registered_targets[ event[ 'category' ] ] ) + proxy.set_property( "target", event[ 'category' ] ) + + if "freq" in t and t[ "freq" ] is not None: + self.tree.update( f'Problem/Events/0/PeriodicEvent/{t["id"]}', 'timeFrequency', + str( timedelta( days=int( t[ "freq" ] ) ).total_seconds() ) ) + proxy.set_property( "time_frequency", str( timedelta( days=int( t[ "freq" ] ) ).total_seconds() ) ) + proxy.commit() + + self.ctrl.simput_reload_data() + + #remove lost indexes + for i in rm_list: + self.tree.drop( f'Problem/Events/0/PeriodicEvent/{i}' ) + #drop proxies as well + self.simput_manager.proxymanager.delete( proxy_id=f'Problem/Events/0/PeriodicEvent/{t["id"]}' ) + + return + + @staticmethod + def shift_str( dt_str: str, time_delta: timedelta ) -> str: + """Helper function for shifting time.""" + return ( datetime.strptime( dt_str, date_fmt ) + time_delta ).strftime( date_fmt ) + + def _updated_sdate( self, sdate: Any, **_: Any ) -> None: + #sdate seems to some sort of panda Timestamp + if sdate is not None: + former_origin_time: str = min( + self.state.tasks, key=lambda d: datetime.strptime( d.get( "start" ), date_fmt ) ).get( "start" ) + time_delta: timedelta = sdate.to_datetime() - pytz.utc.localize( + datetime.strptime( former_origin_time, date_fmt ) ) + self.state.tasks = [ { + **d, "start": TimelineEditor.shift_str( d[ "start" ], time_delta ), + "end": TimelineEditor.shift_str( d[ "end" ], time_delta ) + } for d in self.state.tasks ] + + return diff --git a/geos-trame/vue-components/package.json b/geos-trame/vue-components/package.json index b4e57be70..ac0290691 100644 --- a/geos-trame/vue-components/package.json +++ b/geos-trame/vue-components/package.json @@ -17,10 +17,9 @@ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore --ignore-pattern public", "semantic-release": "semantic-release" }, - "dependencies": { - }, - "peerDependencies": { - "vue": "^2.7.0 || >=3.0.0" + "dependencies":{ + "vue":"^3.4.27", + "d3":"^7.9.0" }, "devDependencies": { "@rushstack/eslint-patch": "^1.1.4", @@ -29,8 +28,8 @@ "eslint-plugin-vue": "^9.3.0", "prettier": "^2.7.1", "semantic-release": "19.0.2", - "vite": "^4.1.0", - "vue": "^3.0.0" + "vite": "^5.2.12", + "vue": "^3.4.27" }, "files": [ "dist/*",