From 9d6515556aba536bbb74d1756155fea8c4637d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AA=CC=83n=20Anh=20=C4=90=C6=B0=CC=81c?= Date: Tue, 31 Dec 2024 16:54:02 +0700 Subject: [PATCH 1/4] version 1.0.0 --- MANIFEST.in | 7 ++ PKG-INFO | 63 +++++++++++ netbox_excel.egg-info/PKG-INFO | 62 +++++++++++ netbox_excel.egg-info/SOURCES.txt | 27 +++++ netbox_excel.egg-info/dependency_links.txt | 1 + netbox_excel.egg-info/requires.txt | 8 ++ netbox_excel.egg-info/top_level.txt | 1 + netbox_excel/__init__.py | 16 +++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 700 bytes .../__pycache__/forms.cpython-310.pyc | Bin 0 -> 732 bytes .../__pycache__/models.cpython-310.pyc | Bin 0 -> 1611 bytes .../__pycache__/navigation.cpython-310.pyc | Bin 0 -> 158 bytes .../template_content.cpython-310.pyc | Bin 0 -> 887 bytes netbox_excel/__pycache__/urls.cpython-310.pyc | Bin 0 -> 381 bytes .../__pycache__/views.cpython-310.pyc | Bin 0 -> 2440 bytes netbox_excel/api/__init__.py | 0 .../api/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 160 bytes .../__pycache__/serializers.cpython-310.pyc | Bin 0 -> 994 bytes .../api/__pycache__/urls.cpython-310.pyc | Bin 0 -> 412 bytes .../api/__pycache__/views.cpython-310.pyc | Bin 0 -> 1006 bytes netbox_excel/api/serializers.py | 24 +++++ netbox_excel/api/urls.py | 11 ++ netbox_excel/api/views.py | 20 ++++ netbox_excel/export/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 276 bytes .../__pycache__/devices.cpython-310.pyc | Bin 0 -> 384 bytes .../export/__pycache__/export.cpython-310.pyc | Bin 0 -> 601 bytes netbox_excel/export/devices.py | 6 ++ netbox_excel/export/export.py | 14 +++ netbox_excel/forms.py | 14 +++ netbox_excel/migrations/0001_initial.py | 37 +++++++ netbox_excel/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-310.pyc | Bin 0 -> 1383 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 167 bytes netbox_excel/models.py | 35 ++++++ netbox_excel/navigation.py | 0 netbox_excel/tables.py | 0 netbox_excel/template_content.py | 12 +++ .../templates/netbox_excel/export_excel.html | 66 ++++++++++++ .../templates/netbox_excel/import_excel.html | 44 ++++++++ .../import_excel_console_log.html | 2 + netbox_excel/urls.py | 8 ++ netbox_excel/views.py | 101 ++++++++++++++++++ pyproject.toml | 62 +++++++++++ setup.cfg | 4 + 45 files changed, 647 insertions(+) create mode 100644 MANIFEST.in create mode 100644 PKG-INFO create mode 100644 netbox_excel.egg-info/PKG-INFO create mode 100644 netbox_excel.egg-info/SOURCES.txt create mode 100644 netbox_excel.egg-info/dependency_links.txt create mode 100644 netbox_excel.egg-info/requires.txt create mode 100644 netbox_excel.egg-info/top_level.txt create mode 100644 netbox_excel/__init__.py create mode 100644 netbox_excel/__pycache__/__init__.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/forms.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/models.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/navigation.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/template_content.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/urls.cpython-310.pyc create mode 100644 netbox_excel/__pycache__/views.cpython-310.pyc create mode 100644 netbox_excel/api/__init__.py create mode 100644 netbox_excel/api/__pycache__/__init__.cpython-310.pyc create mode 100644 netbox_excel/api/__pycache__/serializers.cpython-310.pyc create mode 100644 netbox_excel/api/__pycache__/urls.cpython-310.pyc create mode 100644 netbox_excel/api/__pycache__/views.cpython-310.pyc create mode 100644 netbox_excel/api/serializers.py create mode 100644 netbox_excel/api/urls.py create mode 100644 netbox_excel/api/views.py create mode 100644 netbox_excel/export/__init__.py create mode 100644 netbox_excel/export/__pycache__/__init__.cpython-310.pyc create mode 100644 netbox_excel/export/__pycache__/devices.cpython-310.pyc create mode 100644 netbox_excel/export/__pycache__/export.cpython-310.pyc create mode 100644 netbox_excel/export/devices.py create mode 100644 netbox_excel/export/export.py create mode 100644 netbox_excel/forms.py create mode 100644 netbox_excel/migrations/0001_initial.py create mode 100644 netbox_excel/migrations/__init__.py create mode 100644 netbox_excel/migrations/__pycache__/0001_initial.cpython-310.pyc create mode 100644 netbox_excel/migrations/__pycache__/__init__.cpython-310.pyc create mode 100644 netbox_excel/models.py create mode 100644 netbox_excel/navigation.py create mode 100644 netbox_excel/tables.py create mode 100644 netbox_excel/template_content.py create mode 100644 netbox_excel/templates/netbox_excel/export_excel.html create mode 100644 netbox_excel/templates/netbox_excel/import_excel.html create mode 100644 netbox_excel/templates/netbox_excel/import_excel_console_log.html create mode 100644 netbox_excel/urls.py create mode 100644 netbox_excel/views.py create mode 100644 pyproject.toml create mode 100644 setup.cfg diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..cb04ac8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE +include README.md +include SECURITY.md +graft netbox_excel +prune netbox_excel/tests +global-exclude *.py[cod] +global-exclude __pycache__ diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..d6c6182 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,63 @@ +Metadata-Version: 2.1 +Name: netbox-excel +Version: 1.0.0 +Summary: Import device from file excel Netbox Plugin +Home-page: https://github.com/hocchudong/netbox-excel +Author-email: ducna +License: Apache-2.0 +Classifier: Environment :: Web Environment +Classifier: Framework :: Django +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Requires-Python: >=3.10.12 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: build +Requires-Dist: build==1.2.1; extra == "build" +Requires-Dist: setuptools==70.3.0; extra == "build" +Requires-Dist: twine==5.1.1; extra == "build" +Provides-Extra: tools +Requires-Dist: ruff==0.5.1; extra == "tools" + + +## Install Require + +netbox version >= 4.0 + +## Known Issues + +- WARNING: This plugin is only tested with a single NetBox version at this time. + +## Installation Guide + +### In mono service: + +To install the plugin, first using pip and install netbox-excel: + + ``` + cd /opt/netbox + source venv/bin/activate + pip install netbox-excel + ``` + +Next, enable the plugin in /opt/netbox/netbox/netbox/configuration.py, or if you have a /configuration/plugins.py file, the plugins.py file will take precedence. + + ``` + PLUGINS = [ + 'netbox_excel' + ] + ``` +Then you may need to perform the final step of restarting the service to ensure that the changes take effect correctly: + + ``` + python netbox/manage.py migrate netbox_excel + sudo systemctl restart netbox + ``` diff --git a/netbox_excel.egg-info/PKG-INFO b/netbox_excel.egg-info/PKG-INFO new file mode 100644 index 0000000..3d51196 --- /dev/null +++ b/netbox_excel.egg-info/PKG-INFO @@ -0,0 +1,62 @@ +Metadata-Version: 2.1 +Name: netbox-excel +Version: 1.0.0 +Summary: Netbox Excel Netbox Plugin +Author-email: ducna +License: Apache-2.0 +Classifier: Environment :: Web Environment +Classifier: Framework :: Django +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Requires-Python: >=3.10.12 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: build +Requires-Dist: build==1.2.1; extra == "build" +Requires-Dist: setuptools==70.3.0; extra == "build" +Requires-Dist: twine==5.1.1; extra == "build" +Provides-Extra: tools +Requires-Dist: ruff==0.5.1; extra == "tools" + + +## Install Require + +netbox version >= 4.0 + +## Known Issues + +- WARNING: This plugin is only tested with a single NetBox version at this time. + +## Installation Guide + +### In mono service: + +To install the plugin, first using pip and install netbox-excel: + + ``` + cd /opt/netbox + source venv/bin/activate + pip install netbox-excel + ``` + +Next, enable the plugin in /opt/netbox/netbox/netbox/configuration.py, or if you have a /configuration/plugins.py file, the plugins.py file will take precedence. + + ``` + PLUGINS = [ + 'netbox_excel' + ] + ``` +Then you may need to perform the final step of restarting the service to ensure that the changes take effect correctly: + + ``` + python netbox/manage.py migrate netbox_excel + sudo systemctl restart netbox + ``` diff --git a/netbox_excel.egg-info/SOURCES.txt b/netbox_excel.egg-info/SOURCES.txt new file mode 100644 index 0000000..7319f5b --- /dev/null +++ b/netbox_excel.egg-info/SOURCES.txt @@ -0,0 +1,27 @@ +LICENSE +MANIFEST.in +README.md +pyproject.toml +setup.cfg +netbox_excel/__init__.py +netbox_excel/forms.py +netbox_excel/models.py +netbox_excel/navigation.py +netbox_excel/tables.py +netbox_excel/template_content.py +netbox_excel/urls.py +netbox_excel/views.py +netbox_excel.egg-info/PKG-INFO +netbox_excel.egg-info/SOURCES.txt +netbox_excel.egg-info/dependency_links.txt +netbox_excel.egg-info/requires.txt +netbox_excel.egg-info/top_level.txt +netbox_excel/api/__init__.py +netbox_excel/api/serializers.py +netbox_excel/api/urls.py +netbox_excel/api/views.py +netbox_excel/migrations/0001_initial.py +netbox_excel/migrations/__init__.py +netbox_excel/templates/netbox_excel/export_excel.html +netbox_excel/templates/netbox_excel/import_excel.html +netbox_excel/templates/netbox_excel/import_excel_console_log.html \ No newline at end of file diff --git a/netbox_excel.egg-info/dependency_links.txt b/netbox_excel.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/netbox_excel.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/netbox_excel.egg-info/requires.txt b/netbox_excel.egg-info/requires.txt new file mode 100644 index 0000000..d0aab00 --- /dev/null +++ b/netbox_excel.egg-info/requires.txt @@ -0,0 +1,8 @@ + +[build] +build==1.2.1 +setuptools==70.3.0 +twine==5.1.1 + +[tools] +ruff==0.5.1 diff --git a/netbox_excel.egg-info/top_level.txt b/netbox_excel.egg-info/top_level.txt new file mode 100644 index 0000000..7afcf29 --- /dev/null +++ b/netbox_excel.egg-info/top_level.txt @@ -0,0 +1 @@ +netbox_excel diff --git a/netbox_excel/__init__.py b/netbox_excel/__init__.py new file mode 100644 index 0000000..12a25d9 --- /dev/null +++ b/netbox_excel/__init__.py @@ -0,0 +1,16 @@ +from netbox.plugins import PluginConfig + +__version__ = "1.0.0" + +class NetboxExcelConfig(PluginConfig): + name = "netbox_excel" + verbose_name = "Netbox excel" + description = "Import object from file excel" + version = __version__ + author = "ducna" + author_email = "ducna@hcd.com.vn" + base_url = "netbox-excel" + required_settings = [] + default_settings = {"version_info": False} + +config = NetboxExcelConfig diff --git a/netbox_excel/__pycache__/__init__.cpython-310.pyc b/netbox_excel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecd767649328c7b2f7b86acf68a861404e3d47ca GIT binary patch literal 700 zcmY*Xy^ho{5VrHTx!o+ma$M;uWZ{;~X3ld1k(u@i+0+cw$pAI~f$by|+>bX^b05_<`1Y-mN z3Em?JA^L_OV)$!FLKcz;;!kLk{Ib<(gH86a8EW2LiLT=HDSnw>dW{`4BGB~sS=H`6Q^g-Ody)9YeyP2#T zLzKDLDf0zukDh&Kdm;5&96qyBuPZ6qb;TRz(dOZ})l!=@j_a1~|W4&1bQ>Em+wrplW z={+$uv&l6{G3cX^Ci9lEw&0CP4~69zWMj@HyB@e?Ko!%PceOGrU{x_R`fub8$;Woa zqx$`3D|)^4L%KKpcwH0nr<{Nv6LUhE|*R@4wIDQ`}p^7-h1M5>1#j6OZz&JO0CLJ6s&@D@dvHB#*+X5 literal 0 HcmV?d00001 diff --git a/netbox_excel/__pycache__/forms.cpython-310.pyc b/netbox_excel/__pycache__/forms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f507b00de943c34ac2fdcdf751897a599e909073 GIT binary patch literal 732 zcmZuv%Wl*#6!j|)Iw_S3Vm)Fv3)&y3P*K%E-As`NvB<_sB{dnhEw7%e5k47aT0Dd&WR z6{SoiXK$I2Mtx&O0birbo9 z4l|Cl{gY{7VOoGSUR@&m#T?nur$0xBb<-VI&|TE)hfiZyi=x`FS3tMk5IvB-uY=~w zk#PY=OCV+m?<@bb_~`FTT=vkS;YKV(DnTs+kmT)mk%lI~hC{!1x_)$w8b%LfX04ZR ztVNvUE9ao<^ydP;d`(UUHvT~D=^QNN-8iep-Bkm)I?6xRMGmbGY<2;5Hi;Zv+J%%N zOg=h)_x|jBHXjTdo%HKBmXXL`!v-{>oe8EGqa`@@k|?^c)`%bN}&V(1Rz~G(?h53 z)#pA#?tL0ud-5yv((VGHE2Yq2x!+>7!0vuPJD<-Oj=Mkf=7$Mme-YRp5rU^Ub%#PQ z;Yn6|x#yAYOTP~C!0Lev>nM+`9?D4_=dslznbgyKYW0bn)oGr3>^ml6k^Ig?0x_g_ zVV3-Tk8zGCOu1MbSsJu|6W0aBwDE4Trr0o9o<)}VV;H5)|f1@zyx z+n3~saPgB}Dtiz=4xnydL66qyWwFsOt8LYk6nTFuXYHzjaTs#WI64)wR)hbGnH9?ol~ zit^1)U(`i2tc$WA6sVmo6`)I1b$!(~>Lc9N)@M&piYh@d#i_|7VVReCM~}Y~FY~R( z|2P_=_fz`YpC~@yD6!PHEG5P|G_edDkz?(M9BW79SUV!e+7UU{j>xfgMDv3kjtMKwt8aOn(*>}ccm&iileIrL*cZ2Mj?1>`F<3uFnrl#1AxAjIq4t}_F6dq}&Eggc#oINn znj3suYQS*B{XJoO`vux46a6`gg#FjoeQn_>@*_S^#dXna+NHR1v`u$-P*SS^osg%_ zW9Tb15edxPUA3jVLW&+Qb4u%26wu4J^yF#iwW8Z=&JMhnYbc1aTE)0%nPwvUZrU^L fPuyMjXY+pnhP9%N literal 0 HcmV?d00001 diff --git a/netbox_excel/__pycache__/navigation.cpython-310.pyc b/netbox_excel/__pycache__/navigation.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..426536b1a23e1b59eef79108c0e77777c41530ba GIT binary patch literal 158 zcmd1j<>g`k0=sh==^*+sh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vJKfj890_qdo2Ydr#C=e@kEHJ9Ef>)qm86QR4!SN2c*Nb_tJBdW zCrx2Ae=!9eV&+x#H5J&_92`qabG?J-^e#D4x^eEQ)PvjK$6ztos{dRc{bqCf1Kq>^Gx|n<*MiqBteoiTY?WJb3znP3wWK1iiBEzHcV+fqp&HMHjIHeh~J; EN9!5X+W-In literal 0 HcmV?d00001 diff --git a/netbox_excel/__pycache__/urls.cpython-310.pyc b/netbox_excel/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f2289eea17cb4b442a1da831477a7f84f3b7d58 GIT binary patch literal 381 zcmYk2u};G<5Qfijk_O_oZ@|zk1I+_a5K9LZ7NibYs!*Gd%84TYDl5&HPWq z*pHiaLf7pFXog+ReP5-j>C$oJUua}sl=YD=oz4+P(bO)pY~DYj4ef Kkr|zl=;s$C4sLY- literal 0 HcmV?d00001 diff --git a/netbox_excel/__pycache__/views.cpython-310.pyc b/netbox_excel/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd99babcbf72f33532f25b6db9c29c4203ab5aec GIT binary patch literal 2440 zcmZWrOOM<{5O&)hkH_PA?1NdM|#r#3(It@Zbeg2f-B2{#*#N(9li<>@;E~P9M zor{}?24Lgnfgup*7`>IlCYkGJY|Z8Ah$cNvSOd!4fNq376&977xLy>AxvKBy3=HJ% zMo4d8_3`-qp9-BCv}I`7TR;kB9S|LNHAA&(=WuYte4nlNI@hiSkJIGMG8 zh}xwqA6;BwXcs_KM}KL?SsUb^6*dd(zw=A!m@k#5?Xz)|55lC9&Xpz{vIkwy7ROtM zP`?R-7;Bjc#8SJ&fX3!QI0VuR3_u{3ApHjLJqPJ;PIlD$082sEwW%7Yh3e`92)v)j zR4de-TA|&Cz#J%3of`mOt*Bj4zJ4$@0)1MCW_8c5$fxyNZ&w-vG}kH(@TvJ+QwPnLl?M2<0UE0?)?q)%?e*3+ zIjRKaf--IKHfTD~y3lNBGy8L)KMQTHs266@c(Jl*1&zD*v|Z@nWgEQ&i`wfQ*d6(N z#ql(hU#a+!iXW}`LdB0&+^+cXil3#|v~yL))}(pCz+T#G4xjU9^G?3%a1_!=H8<_ z4rh;lbyoLwf9KAVdwaVN^BiO1g=)07`)Z>6#NJ8}MXec<__Xa++}F`P@nJ zc$G4SZyjBSv5{z&DZ^#(HlGW8Yy3h;yqF4eoMs^p(?ql~%0e%4%gw}WwLYMk&%&{s zVoPK8lhN!J*yN^b?hBhl2eN^*X@46t-|y9O7xL(2!5fdG(D!h|rHv$5jI;7dLk6^Q zHRdw3bF%1h?)gLX^_{gaqG<2CXDYL*o|()c9x(abg%C1y>D8pY6!>Ah zRIbXFdJe@3FfT3V8!@Cv7F$w#5#?#nc z3uzQ&?&_91U}-)UwUE;|6B=YPp@rc7DOkZy$YXMMNW;O9GYkoK499eNBf3Q2C^5(T zd5$uB9jCs5L$^1>FldE&d*7>CHl&n``jC21 z#4@aASsrm=SCF_-m~mm0(*CF`FSPTV38A+ z6v~rLI8YW$nczz@hkG8Zh?uM~|7T+$mTDRXF^H*IMAr=6RCQG+Ix)#Sd`8uDqJDc^ zRi&oXB30#Q0X3dkT~jw<)ytp$%x+tnNi0}x>I@5HZ%%ZBbuSsDi`fu-@N=HYN(Glw z4iBODOS~o0qK0KIE2~=_{h2#hlNMuq^_jePakAQ>tWyE?Q|9rMWs6woS>xr!C5$rk zSQFIDM)D>SOjgq118^d8Ok!%Dhja6YzK8zg`k0yoK`bP)X*L?8o3AjbiSi&=m~3PUi1CZpdPg1$kdOI&&Q9&|K`rp|Pk_Es|g$MlJwFm6JU^gU- zL;xy6H}1ji`TirIw*-&>A;_4ZSs+?ifMeU!gK(1z+2m>Dyiz!}$}8yxLfXQVbyMn< zV@xA>q>s)m^2Ay*OY)a+Amd3hdZc4f6NbOHzju8~YpYy4%o9Rk6bG^C!)M~vf zSG(+)L@#y;?~=SOr#(X_^_64zcx)+)T4TZ}qwzg6hNOW0x*NF0GQWmt@qu6IHv&ba zvsO>)IcB9=IA6~{?q!-2IOzsU*>F+XTIE-d@IHctbo=D69lg`DG&VyxY(^OFW5By^ zyl)23|3Qm$w8cd`l2*RKX6Q+2YlnJF00HgUJvpW$FB!If(W{Y>;O4@BUthHO)Db@H tDm{S;PI?tI*xK&6+w6)wX&tn!oh{}e2`+xob-_!lX{#e4iI>n5|1XcR`H27k literal 0 HcmV?d00001 diff --git a/netbox_excel/api/__pycache__/urls.cpython-310.pyc b/netbox_excel/api/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5103a3a038e21fbd82c8efbd6cc81af6c161fbf3 GIT binary patch literal 412 zcmYjNy-ve05VjL1gfx**2M|I$Kn9W*K;=hb$WozJV#rbzazsH%Y}pQ!nHMP|ujG}9 zS773t6oe(8{e9ovcW3iB4hgQ~6Iq>7LOw0{zZil`+-ioQBqeP~OAAWv-D#ZGE!>tB zj1r)4r0~E6Z{>rx-78&S?+)_bQ&8-eY)KOD3q43eO*996%{L3)YmkX!ju7AT&CTWk zn!J1D3(oaj4KHSzw}MMO2$ooaHc>rrE3nz~Bs9}*s7}|SPUT>!q=Svj;V6umgZzvh zO1daY`!M5=`DSu;oBuTpBviG+$qegdqs#=OSZgFY^+9B8t8Mtc$hgqi*yQ&RO<&dB p4xJ4%?!Y)khErzNHWjZXIB$8`H!#DWCn~}akpt>`K8>7^{s0&@cW(dy literal 0 HcmV?d00001 diff --git a/netbox_excel/api/__pycache__/views.cpython-310.pyc b/netbox_excel/api/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4620948ad093ac3208211d41d8b46e00b3d378c GIT binary patch literal 1006 zcmZ`%O^?$s5Vhl^NwcX~l~1iW^ZoUfKV^ z6^{HRUpaB-#EBUv-7V6tB~RvgJd-zXtUH~Q!1{J%PX16rej(UjA%GEV`w@&Ff+|wd zoKo}y6_hMz-e)Q-qdf9{s9I&5$CSJ!A`-1jB3hDuWqATP775@)GQe%oo|1n08!D4N zbq6osk3VQ#eOkzKfZd6#Ug^y{Eu_MusjT4i{Dq?$^zDAZyr4QB5JW>|55 z1pr2{?ISRfzwv776#soV%3sQj4{c z;AfnltqX-^0mYmPJ>%TqBf9RO_S$&X0)s(|P67izJUDy+S+ z0TizbyJ;Y$2{m{&>8I|2k+$M<17e(O^Lb$Ox{}5k)Y{#x2RMMi4E$hZWevl~<2q6g u&d0Br*qU3D+*|?mrLw*yFPiAm# literal 0 HcmV?d00001 diff --git a/netbox_excel/api/serializers.py b/netbox_excel/api/serializers.py new file mode 100644 index 0000000..0efdb8e --- /dev/null +++ b/netbox_excel/api/serializers.py @@ -0,0 +1,24 @@ +from rest_framework import serializers + +from netbox.api.serializers import NetBoxModelSerializer +from netbox_excel.models import ImportExcel + + +class ImportExcelSerializer(NetBoxModelSerializer): + display = serializers.SerializerMethodField() + + class Meta: + model = ImportExcel + fields = ( + "id", + "name", + "description", + "tags", + "custom_field_data", + "created", + "last_updated", + ) + brief_fields = ("id", "name", "description") + + def get_display(self, obj): + return f"{obj}" \ No newline at end of file diff --git a/netbox_excel/api/urls.py b/netbox_excel/api/urls.py new file mode 100644 index 0000000..1a3864b --- /dev/null +++ b/netbox_excel/api/urls.py @@ -0,0 +1,11 @@ +from netbox.api.routers import NetBoxRouter +from netbox_excel.api.views import ( + NetboxExcelInfoRootView, + ImportExcelViewSet, +) + +router = NetBoxRouter() +router.APIRootView = NetboxExcelInfoRootView + +router.register("import_excel", ImportExcelViewSet) +urlpatterns = router.urls diff --git a/netbox_excel/api/views.py b/netbox_excel/api/views.py new file mode 100644 index 0000000..c2b7e37 --- /dev/null +++ b/netbox_excel/api/views.py @@ -0,0 +1,20 @@ +from rest_framework.routers import APIRootView + +from netbox.api.viewsets import NetBoxModelViewSet +from netbox_excel.api.serializers import ( + ImportExcelSerializer, +) +from netbox_excel.models import ImportExcel + + +class NetboxExcelInfoRootView(APIRootView): + """ + Netbox Excel API root view + """ + + def get_view_name(self): + return "Netbox Excel" + +class ImportExcelViewSet(NetBoxModelViewSet): + queryset = ImportExcel.objects.all() + serializer_class = ImportExcelSerializer \ No newline at end of file diff --git a/netbox_excel/export/__init__.py b/netbox_excel/export/__init__.py new file mode 100644 index 0000000..fdffb01 --- /dev/null +++ b/netbox_excel/export/__init__.py @@ -0,0 +1,2 @@ +from .devices import get_device +from .export import export_all , export_current_view diff --git a/netbox_excel/export/__pycache__/__init__.cpython-310.pyc b/netbox_excel/export/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ca47f5bbb7ca265ba330beb10a757bfc80e7a17 GIT binary patch literal 276 zcmYk0u?~VT5QbYoF+?VP09OX(1&ncWbTKY0jnWHgQYa|}@rjJD)YZvXaPk^3@sj)R z|B^pLyPa)I#ajtG_jTjW%*m2|0q2BDMgRZ+ literal 0 HcmV?d00001 diff --git a/netbox_excel/export/__pycache__/devices.cpython-310.pyc b/netbox_excel/export/__pycache__/devices.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9278b9bd785abd657f001e1be8cd32aa1ce3750d GIT binary patch literal 384 zcmYjNu};G<5IsAgO+&gP{vbn}A3z8RSi7)wi5lCAQk*!lU6dl&_y{DHc4Xm$yfX0% zoj9kd)RXSrJ>ApY)2Zn+28^FiQ+{Fn_2fJ#k~>Uug}{Lm2{l;~0-W;T1D2GBPmo^x z;S^HhMi1hpVuE>k3W80Dd5>wX5iG39j_%bgj}h8&=KRt&2-x^CJ(u^$|i$5)u;af3`>icga~*&uhhnG`-7y(z`f=394Om RgR+ejxBeZ*|098f{sY{`Tb2L- literal 0 HcmV?d00001 diff --git a/netbox_excel/export/__pycache__/export.cpython-310.pyc b/netbox_excel/export/__pycache__/export.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9efc868a260609ddc48e82a5241988ec3581b36f GIT binary patch literal 601 zcmZ`$yH3L}6t&Z|aUMlTdhnS4 zXFl|HKFmhILmo6FBTblva6h8KE6O5DXb2oryhdsC4vuJhc*zku3{>D2bBiCTQ;q1n)xjO?OD8UIP_y CG>FUq literal 0 HcmV?d00001 diff --git a/netbox_excel/export/devices.py b/netbox_excel/export/devices.py new file mode 100644 index 0000000..054634c --- /dev/null +++ b/netbox_excel/export/devices.py @@ -0,0 +1,6 @@ +from dcim.models import Device + +def get_device(): + devices = Device.objects.all() + devices = devices.order_by('rack', '-position') + return devices \ No newline at end of file diff --git a/netbox_excel/export/export.py b/netbox_excel/export/export.py new file mode 100644 index 0000000..fb1b782 --- /dev/null +++ b/netbox_excel/export/export.py @@ -0,0 +1,14 @@ +import openpyxl +from netbox_excel.models import ExportExcel +from .devices import get_device +import pandas as pd +# from io import StringIO +# import xlsxwriter +from django.http import HttpResponse , HttpResponseRedirect + +def export_all(): + + return + +def export_current_view(): + return \ No newline at end of file diff --git a/netbox_excel/forms.py b/netbox_excel/forms.py new file mode 100644 index 0000000..6cf6e1d --- /dev/null +++ b/netbox_excel/forms.py @@ -0,0 +1,14 @@ +from django import forms +from netbox_excel.models import ImportExcel + + +class ImportExcelForm(forms.Form): + file = forms.FileField() + +class ExportExcelForm(forms.Form): + OPTIONS = [ + ('all', 'All'), + ('current_view', 'Current View'), + ] + type = forms.ChoiceField(choices=OPTIONS, label="Select an type") + # type = forms.CharField(label="type", max_length=100) \ No newline at end of file diff --git a/netbox_excel/migrations/0001_initial.py b/netbox_excel/migrations/0001_initial.py new file mode 100644 index 0000000..e924415 --- /dev/null +++ b/netbox_excel/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 5.0.9 on 2024-12-23 03:59 + +import taggit.managers +import utilities.json +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('extras', '0121_customfield_related_object_filter'), + ] + + operations = [ + migrations.CreateModel( + name='ImportExcel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), + ('name', models.CharField(max_length=128)), + ('deivce_error', models.CharField(blank=True, null=True)), + ('deivce_type_error', models.CharField(blank=True, null=True)), + ('deivce_role_error', models.CharField(blank=True, null=True)), + ('rack_error', models.CharField(blank=True, null=True)), + ('manufacturer_error', models.CharField(blank=True, null=True)), + ('description', models.CharField(blank=True, max_length=255, null=True)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/netbox_excel/migrations/__init__.py b/netbox_excel/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netbox_excel/migrations/__pycache__/0001_initial.cpython-310.pyc b/netbox_excel/migrations/__pycache__/0001_initial.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..281ac045eb78ba08d6b090915883a1089a609e65 GIT binary patch literal 1383 zcmZ8hOK&7K5boDZ_q;Q+yIJ1L326>Y5|DrcLJMIREmFc^bDE3gY1j0)wDjMEM(Dd9*dxp$G?WlnE9{0dFKXgz0-MM`UTG< z)x0W|=@u0Op;Ei|W}6{+j$QqO1Gs3*fcY3L$o!@C+_h04&J*V2*J0i3a2>E1B|Rre zt^>rqJKT48fVj_m7{D+exq##Vd*~@uohP2idNhMf)GuTRA zP1ctDgyF%tU1^gc%B|G^w}DIzgSpIYk{g#%h^qb2L1L zJsT=f4BS}>TArJj!J5`Wn;6OzOE1wSE#T%CG@Sc*HRuHqP-c2#h79;mDG-pdlJh{? zE71mMcl719-8R!!BrCBK^(9Tu8*4KwXj!jms%r_dl?@oERPwF8h&Q)3e%Qo#dKW0t zG*eg8hWEu8%`#xGG!$3;dlBXz>y51HY-0jlAsH)$WlA-=dxD-!*YTiCT)9x{bpF^R zgxItRF?~Yt3DkIMVupnLSX0sN#JuDh3u1}bLdlRY4oU{r=it~?@WehI8CuEk8=ifQ zwS8$xH=}Q{G-tek2KK&x{ljUqj2KU~>D8JG^bX2IxvEODf9$T4)3M4g+UoRAGQy{ayD(^k|OFI)66 z*h|_1(t!KF#KGa=;Sp)+SZpt){U0UR)xs^JAPA$|s1x|JV9($4_x&-ByKm;t{NV3s z65K|ACAZFYo|s9K&0?2~{1jc8@f|Mg`k0=)+r=^*+sh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vDKfjAwl9``ZtREkrnU`4-AFo$Xd5gm)H$SB` PC)EyQM==wSU||3N9Y!gs literal 0 HcmV?d00001 diff --git a/netbox_excel/models.py b/netbox_excel/models.py new file mode 100644 index 0000000..ee49271 --- /dev/null +++ b/netbox_excel/models.py @@ -0,0 +1,35 @@ +from django.db import models +from django.urls import reverse +from django.utils import safestring + +from netbox.models import NetBoxModel +from utilities.querysets import RestrictedQuerySet +from netbox.models.features import TagsMixin + +class ImportExcel(NetBoxModel,TagsMixin): + name = models.CharField(max_length=128) + file = models.FileField(upload_to='dcim/devices/import/excel') + deivce_error = models.CharField(null=True, blank=True) + deivce_type_error = models.CharField(null=True, blank=True) + deivce_role_error = models.CharField(null=True, blank=True) + rack_error = models.CharField(null=True, blank=True) + manufacturer_error = models.CharField(null=True, blank=True) + description = models.CharField(max_length=255, null=True, blank=True) + + def __str__(self): + return self.name + +class ExportExcel(models.Model): + rack = models.CharField(null=True, blank=True) + u_number = models.CharField(null=True, blank=True) + u_end = models.CharField(null=True, blank=True) + position = models.CharField(null=True, blank=True) + device_name = models.CharField(null=True, blank=True) + device_role = models.CharField(null=True, blank=True) # chủng loại + owner_device = models.CharField(null=True, blank=True) # Quản lý + contract_number = models.CharField(null=True, blank=True) # So HD + serial_number = models.CharField(null=True, blank=True) + device_type = models.CharField(null=True, blank=True) # Model + device_description = models.CharField(null=True, blank=True) + year_of_investment = models.CharField(null=True, blank=True) + \ No newline at end of file diff --git a/netbox_excel/navigation.py b/netbox_excel/navigation.py new file mode 100644 index 0000000..e69de29 diff --git a/netbox_excel/tables.py b/netbox_excel/tables.py new file mode 100644 index 0000000..e69de29 diff --git a/netbox_excel/template_content.py b/netbox_excel/template_content.py new file mode 100644 index 0000000..c66929f --- /dev/null +++ b/netbox_excel/template_content.py @@ -0,0 +1,12 @@ +from netbox.plugins import PluginTemplateExtension +class ImportExcelDevice(PluginTemplateExtension): + model = 'dcim.device' + def list_buttons(self): + return self.render('netbox_excel/import_excel.html') + +class ExportExcelDevice(PluginTemplateExtension): + model = 'dcim.device' + def list_buttons(self): + return self.render('netbox_excel/export_excel.html') + +template_extensions = [ExportExcelDevice] \ No newline at end of file diff --git a/netbox_excel/templates/netbox_excel/export_excel.html b/netbox_excel/templates/netbox_excel/export_excel.html new file mode 100644 index 0000000..032be44 --- /dev/null +++ b/netbox_excel/templates/netbox_excel/export_excel.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/netbox_excel/templates/netbox_excel/import_excel.html b/netbox_excel/templates/netbox_excel/import_excel.html new file mode 100644 index 0000000..efa665c --- /dev/null +++ b/netbox_excel/templates/netbox_excel/import_excel.html @@ -0,0 +1,44 @@ + + + + + + + + \ No newline at end of file diff --git a/netbox_excel/templates/netbox_excel/import_excel_console_log.html b/netbox_excel/templates/netbox_excel/import_excel_console_log.html new file mode 100644 index 0000000..519427d --- /dev/null +++ b/netbox_excel/templates/netbox_excel/import_excel_console_log.html @@ -0,0 +1,2 @@ + +

This is console log

\ No newline at end of file diff --git a/netbox_excel/urls.py b/netbox_excel/urls.py new file mode 100644 index 0000000..4278d81 --- /dev/null +++ b/netbox_excel/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from netbox_excel import views + +urlpatterns = [ + path('import/excel', views.ImportExcelView, name='import_file'), + path('export/excel', views.ExportExcelView, name='export_file'), + # path('dcim/devices/import-excel', CustomDeviceBulkImportView.as_view(), name='import_excel'), +] diff --git a/netbox_excel/views.py b/netbox_excel/views.py new file mode 100644 index 0000000..0be9f28 --- /dev/null +++ b/netbox_excel/views.py @@ -0,0 +1,101 @@ +from django.shortcuts import render +from django.http import HttpResponse, HttpResponseRedirect +from .forms import ImportExcelForm, ExportExcelForm +from netbox_excel.models import ExportExcel +from django.views.decorators.csrf import requires_csrf_token +from netbox_excel.export import get_device, export_all, export_current_view +import openpyxl + + +@requires_csrf_token +def ImportExcelView(request): + if request.method == 'POST': + form = ImportExcelForm(request.POST, request.FILES) + else: + form = ImportExcelForm() + return render(request, 'netbox_excel/import_excel_console_log.html', {'form': form}) + +@requires_csrf_token +def ExportExcelView(request): + if request.method == 'POST': + # form = ExportExcelForm(request.POST) + # quick_search = request.POST.get('quick_search') + # type = request.POST.get('type') + + # Create a new workbook and select the active worksheet + workbook = openpyxl.Workbook() + sheet = workbook.active + sheet.title = "Data Export" + + # create the headers + headers = ['Rack', 'Số U','Vị trí bắt đầu','Vị trí kế thúc', 'Tên Thiết bị', 'Chủng loại', 'Quản lý', 'Số HĐ', 'Model', 'SN', 'Thời gian lắp đặt', 'Ghi Chú'] + sheet.append(headers) + + # check form data: 1. export all table + result = [] + # 1. export all table + + # get all device + devices_list = get_device() + + for device in devices_list: + # get data custom feild + device_owner = "" + year_of_investment = "" + contract_number = "" + custom_fields = device.get_custom_fields_by_group() + for key, value in custom_fields[''].items(): + if str(key) == 'Device owner' and value != None: + device_owner = value + elif str(key) == 'Year of investment' and value != None: + year_of_investment = value + elif str(key) == 'Contract number' and value != None: + contract_number = value + # Tính start U - end U + end_u = int(device.position) + int(device.device_type.u_height) - 1 + # create new item export + item_export = ExportExcel( + rack = device.rack, + device_role = device.role, + device_type = device.device_type, + device_name = device.name, + position = int(device.position), + serial_number = device.serial, + device_description = device.description, + owner_device = device_owner, + year_of_investment = year_of_investment, + contract_number = contract_number, + u_number = int(device.device_type.u_height), + u_end = end_u, + ) + + # append data to result export + result.append(item_export) + + # create item in sheet + item_sheet = [ + str(item_export.rack), + str(item_export.u_number), + str(item_export.position), # U start + str(item_export.u_end), # U end + str(item_export.device_name), + str(item_export.device_role), + str(item_export.owner_device), + str(item_export.contract_number), + str(item_export.device_type), + str(item_export.serial_number), + str(item_export.year_of_investment), + str(item_export.device_description), + ] + sheet.append(item_sheet) + + # add header response + response = HttpResponse(content_type='application/vnd.ms-excel') + response['Content-Disposition'] = 'attachment;filename="device_export_excel.xlsx"' + + workbook.save(response) + return response + # return HttpResponseRedirect("/dcim/devices/") + else: + # form = ExportExcelForm() + return HttpResponseRedirect("/dcim/devices/") \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5f8732f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,62 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "netbox-excel" +dynamic = ["version"] +description = "Netbox Excel Netbox Plugin" +readme = "README.md" +requires-python = ">=3.10.12" +license = { text = "Apache-2.0" } +authors = [ + {name = "ducna", email = "ducna-hcd@gmail.com"}, +] +classifiers = [ + "Environment :: Web Environment", + "Framework :: Django", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", +] +dependencies = [] + +[project.optional-dependencies] +build = [ + "build == 1.2.1", + "setuptools == 70.3.0", + "twine == 5.1.1", +] +tools = [ + "ruff == 0.5.1", +] + +[tool.setuptools.dynamic] +version = {attr = "netbox_excel.__version__"} + +[tool.setuptools.packages.find] +include = ["netbox_excel"] + +[tool.ruff] +line-length = 120 +target-version = "py311" +src = ["netbox_excel"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = [ + "D104", # https://beta.ruff.rs/docs/rules/#pydocstyle-d - don't require doc strings in __init__.py files + "F401", # https://beta.ruff.rs/docs/rules/#pyflakes-f - don't complain about unused imports in __init__.py files +] +"netbox_excel/migrations/*.py" = [ + "E501", # https://beta.ruff.rs/docs/rules/line-too-long/ - don't check on Django generated migration files +] + +[tool.ruff.format] +exclude = ["netbox_excel/migrations/*.py"] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..4927abe --- /dev/null +++ b/setup.cfg @@ -0,0 +1,4 @@ +[egg_info] +tag_build = +tag_date = 0 + From 7a8df1d8f2507023136afbbe6bf58c283928318c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AA=CC=83n=20Anh=20=C4=90=C6=B0=CC=81c?= Date: Thu, 2 Jan 2025 14:41:02 +0700 Subject: [PATCH 2/4] add git ignore --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 13 ++ netbox_excel.egg-info/PKG-INFO | 62 ------ netbox_excel.egg-info/SOURCES.txt | 27 --- netbox_excel.egg-info/dependency_links.txt | 1 - netbox_excel.egg-info/requires.txt | 8 - netbox_excel.egg-info/top_level.txt | 1 - .../template_content.cpython-310.pyc | Bin 887 -> 887 bytes .../__pycache__/views.cpython-310.pyc | Bin 2440 -> 1306 bytes netbox_excel/export/__init__.py | 3 +- .../__pycache__/__init__.cpython-310.pyc | Bin 276 -> 334 bytes .../export/__pycache__/export.cpython-310.pyc | Bin 601 -> 3710 bytes netbox_excel/export/export.py | 192 +++++++++++++++++- netbox_excel/export/rack.py | 6 + .../templates/netbox_excel/export_excel.html | 5 +- netbox_excel/views.py | 76 +------ 16 files changed, 216 insertions(+), 178 deletions(-) create mode 100644 .DS_Store create mode 100644 .gitignore delete mode 100644 netbox_excel.egg-info/PKG-INFO delete mode 100644 netbox_excel.egg-info/SOURCES.txt delete mode 100644 netbox_excel.egg-info/dependency_links.txt delete mode 100644 netbox_excel.egg-info/requires.txt delete mode 100644 netbox_excel.egg-info/top_level.txt create mode 100644 netbox_excel/export/rack.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..310ff1a1a114349d80ae4aa7814feae87cefc7d5 GIT binary patch literal 6148 zcmeHKyH3ME5S)V)k)TL}M0q~|5`S=t!WZxXpgcfG7A^pZ(jC9e>;vS;NlO8<*6#S$ zJ3e`eTQ7jJeBa&x3jlMvAwDfk&7PZ2>?AXW#oy861NYc5ZYI@-6UIHq3psCCW6a;7 z+rDnPb>DYJy*O5cYm%RMUE!HEkLbs=!@DXg1*Cu!kOETR|0=+qZ8kp(RFnczKnnaQ zVCO@n8@9wD(2ouV9|4Fnrrmh&vjnkPg4hy=KxSwqsKlU_95G7JnXg&b5{JN`qxjBz zPwrfDLMgsG^VQ2yTA-p7kOKP(oW^ot{r{GJWd7eLX(a`uz@Ji}%vN`+B|j;9>*V9C v*H-!i{o7dU#d literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f65808 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +**/netbox +**/netbox-docker +**/*.pyc +**/__pycache__ +**/.idea +.vscode/ +dist/* +ci/docker-compose.override.yml +ci/reports +out/production +*.iml +build/* +netbox_excel.egg-info/* \ No newline at end of file diff --git a/netbox_excel.egg-info/PKG-INFO b/netbox_excel.egg-info/PKG-INFO deleted file mode 100644 index 3d51196..0000000 --- a/netbox_excel.egg-info/PKG-INFO +++ /dev/null @@ -1,62 +0,0 @@ -Metadata-Version: 2.1 -Name: netbox-excel -Version: 1.0.0 -Summary: Netbox Excel Netbox Plugin -Author-email: ducna -License: Apache-2.0 -Classifier: Environment :: Web Environment -Classifier: Framework :: Django -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Internet :: WWW/HTTP -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Requires-Python: >=3.10.12 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: build -Requires-Dist: build==1.2.1; extra == "build" -Requires-Dist: setuptools==70.3.0; extra == "build" -Requires-Dist: twine==5.1.1; extra == "build" -Provides-Extra: tools -Requires-Dist: ruff==0.5.1; extra == "tools" - - -## Install Require - -netbox version >= 4.0 - -## Known Issues - -- WARNING: This plugin is only tested with a single NetBox version at this time. - -## Installation Guide - -### In mono service: - -To install the plugin, first using pip and install netbox-excel: - - ``` - cd /opt/netbox - source venv/bin/activate - pip install netbox-excel - ``` - -Next, enable the plugin in /opt/netbox/netbox/netbox/configuration.py, or if you have a /configuration/plugins.py file, the plugins.py file will take precedence. - - ``` - PLUGINS = [ - 'netbox_excel' - ] - ``` -Then you may need to perform the final step of restarting the service to ensure that the changes take effect correctly: - - ``` - python netbox/manage.py migrate netbox_excel - sudo systemctl restart netbox - ``` diff --git a/netbox_excel.egg-info/SOURCES.txt b/netbox_excel.egg-info/SOURCES.txt deleted file mode 100644 index 7319f5b..0000000 --- a/netbox_excel.egg-info/SOURCES.txt +++ /dev/null @@ -1,27 +0,0 @@ -LICENSE -MANIFEST.in -README.md -pyproject.toml -setup.cfg -netbox_excel/__init__.py -netbox_excel/forms.py -netbox_excel/models.py -netbox_excel/navigation.py -netbox_excel/tables.py -netbox_excel/template_content.py -netbox_excel/urls.py -netbox_excel/views.py -netbox_excel.egg-info/PKG-INFO -netbox_excel.egg-info/SOURCES.txt -netbox_excel.egg-info/dependency_links.txt -netbox_excel.egg-info/requires.txt -netbox_excel.egg-info/top_level.txt -netbox_excel/api/__init__.py -netbox_excel/api/serializers.py -netbox_excel/api/urls.py -netbox_excel/api/views.py -netbox_excel/migrations/0001_initial.py -netbox_excel/migrations/__init__.py -netbox_excel/templates/netbox_excel/export_excel.html -netbox_excel/templates/netbox_excel/import_excel.html -netbox_excel/templates/netbox_excel/import_excel_console_log.html \ No newline at end of file diff --git a/netbox_excel.egg-info/dependency_links.txt b/netbox_excel.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/netbox_excel.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/netbox_excel.egg-info/requires.txt b/netbox_excel.egg-info/requires.txt deleted file mode 100644 index d0aab00..0000000 --- a/netbox_excel.egg-info/requires.txt +++ /dev/null @@ -1,8 +0,0 @@ - -[build] -build==1.2.1 -setuptools==70.3.0 -twine==5.1.1 - -[tools] -ruff==0.5.1 diff --git a/netbox_excel.egg-info/top_level.txt b/netbox_excel.egg-info/top_level.txt deleted file mode 100644 index 7afcf29..0000000 --- a/netbox_excel.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -netbox_excel diff --git a/netbox_excel/__pycache__/template_content.cpython-310.pyc b/netbox_excel/__pycache__/template_content.cpython-310.pyc index 05348fd552910e72196a342a753a43b877fad443..0a9f446a686a1c9b037f5c4d0c593574fc69146f 100644 GIT binary patch delta 22 ccmey)_MMG4pO=@50SHbnD^6dtkvEGO08I1-y#N3J delta 22 ccmey)_MMG4pO=@50SLYp7N@V>$eYCs08GmUvj6}9 diff --git a/netbox_excel/__pycache__/views.cpython-310.pyc b/netbox_excel/__pycache__/views.cpython-310.pyc index bd99babcbf72f33532f25b6db9c29c4203ab5aec..54390e76acda03832a533fef664c8df81c519072 100644 GIT binary patch delta 326 zcmYL@u};G<5QcrWle%eBR9Y6qjFbr>R)$I}OfZx^3bj?0L~-Prw3QNp)P;qq0}=yj z2Oa?NHhBOZfG6NAkT~gn-+lgV{ZE6}pr^wyAXsl-_tR_dqjw6?P%R5>Y@B3ST+Y-Y zHpwJy_W!8PvsElrIh&}@7|!5<<7r10WR}6Wfwv4Pz3uuS|%4H{7lY)pY(-fZBlnI1*+L zuYWyks;ud{wd2HvYxq1Vm2p`kZu$-%I4l<0q_TdM|#r#3(It@Zbeg2f-B2{#*#N(9li<>@;E~P9M zor{}?24Lgnfgup*7`>IlCYkGJY|Z8Ah$cNvSOd!4fNq376&977xLy>AxvKBy3=HJ% zMo4d8_3`-qp9-BCv}I`7TR;kB9S|LNHAA&(=WuYte4nlNI@hiSkJIGMG8 zh}xwqA6;BwXcs_KM}KL?SsUb^6*dd(zw=A!m@k#5?Xz)|55lC9&Xpz{vIkwy7ROtM zP`?R-7;Bjc#8SJ&fX3!QI0VuR3_u{3ApHjLJqPJ;PIlD$082sEwW%7Yh3e`92)v)j zR4de-TA|&Cz#J%3of`mOt*Bj4zJ4$@0)1MCW_8c5$fxyNZ&w-vG}kH(@TvJ+QwPnLl?M2<0UE0?)?q)%?e*3+ zIjRKaf--IKHfTD~y3lNBGy8L)KMQTHs266@c(Jl*1&zD*v|Z@nWgEQ&i`wfQ*d6(N z#ql(hU#a+!iXW}`LdB0&+^+cXil3#|v~yL))}(pCz+T#G4xjU9^G?3%a1_!=H8<_ z4rh;lbyoLwf9KAVdwaVN^BiO1g=)07`)Z>6#NJ8}MXec<__Xa++}F`P@nJ zc$G4SZyjBSv5{z&DZ^#(HlGW8Yy3h;yqF4eoMs^p(?ql~%0e%4%gw}WwLYMk&%&{s zVoPK8lhN!J*yN^b?hBhl2eN^*X@46t-|y9O7xL(2!5fdG(D!h|rHv$5jI;7dLk6^Q zHRdw3bF%1h?)gLX^_{gaqG<2CXDYL*o|()c9x(abg%C1y>D8pY6!>Ah zRIbXFdJe@3FfT3V8!@Cv7F$w#5#?#nc z3uzQ&?&_91U}-)UwUE;|6B=YPp@rc7DOkZy$YXMMNW;O9GYkoK499eNBf3Q2C^5(T zd5$uB9jCs5L$^1>FldE&d*7>CHl&n``jC21 z#4@aASsrm=SCF_-m~mm0(*CF`FSPTV38A+ z6v~rLI8YW$nczz@hkG8Zh?uM~|7T+$mTDRXF^H*IMAr=6RCQG+Ix)#Sd`8uDqJDc^ zRi&oXB30#Q0X3dkT~jw<)ytp$%x+tnNi0}x>I@5HZ%%ZBbuSsDi`fu-@N=HYN(Glw z4iBODOS~o0qK0KIE2~=_{h2#hlNMuq^_jePakAQ>tWyE?Q|9rMWs6woS>xr!C5$rk zSQFIDM)D>SOjgq118^d8Ok!%Dhja6YzK8zcP!&Xo*B>_F{B>=R!_aj*bc IJPaa?06p6vpa1{> diff --git a/netbox_excel/export/__pycache__/export.cpython-310.pyc b/netbox_excel/export/__pycache__/export.cpython-310.pyc index 9efc868a260609ddc48e82a5241988ec3581b36f..d4be4b5e5dba843becf402c486e497e083b93828 100644 GIT binary patch literal 3710 zcma)9&2QYs73U1OTrQXUq1Ecc@)wh~hfQI--$|3yNMRdsn^tYCq(;=F%xXE_<%&zL zhO?3-kU;?TRv2*pfEIFn2-2d>r9c50MGwwj&`Z&|2|ei^_25I%_l8`31SpUt-tWzO zZ@&0A^WNLIRLX*- zQ0-D!8kwsmw6Sk_)dFsYm65$_kE*Lx(3gGd*V?M%S9}|I&9C|n@VZY{v`+01R?(I_ zL{@LzAIDtWy5D2rAps@m>^>8o&o+Y|1F4M?@AlTcL3fi)^;9~yg&5yq$vBP@Cflc_ zJIoI_>xmw&GW{(;qYiWfDESAxuh90$BQ4WXZAdfXlazRRAKv))B%>+WGgA795bYt^ zc}0|VwG3>T0aQx$o7#K#Gt<|zGLQvSNzFYI=CV`U*UxEtvW8#DnquCE8A0C5X2e2TN*if&SI^AUd`S2WlwhqzpmRV=K<9xRpbJ13fiCs! ztfl;c*_?XMIrW}R-+oEUaCQ4dEvs+*apQ5S-PKk!Lla+lNVW$=%jSu;*lwrV@u->+?*Zr?Q6)7X5j3<_4E&s`_>>h z*#En5*AAZi=1AR&!NHS{qrMx)2m6nLBMcUAP7d}TM{fA^Z$}o0D+fqyw8#z55}sCqxMmR6WQ8^?RfD$?_@i&(JQtyoyM9JtwW5K zhc5td9a{>}WgRMY9&bsr7l)Hk)G=fw5nT@P3zSaY%uTY)B44R(Y~;#A17|BCmDw+w1dqGM1%4uu&rQL~xET%I9%}fzIdn!KI=Qz=HljfEfZj^Q936iGXRDkR{%Y`b;+O1@P|0$H7(kIb07THNS{( z8R0y_C4>tIE`T&3U4*i<*$pRf2u6(enTM)GRxp+1pSU#Uu*c4Vw6>-`DJ262>z4ocB6Ctp1A)ZT17ZAH6*`|(uw+-T-qr$an-e)4_cHf8OMJEgwPw)rhIRMkxRSCjRr zTk@%M-`0+m>bR3Y8aPJI=N>%vP?QgnIvf{iv(Z>=d#WR0pJ@DDSnS!&Yw=iIi8RHrc47oPvvj-%IUon9g> z+9IbPi`r@z^q=O}o3IMdhxEVvSFpt%UdibXPdv&ufD*j5DQ4$prlGX%mnD$Au_URal`oVec2W;%PR!=Z~tBm>f1jFYqAkVsjKm8MA-2G35N|7H=Chaq(bx6ss zfNa_0cxHGN!9l1YV4CK5P&l3#j=7w-5M~i@Dn5sx1Hd+L**uhD(9R<(hU+|@s!*z8 ztct8E(7IrirHKbwVyDHKnDEF|K?YI|#Ei}*{!JYJErf3)d9?;xn~T*CZ2;VYn!_DRs^zr-FGQ%~-+Y!z`aj>7FCDyPsR;Q%L5ky1l9>Hm+3 zOB8w(an4el5`1hR^K-jt8K)!WN!$3`shhNZ66r9EbQ`Q1d)u@9+x2pKuPI)MLF9M3Mt+Czm>6|guib;=vcFBs;e^(w42 Z3E6f0^OoRwv@{6k@IXM;Eou!N>wm$jv}*tW delta 341 zcmew-bCZQHpO=@50SG+uiqcgWC-TWK`b^YT(PcGYgPNVF+ee$>_I|p@ 1 => merg cell + if device.u_number > 1: + # Từ cột thứ 3 đến cuối đều cần merg ô bằng chiều cao của thiết bị + height_device_in_sheet = u_height_sheet - device.u_number + 1 + # copy data từ row position sang ô đầu tiên + for col in range(3, 10): + sheet.cell(row=height_device_in_sheet, column=col).value = sheet.cell(row=u_height_sheet, column=col).value + + sheet.merge_cells(start_row=height_device_in_sheet, start_column=3, end_row=u_height_sheet, end_column=3) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=4, end_row=u_height_sheet, end_column=4) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=5, end_row=u_height_sheet, end_column=5) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=6, end_row=u_height_sheet, end_column=6) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=7, end_row=u_height_sheet, end_column=7) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=8, end_row=u_height_sheet, end_column=8) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=9, end_row=u_height_sheet, end_column=9) + sheet.merge_cells(start_row=height_device_in_sheet, start_column=10, end_row=u_height_sheet, end_column=10) + # add item into sheet + empty_item = [rack,u_height_rack] + sheet.append(empty_item) + # print(f"add empty into rack {rack} and U{u_height_rack}") + # variable counter + u_height_rack-= 1 + u_height_sheet+= 1 + + return workbook + except Exception as e: + print("error export all device") + return workbook + +# Tìm device với rack name và position +def find_device_item(devices_list, rack_name, position): + for device in devices_list: + if device.rack == rack_name and device.position == position: + return device + + return + + +def export_only_device(): + # Create a new workbook and select the active worksheet + workbook = openpyxl.Workbook() + sheet = workbook.active + sheet.title = "Data Export" + + # create the headers + headers = ['Rack', 'Số U','Vị trí bắt đầu','Vị trí kế thúc', 'Tên Thiết bị', 'Chủng loại', 'Quản lý', 'Số HĐ', 'Model', 'SN', 'Thời gian lắp đặt', 'Ghi Chú'] + sheet.append(headers) + + try: + + # check form data: 1. export all table + item_sheet_list = [] + # get all device + devices_list = get_device() + + # start loop for + for device in devices_list: + # get data custom feild + device_owner = "" + year_of_investment = "" + contract_number = "" + custom_fields = device.get_custom_fields_by_group() + for key, value in custom_fields[''].items(): + if str(key) == 'Device owner' and value != None: + device_owner = value + elif str(key) == 'Year of investment' and value != None: + year_of_investment = value + elif str(key) == 'Contract number' and value != None: + contract_number = value + # Tính start U - end U + end_u = int(device.position) + int(device.device_type.u_height) - 1 + # create new item export + item_export = ExportExcel( + rack = device.rack, + device_role = device.role, + device_type = device.device_type, + device_name = device.name, + position = int(device.position), + serial_number = device.serial, + device_description = device.description, + owner_device = device_owner, + year_of_investment = year_of_investment, + contract_number = contract_number, + u_number = int(device.device_type.u_height), + u_end = end_u, + ) + + # append data to item_sheet_list export + item_sheet_list.append(item_export) + + # create item in sheet + item_sheet = [ + str(item_export.rack), + str(item_export.u_number), + str(item_export.position), # U start + str(item_export.u_end), # U end + str(item_export.device_name), + str(item_export.device_role), + str(item_export.owner_device), + str(item_export.contract_number), + str(item_export.device_type), + str(item_export.serial_number), + str(item_export.year_of_investment), + str(item_export.device_description), + ] + sheet.append(item_sheet) + # end loop for + return workbook + except Exception as e: + print("return empty excel") + return workbook \ No newline at end of file diff --git a/netbox_excel/export/rack.py b/netbox_excel/export/rack.py new file mode 100644 index 0000000..16dacd1 --- /dev/null +++ b/netbox_excel/export/rack.py @@ -0,0 +1,6 @@ +from dcim.models import Rack + +def get_rack_have_device(): + racks = Rack.objects.all() + racks = racks.order_by('name') + return racks \ No newline at end of file diff --git a/netbox_excel/templates/netbox_excel/export_excel.html b/netbox_excel/templates/netbox_excel/export_excel.html index 032be44..140ef72 100644 --- a/netbox_excel/templates/netbox_excel/export_excel.html +++ b/netbox_excel/templates/netbox_excel/export_excel.html @@ -46,8 +46,8 @@ diff --git a/netbox_excel/views.py b/netbox_excel/views.py index 0be9f28..40bdb8d 100644 --- a/netbox_excel/views.py +++ b/netbox_excel/views.py @@ -3,7 +3,7 @@ from .forms import ImportExcelForm, ExportExcelForm from netbox_excel.models import ExportExcel from django.views.decorators.csrf import requires_csrf_token -from netbox_excel.export import get_device, export_all, export_current_view +from netbox_excel.export import get_device, export_all_view_rack, export_only_device import openpyxl @@ -18,77 +18,13 @@ def ImportExcelView(request): @requires_csrf_token def ExportExcelView(request): if request.method == 'POST': - # form = ExportExcelForm(request.POST) # quick_search = request.POST.get('quick_search') - # type = request.POST.get('type') - - # Create a new workbook and select the active worksheet - workbook = openpyxl.Workbook() - sheet = workbook.active - sheet.title = "Data Export" - - # create the headers - headers = ['Rack', 'Số U','Vị trí bắt đầu','Vị trí kế thúc', 'Tên Thiết bị', 'Chủng loại', 'Quản lý', 'Số HĐ', 'Model', 'SN', 'Thời gian lắp đặt', 'Ghi Chú'] - sheet.append(headers) - - # check form data: 1. export all table - result = [] - # 1. export all table - - # get all device - devices_list = get_device() - - for device in devices_list: - # get data custom feild - device_owner = "" - year_of_investment = "" - contract_number = "" - custom_fields = device.get_custom_fields_by_group() - for key, value in custom_fields[''].items(): - if str(key) == 'Device owner' and value != None: - device_owner = value - elif str(key) == 'Year of investment' and value != None: - year_of_investment = value - elif str(key) == 'Contract number' and value != None: - contract_number = value - # Tính start U - end U - end_u = int(device.position) + int(device.device_type.u_height) - 1 - # create new item export - item_export = ExportExcel( - rack = device.rack, - device_role = device.role, - device_type = device.device_type, - device_name = device.name, - position = int(device.position), - serial_number = device.serial, - device_description = device.description, - owner_device = device_owner, - year_of_investment = year_of_investment, - contract_number = contract_number, - u_number = int(device.device_type.u_height), - u_end = end_u, - ) - - # append data to result export - result.append(item_export) - - # create item in sheet - item_sheet = [ - str(item_export.rack), - str(item_export.u_number), - str(item_export.position), # U start - str(item_export.u_end), # U end - str(item_export.device_name), - str(item_export.device_role), - str(item_export.owner_device), - str(item_export.contract_number), - str(item_export.device_type), - str(item_export.serial_number), - str(item_export.year_of_investment), - str(item_export.device_description), - ] - sheet.append(item_sheet) + type = request.POST.get('type') + if type == "only_device": + workbook = export_only_device() + else: + workbook = export_all_view_rack() # add header response response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment;filename="device_export_excel.xlsx"' From d844e3ed8e7dbf4f54119d5635a315361740cf2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AA=CC=83n=20Anh=20=C4=90=C6=B0=CC=81c?= Date: Tue, 7 Jan 2025 09:41:54 +0700 Subject: [PATCH 3/4] fix bug export excel --- netbox_excel/.DS_Store | Bin 0 -> 6148 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 334 -> 341 bytes .../__pycache__/devices.cpython-310.pyc | Bin 384 -> 391 bytes .../export/__pycache__/export.cpython-310.pyc | Bin 3710 -> 3737 bytes netbox_excel/export/export.py | 5 +++++ .../templates/netbox_excel/export_excel.html | 6 +++--- 6 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 netbox_excel/.DS_Store diff --git a/netbox_excel/.DS_Store b/netbox_excel/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a9f4bfef5bbf4c95a073e6573caca9ac6fabefa4 GIT binary patch literal 6148 zcmeHK!AiqG5S?vnn^1%v6vR`&YsJ=9MZAPsf53ZC2~Yz73ze{7Z zkqnli+3_D4pm$e>0X)M9BrLwaw_p^7N!n<I#oKb<5Ue|$~9 zE~Jctx$Os+;UMePHcn-n`e8g6tAwyWz>u4(Fz(B&BPVe`QMsPpux!ij)wZY8-DbOS zPXfV-oWm;`3spUN<2aJ>J^Crf zh7@ZaV$Biw%1*vS%uixu2ABb6U=Ub-iu}YcsudsY>d#4(c5$3B~0a mKU2_9tr%me6>p&`LBB%=qH8eMh#nOFBcN#Dg&Fu$20j3tY*Y{c literal 0 HcmV?d00001 diff --git a/netbox_excel/export/__pycache__/__init__.cpython-310.pyc b/netbox_excel/export/__pycache__/__init__.cpython-310.pyc index 37657525fa7381c234811cde379782e20b3dcc14..696dc0cb435f1a7ed3fa549134eeda69d8af2d1a 100644 GIT binary patch delta 70 zcmX@dbd`xapO=@50SE-ARZrx8tmmtrUr?f-ms*mPU!h-?npdWulbNJnP+5|ZpJ%LR YXrN!5S(2(-keHmEn4VfZS(MQd00C(hyZ`_I delta 63 zcmcc0bdHHTpO=@50SI#cl}_Y-tnQ|tUr?f-ms*mPUjd^Ea!S)P^PoK4)QaTPoXIkb FmH?K;6}A8X diff --git a/netbox_excel/export/__pycache__/devices.cpython-310.pyc b/netbox_excel/export/__pycache__/devices.cpython-310.pyc index 9278b9bd785abd657f001e1be8cd32aa1ce3750d..757478f01eb31c40fc2a68da0235662c446d952e 100644 GIT binary patch delta 69 zcmZo*ZfEAs=jG*M00O~j)f2f->G|mA7nJDdrIsY+SLm0e=9TH^WG3ksRF-7q=Nao6 X8t4~imZa(yBqnDkrl%H9{L=yezS9>s delta 63 zcmZo?ZeZrl=jG*M0D^ThiYIcPQg_wQFDTK^OD##tuYgenIi=~Dc~G8iYDIEt&SYlB F764_l6%7CY diff --git a/netbox_excel/export/__pycache__/export.cpython-310.pyc b/netbox_excel/export/__pycache__/export.cpython-310.pyc index d4be4b5e5dba843becf402c486e497e083b93828..3946a04892f87118497f2566907957c11f043b0e 100644 GIT binary patch delta 514 zcmXYu&ubGw6oBWwnas{^c9X0Ji&8}h^`N%fQYiGMMexvL1rJs*bwg}YvPn9Y24`#~ z7cYgtK+#(QUi4OS75@wG>cxM-llXSof%m={zBiBg@o)Llty&gE0izLLzU}NZuWAVJ z)5Y-MY)4ln%<>Mif}&#hWA@^ml}2`D%HFWi-Qn$&TaQ9f_(smWIkSNYQhtNY)SOZM zy?wM*8*1NFCyWj&tLdc&>KRLDFn4biIM)>)|}sh6c9TAwxra;@imZz4Q_d@+?ln4|cTu&G5i?PAg{oV}`zv2>1l*g6ZZI#@XT< z-$NYTbfMf`?8^&S+fA~8PWo+qkewzc?fyw}ba;>q#`<;E-(1^RPmT|Ddv(xy)oXRy z$F8Bamp{{=DU={V@c;x$6d>|BBDj{)H9XIS5&_^3x$1tYOBgRs{ofqkFP=wtfM=ol E50txvE&u=k delta 502 zcmX|;%Sr<=6ozw>nM`In(~Ao)RiRWw@Pckt1VuqtZd?db5p+tmt*w?(DkQk*S||nt zH!?*x;!64!UAl3t&mrRJFg5U>kHeQ767o3tJCUq=oqai1v*BQUn)A@307NXKVmB(ypN_skZkN+>rFGSLFxsL;lSbbV4` zeJbm6DK;jW-Pj|nM9w8`*h4qL@TNysy5dC zPPaUS*R$>t%9BQD45oJgh%`e(>TQ}^dsQjlR_S2a$7A@mP-TJExport Device From 6fbc8bb61064fff39bb68062a2d9a970081f7918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AA=CC=83n=20Anh=20=C4=90=C6=B0=CC=81c?= Date: Mon, 20 Jan 2025 08:07:27 +0700 Subject: [PATCH 4/4] update description --- .DS_Store | Bin 6148 -> 6148 bytes netbox_excel/export/export.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 310ff1a1a114349d80ae4aa7814feae87cefc7d5..8de75561aabffa4134b2e4d8b2f8b4f56e66dc39 100644 GIT binary patch delta 57 zcmZoMXfc=|&e%4wP;8=}A|vC(0Ba!8qp|U|4ErQT^UdrW0vwEO8$W($p3E