brilliance-admin 0.42.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- admin_panel/__init__.py +4 -0
- admin_panel/api/__init__.py +0 -0
- admin_panel/api/routers.py +18 -0
- admin_panel/api/utils.py +28 -0
- admin_panel/api/views/__init__.py +0 -0
- admin_panel/api/views/auth.py +29 -0
- admin_panel/api/views/autocomplete.py +33 -0
- admin_panel/api/views/graphs.py +30 -0
- admin_panel/api/views/index.py +38 -0
- admin_panel/api/views/schema.py +29 -0
- admin_panel/api/views/settings.py +29 -0
- admin_panel/api/views/table.py +136 -0
- admin_panel/auth.py +32 -0
- admin_panel/docs.py +37 -0
- admin_panel/exceptions.py +38 -0
- admin_panel/integrations/__init__.py +0 -0
- admin_panel/integrations/sqlalchemy/__init__.py +6 -0
- admin_panel/integrations/sqlalchemy/auth.py +144 -0
- admin_panel/integrations/sqlalchemy/autocomplete.py +38 -0
- admin_panel/integrations/sqlalchemy/fields.py +254 -0
- admin_panel/integrations/sqlalchemy/fields_schema.py +316 -0
- admin_panel/integrations/sqlalchemy/table/__init__.py +19 -0
- admin_panel/integrations/sqlalchemy/table/base.py +141 -0
- admin_panel/integrations/sqlalchemy/table/create.py +73 -0
- admin_panel/integrations/sqlalchemy/table/delete.py +18 -0
- admin_panel/integrations/sqlalchemy/table/list.py +178 -0
- admin_panel/integrations/sqlalchemy/table/retrieve.py +61 -0
- admin_panel/integrations/sqlalchemy/table/update.py +95 -0
- admin_panel/schema/__init__.py +7 -0
- admin_panel/schema/admin_schema.py +191 -0
- admin_panel/schema/category.py +149 -0
- admin_panel/schema/graphs/__init__.py +1 -0
- admin_panel/schema/graphs/category_graphs.py +50 -0
- admin_panel/schema/group.py +67 -0
- admin_panel/schema/table/__init__.py +8 -0
- admin_panel/schema/table/admin_action.py +76 -0
- admin_panel/schema/table/category_table.py +175 -0
- admin_panel/schema/table/fields/__init__.py +5 -0
- admin_panel/schema/table/fields/base.py +249 -0
- admin_panel/schema/table/fields/function_field.py +65 -0
- admin_panel/schema/table/fields_schema.py +216 -0
- admin_panel/schema/table/table_models.py +53 -0
- admin_panel/static/favicon.jpg +0 -0
- admin_panel/static/index-BeniOHDv.js +525 -0
- admin_panel/static/index-vlBToOhT.css +8 -0
- admin_panel/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
- admin_panel/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
- admin_panel/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
- admin_panel/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
- admin_panel/static/tinymce/dark-first/content.min.css +250 -0
- admin_panel/static/tinymce/dark-first/skin.min.css +2820 -0
- admin_panel/static/tinymce/dark-slim/content.min.css +249 -0
- admin_panel/static/tinymce/dark-slim/skin.min.css +2821 -0
- admin_panel/static/tinymce/img/example.png +0 -0
- admin_panel/static/tinymce/img/tinymce.woff2 +0 -0
- admin_panel/static/tinymce/lightgray/content.min.css +1 -0
- admin_panel/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
- admin_panel/static/tinymce/lightgray/skin.min.css +1 -0
- admin_panel/static/tinymce/plugins/accordion/css/accordion.css +17 -0
- admin_panel/static/tinymce/plugins/accordion/plugin.js +48 -0
- admin_panel/static/tinymce/plugins/codesample/css/prism.css +1 -0
- admin_panel/static/tinymce/plugins/customLink/css/link.css +3 -0
- admin_panel/static/tinymce/plugins/customLink/plugin.js +147 -0
- admin_panel/static/tinymce/tinymce.min.js +2 -0
- admin_panel/static/vanilla-picker-B6E6ObS_.js +8 -0
- admin_panel/templates/index.html +25 -0
- admin_panel/translations.py +145 -0
- admin_panel/utils.py +50 -0
- brilliance_admin-0.42.0.dist-info/METADATA +155 -0
- brilliance_admin-0.42.0.dist-info/RECORD +73 -0
- brilliance_admin-0.42.0.dist-info/WHEEL +5 -0
- brilliance_admin-0.42.0.dist-info/licenses/LICENSE +17 -0
- brilliance_admin-0.42.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* vanilla-picker v2.12.3
|
|
3
|
+
* https://vanilla-picker.js.org
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2017-2024 Andreas Borgen (https://github.com/Sphinxxxx), Adam Brooks (https://github.com/dissimulate)
|
|
6
|
+
* Released under the ISC license.
|
|
7
|
+
*/var O=function(l,n){if(!(l instanceof n))throw new TypeError("Cannot call a class as a function")},R=function(){function l(n,e){for(var t=0;t<e.length;t++){var r=e[t];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(n,r.key,r)}}return function(n,e,t){return e&&l(n.prototype,e),t&&l(n,t),n}}(),y=function(){function l(n,e){var t=[],r=!0,i=!1,o=void 0;try{for(var c=n[Symbol.iterator](),a;!(r=(a=c.next()).done)&&(t.push(a.value),!(e&&t.length===e));r=!0);}catch(s){i=!0,o=s}finally{try{!r&&c.return&&c.return()}finally{if(i)throw o}}return t}return function(n,e){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return l(n,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();String.prototype.startsWith=String.prototype.startsWith||function(l){return this.indexOf(l)===0};String.prototype.padStart=String.prototype.padStart||function(l,n){for(var e=this;e.length<l;)e=n+e;return e};var I={cb:"0f8ff",tqw:"aebd7",q:"-ffff",qmrn:"7fffd4",zr:"0ffff",bg:"5f5dc",bsq:"e4c4",bck:"---",nch:"ebcd",b:"--ff",bvt:"8a2be2",brwn:"a52a2a",brw:"deb887",ctb:"5f9ea0",hrt:"7fff-",chcT:"d2691e",cr:"7f50",rnw:"6495ed",crns:"8dc",crms:"dc143c",cn:"-ffff",Db:"--8b",Dcn:"-8b8b",Dgnr:"b8860b",Dgr:"a9a9a9",Dgrn:"-64-",Dkhk:"bdb76b",Dmgn:"8b-8b",Dvgr:"556b2f",Drng:"8c-",Drch:"9932cc",Dr:"8b--",Dsmn:"e9967a",Dsgr:"8fbc8f",DsTb:"483d8b",DsTg:"2f4f4f",Dtrq:"-ced1",Dvt:"94-d3",ppnk:"1493",pskb:"-bfff",mgr:"696969",grb:"1e90ff",rbrc:"b22222",rwht:"af0",stg:"228b22",chs:"-ff",gnsb:"dcdcdc",st:"8f8ff",g:"d7-",gnr:"daa520",gr:"808080",grn:"-8-0",grnw:"adff2f",hnw:"0fff0",htpn:"69b4",nnr:"cd5c5c",ng:"4b-82",vr:"0",khk:"0e68c",vnr:"e6e6fa",nrb:"0f5",wngr:"7cfc-",mnch:"acd",Lb:"add8e6",Lcr:"08080",Lcn:"e0ffff",Lgnr:"afad2",Lgr:"d3d3d3",Lgrn:"90ee90",Lpnk:"b6c1",Lsmn:"a07a",Lsgr:"20b2aa",Lskb:"87cefa",LsTg:"778899",Lstb:"b0c4de",Lw:"e0",m:"-ff-",mgrn:"32cd32",nn:"af0e6",mgnt:"-ff",mrn:"8--0",mqm:"66cdaa",mmb:"--cd",mmrc:"ba55d3",mmpr:"9370db",msg:"3cb371",mmsT:"7b68ee","":"-fa9a",mtr:"48d1cc",mmvt:"c71585",mnLb:"191970",ntc:"5fffa",mstr:"e4e1",mccs:"e4b5",vjw:"dead",nv:"--80",c:"df5e6",v:"808-0",vrb:"6b8e23",rng:"a5-",rngr:"45-",rch:"da70d6",pgnr:"eee8aa",pgrn:"98fb98",ptrq:"afeeee",pvtr:"db7093",ppwh:"efd5",pchp:"dab9",pr:"cd853f",pnk:"c0cb",pm:"dda0dd",pwrb:"b0e0e6",prp:"8-080",cc:"663399",r:"--",sbr:"bc8f8f",rb:"4169e1",sbrw:"8b4513",smn:"a8072",nbr:"4a460",sgrn:"2e8b57",ssh:"5ee",snn:"a0522d",svr:"c0c0c0",skb:"87ceeb",sTb:"6a5acd",sTgr:"708090",snw:"afa",n:"-ff7f",stb:"4682b4",tn:"d2b48c",t:"-8080",thst:"d8bfd8",tmT:"6347",trqs:"40e0d0",vt:"ee82ee",whT:"5deb3",wht:"",hts:"5f5f5",w:"-",wgrn:"9acd32"};function A(l){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:1,e=n>0?l.toFixed(n).replace(/0+$/,"").replace(/\.$/,""):l.toString();return e||"0"}var N=function(){function l(n,e,t,r){O(this,l);var i=this;function o(a){if(a.startsWith("hsl")){var s=a.match(/([\-\d\.e]+)/g).map(Number),p=y(s,4),f=p[0],u=p[1],d=p[2],b=p[3];b===void 0&&(b=1),f/=360,u/=100,d/=100,i.hsla=[f,u,d,b]}else if(a.startsWith("rgb")){var m=a.match(/([\-\d\.e]+)/g).map(Number),h=y(m,4),v=h[0],g=h[1],E=h[2],k=h[3];k===void 0&&(k=1),i.rgba=[v,g,E,k]}else a.startsWith("#")?i.rgba=l.hexToRgb(a):i.rgba=l.nameToRgb(a)||l.hexToRgb(a)}if(n!==void 0)if(Array.isArray(n))this.rgba=n;else if(t===void 0){var c=n&&""+n;c&&o(c.toLowerCase())}else this.rgba=[n,e,t,r===void 0?1:r]}return R(l,[{key:"printRGB",value:function(e){var t=e?this.rgba:this.rgba.slice(0,3),r=t.map(function(i,o){return A(i,o===3?3:0)});return e?"rgba("+r+")":"rgb("+r+")"}},{key:"printHSL",value:function(e){var t=[360,100,100,1],r=["","%","%",""],i=e?this.hsla:this.hsla.slice(0,3),o=i.map(function(c,a){return A(c*t[a],a===3?3:1)+r[a]});return e?"hsla("+o+")":"hsl("+o+")"}},{key:"printHex",value:function(e){var t=this.hex;return e?t:t.substring(0,7)}},{key:"rgba",get:function(){if(this._rgba)return this._rgba;if(!this._hsla)throw new Error("No color is set");return this._rgba=l.hslToRgb(this._hsla)},set:function(e){e.length===3&&(e[3]=1),this._rgba=e,this._hsla=null}},{key:"rgbString",get:function(){return this.printRGB()}},{key:"rgbaString",get:function(){return this.printRGB(!0)}},{key:"hsla",get:function(){if(this._hsla)return this._hsla;if(!this._rgba)throw new Error("No color is set");return this._hsla=l.rgbToHsl(this._rgba)},set:function(e){e.length===3&&(e[3]=1),this._hsla=e,this._rgba=null}},{key:"hslString",get:function(){return this.printHSL()}},{key:"hslaString",get:function(){return this.printHSL(!0)}},{key:"hex",get:function(){var e=this.rgba,t=e.map(function(r,i){return i<3?r.toString(16):Math.round(r*255).toString(16)});return"#"+t.map(function(r){return r.padStart(2,"0")}).join("")},set:function(e){this.rgba=l.hexToRgb(e)}}],[{key:"hexToRgb",value:function(e){var t=(e.startsWith("#")?e.slice(1):e).replace(/^(\w{3})$/,"$1F").replace(/^(\w)(\w)(\w)(\w)$/,"$1$1$2$2$3$3$4$4").replace(/^(\w{6})$/,"$1FF");if(!t.match(/^([0-9a-fA-F]{8})$/))throw new Error("Unknown hex color; "+e);var r=t.match(/^(\w\w)(\w\w)(\w\w)(\w\w)$/).slice(1).map(function(i){return parseInt(i,16)});return r[3]=r[3]/255,r}},{key:"nameToRgb",value:function(e){var t=e.toLowerCase().replace("at","T").replace(/[aeiouyldf]/g,"").replace("ght","L").replace("rk","D").slice(-5,4),r=I[t];return r===void 0?r:l.hexToRgb(r.replace(/\-/g,"00").padStart(6,"f"))}},{key:"rgbToHsl",value:function(e){var t=y(e,4),r=t[0],i=t[1],o=t[2],c=t[3];r/=255,i/=255,o/=255;var a=Math.max(r,i,o),s=Math.min(r,i,o),p=void 0,f=void 0,u=(a+s)/2;if(a===s)p=f=0;else{var d=a-s;switch(f=u>.5?d/(2-a-s):d/(a+s),a){case r:p=(i-o)/d+(i<o?6:0);break;case i:p=(o-r)/d+2;break;case o:p=(r-i)/d+4;break}p/=6}return[p,f,u,c]}},{key:"hslToRgb",value:function(e){var t=y(e,4),r=t[0],i=t[1],o=t[2],c=t[3],a=void 0,s=void 0,p=void 0;if(i===0)a=s=p=o;else{var f=function(h,v,g){return g<0&&(g+=1),g>1&&(g-=1),g<.16666666666666666?h+(v-h)*6*g:g<.5?v:g<.6666666666666666?h+(v-h)*(.6666666666666666-g)*6:h},u=o<.5?o*(1+i):o+i-o*i,d=2*o-u;a=f(d,u,r+1/3),s=f(d,u,r),p=f(d,u,r-1/3)}var b=[a*255,s*255,p*255].map(Math.round);return b[3]=c,b}}]),l}(),z=function(){function l(){O(this,l),this._events=[]}return R(l,[{key:"add",value:function(e,t,r){e.addEventListener(t,r,!1),this._events.push({target:e,type:t,handler:r})}},{key:"remove",value:function(e,t,r){this._events=this._events.filter(function(i){var o=!0;return e&&e!==i.target&&(o=!1),t&&t!==i.type&&(o=!1),r&&r!==i.handler&&(o=!1),o&&l._doRemove(i.target,i.type,i.handler),!o})}},{key:"destroy",value:function(){this._events.forEach(function(e){return l._doRemove(e.target,e.type,e.handler)}),this._events=[]}}],[{key:"_doRemove",value:function(e,t,r){e.removeEventListener(t,r,!1)}}]),l}();function F(l){var n=document.createElement("div");return n.innerHTML=l,n.firstElementChild}function L(l,n,e){var t=!1;function r(a,s,p){return Math.max(s,Math.min(a,p))}function i(a,s,p){if(p&&(t=!0),!!t){a.preventDefault();var f=n.getBoundingClientRect(),u=f.width,d=f.height,b=s.clientX,m=s.clientY,h=r(b-f.left,0,u),v=r(m-f.top,0,d);e(h/u,v/d)}}function o(a,s){var p=a.buttons===void 0?a.which:a.buttons;p===1?i(a,a,s):t=!1}function c(a,s){a.touches.length===1?i(a,a.touches[0],s):t=!1}l.add(n,"mousedown",function(a){o(a,!0)}),l.add(n,"touchstart",function(a){c(a,!0)}),l.add(window,"mousemove",o),l.add(n,"touchmove",c),l.add(window,"mouseup",function(a){t=!1}),l.add(n,"touchend",function(a){t=!1}),l.add(n,"touchcancel",function(a){t=!1})}var U=`linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0 / 2em 2em,
|
|
8
|
+
linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em / 2em 2em`,B=360,P="keydown",x="mousedown",T="focusin";function _(l,n){return(n||document).querySelector(l)}function G(l){l.preventDefault(),l.stopPropagation()}function H(l,n,e,t,r){l.add(n,P,function(i){e.indexOf(i.key)>=0&&t(i)})}var K=function(){function l(n){O(this,l),this.settings={popup:"right",layout:"default",alpha:!0,editor:!0,editorFormat:"hex",cancelButton:!1,defaultColor:"#0cf"},this._events=new z,this.onChange=null,this.onDone=null,this.onOpen=null,this.onClose=null,this.setOptions(n)}return R(l,[{key:"setOptions",value:function(e){var t=this;if(!e)return;var r=this.settings;function i(s,p,f){for(var u in s)p[u]=s[u]}if(e instanceof HTMLElement)r.parent=e;else{r.parent&&e.parent&&r.parent!==e.parent&&(this._events.remove(r.parent),this._popupInited=!1),i(e,r),e.onChange&&(this.onChange=e.onChange),e.onDone&&(this.onDone=e.onDone),e.onOpen&&(this.onOpen=e.onOpen),e.onClose&&(this.onClose=e.onClose);var o=e.color||e.colour;o&&this._setColor(o)}var c=r.parent;if(c&&r.popup&&!this._popupInited){var a=function(p){return t.openHandler(p)};this._events.add(c,"click",a),H(this._events,c,[" ","Spacebar","Enter"],a),this._popupInited=!0}else e.parent&&!r.popup&&this.show()}},{key:"openHandler",value:function(e){if(this.show()){e&&e.preventDefault(),this.settings.parent.style.pointerEvents="none";var t=e&&e.type===P?this._domEdit:this.domElement;setTimeout(function(){return t.focus()},100),this.onOpen&&this.onOpen(this.colour)}}},{key:"closeHandler",value:function(e){var t=e&&e.type,r=!1;if(!e)r=!0;else if(t===x||t===T){var i=(this.__containedEvent||0)+100;e.timeStamp>i&&(r=!0)}else G(e),r=!0;r&&this.hide()&&(this.settings.parent.style.pointerEvents="",t!==x&&this.settings.parent.focus(),this.onClose&&this.onClose(this.colour))}},{key:"movePopup",value:function(e,t){this.closeHandler(),this.setOptions(e),t&&this.openHandler()}},{key:"setColor",value:function(e,t){this._setColor(e,{silent:t})}},{key:"_setColor",value:function(e,t){if(typeof e=="string"&&(e=e.trim()),!!e){t=t||{};var r=void 0;try{r=new N(e)}catch(o){if(t.failSilently)return;throw o}if(!this.settings.alpha){var i=r.hsla;i[3]=1,r.hsla=i}this.colour=this.color=r,this._setHSLA(null,null,null,null,t)}}},{key:"setColour",value:function(e,t){this.setColor(e,t)}},{key:"show",value:function(){var e=this.settings.parent;if(!e)return!1;if(this.domElement){var t=this._toggleDOM(!0);return this._setPosition(),t}var r=this.settings.template||'<div class="picker_wrapper" tabindex="-1"><div class="picker_arrow"></div><div class="picker_hue picker_slider"><div class="picker_selector"></div></div><div class="picker_sl"><div class="picker_selector"></div></div><div class="picker_alpha picker_slider"><div class="picker_selector"></div></div><div class="picker_editor"><input aria-label="Type a color name or hex value"/></div><div class="picker_sample"></div><div class="picker_done"><button>Ok</button></div><div class="picker_cancel"><button>Cancel</button></div></div>',i=F(r);return this.domElement=i,this._domH=_(".picker_hue",i),this._domSL=_(".picker_sl",i),this._domA=_(".picker_alpha",i),this._domEdit=_(".picker_editor input",i),this._domSample=_(".picker_sample",i),this._domOkay=_(".picker_done button",i),this._domCancel=_(".picker_cancel button",i),i.classList.add("layout_"+this.settings.layout),this.settings.alpha||i.classList.add("no_alpha"),this.settings.editor||i.classList.add("no_editor"),this.settings.cancelButton||i.classList.add("no_cancel"),this._ifPopup(function(){return i.classList.add("popup")}),this._setPosition(),this.colour?this._updateUI():this._setColor(this.settings.defaultColor),this._bindEvents(),!0}},{key:"hide",value:function(){return this._toggleDOM(!1)}},{key:"destroy",value:function(){this._events.destroy(),this.domElement&&this.settings.parent.removeChild(this.domElement)}},{key:"_bindEvents",value:function(){var e=this,t=this,r=this.domElement,i=this._events;function o(s,p,f){i.add(s,p,f)}o(r,"click",function(s){return s.preventDefault()}),L(i,this._domH,function(s,p){return t._setHSLA(s)}),L(i,this._domSL,function(s,p){return t._setHSLA(null,s,1-p)}),this.settings.alpha&&L(i,this._domA,function(s,p){return t._setHSLA(null,null,null,1-p)});var c=this._domEdit;o(c,"input",function(s){t._setColor(this.value,{fromEditor:!0,failSilently:!0})}),o(c,"focus",function(s){var p=this;p.selectionStart===p.selectionEnd&&p.select()}),this._ifPopup(function(){var s=function(u){return e.closeHandler(u)};o(window,x,s),o(window,T,s),H(i,r,["Esc","Escape"],s);var p=function(u){e.__containedEvent=u.timeStamp};o(r,x,p),o(r,T,p),o(e._domCancel,"click",s)});var a=function(p){e._ifPopup(function(){return e.closeHandler(p)}),e.onDone&&e.onDone(e.colour)};o(this._domOkay,"click",a),H(i,r,["Enter"],a)}},{key:"_setPosition",value:function(){var e=this.settings.parent,t=this.domElement;e!==t.parentNode&&e.appendChild(t),this._ifPopup(function(r){getComputedStyle(e).position==="static"&&(e.style.position="relative");var i=r===!0?"popup_right":"popup_"+r;["popup_top","popup_bottom","popup_left","popup_right"].forEach(function(o){o===i?t.classList.add(o):t.classList.remove(o)}),t.classList.add(i)})}},{key:"_setHSLA",value:function(e,t,r,i,o){o=o||{};var c=this.colour,a=c.hsla;[e,t,r,i].forEach(function(s,p){(s||s===0)&&(a[p]=s)}),c.hsla=a,this._updateUI(o),this.onChange&&!o.silent&&this.onChange(c)}},{key:"_updateUI",value:function(e){if(!this.domElement)return;e=e||{};var t=this.colour,r=t.hsla,i="hsl("+r[0]*B+", 100%, 50%)",o=t.hslString,c=t.hslaString,a=this._domH,s=this._domSL,p=this._domA,f=_(".picker_selector",a),u=_(".picker_selector",s),d=_(".picker_selector",p);function b(M,S,C){S.style.left=C*100+"%"}function m(M,S,C){S.style.top=C*100+"%"}b(a,f,r[0]),this._domSL.style.backgroundColor=this._domH.style.color=i,b(s,u,r[1]),m(s,u,1-r[2]),s.style.color=o,m(p,d,1-r[3]);var h=o,v=h.replace("hsl","hsla").replace(")",", 0)"),g="linear-gradient("+[h,v]+")";if(this._domA.style.background=g+", "+U,!e.fromEditor){var E=this.settings.editorFormat,k=this.settings.alpha,w=void 0;switch(E){case"rgb":w=t.printRGB(k);break;case"hsl":w=t.printHSL(k);break;default:w=t.printHex(k)}this._domEdit.value=w}this._domSample.style.color=c}},{key:"_ifPopup",value:function(e,t){this.settings.parent&&this.settings.popup?e&&e(this.settings.popup):t&&t()}},{key:"_toggleDOM",value:function(e){var t=this.domElement;if(!t)return!1;var r=e?"":"none",i=t.style.display!==r;return i&&(t.style.display=r),i}}]),l}();{var D=document.createElement("style");D.textContent='.picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}',document.documentElement.firstElementChild.appendChild(D),K.StyleElement=D}export{K as default};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin="anonymous">
|
|
6
|
+
<link rel="preload" as="style" onload="this.rel='stylesheet'" href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap">
|
|
7
|
+
<link rel="preload" as="font" type="font/eot" href="/admin/static/materialdesignicons-webfont-DttUABo4.eot" crossorigin="anonymous">
|
|
8
|
+
<link rel="preload" as="font" type="font/woff2" href="/admin/static/materialdesignicons-webfont-CYDMK1kx.woff2" crossorigin="anonymous">
|
|
9
|
+
<link rel="preload" as="font" type="font/woff" href="/admin/static/materialdesignicons-webfont-CgCzGbLl.woff" crossorigin="anonymous">
|
|
10
|
+
<link rel="preload" as="font" type="font/ttf" href="/admin/static/materialdesignicons-webfont-D3kAzl71.ttf" crossorigin="anonymous">
|
|
11
|
+
|
|
12
|
+
<meta charset="UTF-8" />
|
|
13
|
+
<link rel="icon" href="{{ favicon_image }}" />
|
|
14
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
15
|
+
<title>{{ title }}</title>
|
|
16
|
+
<script type="module" crossorigin src="/admin/static/index-BeniOHDv.js"></script>
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/admin/static/index-vlBToOhT.css">
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body>
|
|
21
|
+
<div id="app"></div>
|
|
22
|
+
<span id="settings" data-json="{{ settings_json }}"></span>
|
|
23
|
+
</body>
|
|
24
|
+
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from typing import ClassVar, Dict
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
from asgiref.local import Local
|
|
6
|
+
|
|
7
|
+
from admin_panel.utils import DataclassBase
|
|
8
|
+
|
|
9
|
+
_active = Local()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pydantic.dataclasses.dataclass
|
|
13
|
+
class TranslateText(DataclassBase):
|
|
14
|
+
slug: str
|
|
15
|
+
translation_kwargs: dict | None = None
|
|
16
|
+
|
|
17
|
+
def __init__(self, slug: str):
|
|
18
|
+
self.slug = slug
|
|
19
|
+
|
|
20
|
+
@pydantic.model_serializer(mode='plain')
|
|
21
|
+
def serialize_model(self, info: pydantic.SerializationInfo) -> str:
|
|
22
|
+
ctx = info.context or {}
|
|
23
|
+
language_manager = ctx.get('language_manager')
|
|
24
|
+
|
|
25
|
+
if not language_manager:
|
|
26
|
+
raise AttributeError('language_manager is not in context manager for serialization')
|
|
27
|
+
|
|
28
|
+
if not issubclass(type(language_manager), LanguageManager):
|
|
29
|
+
raise AttributeError(f'language_manager "{type(language_manager)}" is not subclass of LanguageManager')
|
|
30
|
+
|
|
31
|
+
return language_manager.get_text(self)
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
lm = getattr(_active, '_language_manager', None)
|
|
35
|
+
if not lm:
|
|
36
|
+
|
|
37
|
+
raise AttributeError(f'language_manager is not in local scope for translation: {locals()}')
|
|
38
|
+
|
|
39
|
+
if not issubclass(type(lm), LanguageManager):
|
|
40
|
+
raise AttributeError(f'language_manager "{lm}" is not subclass of LanguageManager')
|
|
41
|
+
|
|
42
|
+
return lm.get_text(self)
|
|
43
|
+
|
|
44
|
+
def __mod__(self, other):
|
|
45
|
+
if not isinstance(other, dict):
|
|
46
|
+
msg = f'TranslateText only dict is supported trough % operand (slug="{self.slug}" other={type(other)})'
|
|
47
|
+
raise AttributeError(msg)
|
|
48
|
+
self.translation_kwargs = other
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
SQLALCHEMY_SEARCH_HELP_RU = '''<b>Доступные поля для поиска:</b>
|
|
53
|
+
%(fields)s
|
|
54
|
+
|
|
55
|
+
<b>Доступные операторы:</b>
|
|
56
|
+
<b>""</b> - кавычки для точного совпадения
|
|
57
|
+
<b>%%</b> - любая последовательность символов
|
|
58
|
+
<b>_</b> - один любой символ
|
|
59
|
+
'''
|
|
60
|
+
sqlalchemy_search_help_EN = '''<b>Available search fields:</b>
|
|
61
|
+
%(fields)s
|
|
62
|
+
|
|
63
|
+
<b>Available operators:</b>
|
|
64
|
+
<b>""</b> - quotes for exact match
|
|
65
|
+
<b>%%</b> - any sequence of characters
|
|
66
|
+
<b>_</b> - any single character
|
|
67
|
+
'''
|
|
68
|
+
|
|
69
|
+
DEFAULT_PHRASES = {
|
|
70
|
+
'ru': {
|
|
71
|
+
'delete': 'Удалить',
|
|
72
|
+
'delete_confirmation_text': 'Вы уверены, что хотите удалить данные записи?\nДанное действие нельзя отменить.',
|
|
73
|
+
'deleted_successfully': 'Записи успешно удалены.',
|
|
74
|
+
'pk_not_found': 'Поле "%(pk_name)s" не найдено среди переданных данных.',
|
|
75
|
+
'record_not_found': 'Запись по ключу %(pk_name)s=%(pk)s не найдена.',
|
|
76
|
+
'db_error_create': 'Ошибка создания записи в базе данных.',
|
|
77
|
+
'db_error_update': 'Ошибка обновления записи в базе данных.',
|
|
78
|
+
'db_error_retrieve': 'Ошибка получения записи из базы данных.',
|
|
79
|
+
'db_error_list': 'Ошибка получения данных таблицы из базы данных.',
|
|
80
|
+
'connection_refused_error': 'Ошибка подключения к базе данных: %(error)s',
|
|
81
|
+
'search_help': 'Доступные поля для поиска: %(fields)s',
|
|
82
|
+
'sqlalchemy_search_help': SQLALCHEMY_SEARCH_HELP_RU,
|
|
83
|
+
'filters_exception': 'Произошла неизвестная техническая ошибка при фильтрации данных.',
|
|
84
|
+
'method_not_allowed': 'Ошибка, данный метод недоступен.',
|
|
85
|
+
'filter_error': 'Проишла ошибка при фильтрации: {error}',
|
|
86
|
+
},
|
|
87
|
+
'en': {
|
|
88
|
+
'delete': 'Delete',
|
|
89
|
+
'delete_confirmation_text': 'Are you sure you want to delete those records?\nThis action cannot be undone.',
|
|
90
|
+
'deleted_successfully': 'The entries were successfully deleted.',
|
|
91
|
+
'pk_not_found': 'The "%(pk_name)s" field was not found in the submitted data.',
|
|
92
|
+
'record_not_found': 'No record found for %(pk_name)s=%(pk)s.',
|
|
93
|
+
'db_error_update': 'Error updating the record in the database.',
|
|
94
|
+
'db_error_create': 'Error creating a record in the database.',
|
|
95
|
+
'db_error_retrieve': 'Error retrieving the record from the database.',
|
|
96
|
+
'db_error_list': 'Failed to retrieve table data from the database.',
|
|
97
|
+
'connection_refused_error': 'Database connection error: %(error)s',
|
|
98
|
+
'search_help': 'Available search fields: %(fields)s',
|
|
99
|
+
'sqlalchemy_search_help': sqlalchemy_search_help_EN,
|
|
100
|
+
'filters_exception': 'An unknown technical error occurred while filtering data.',
|
|
101
|
+
'method_not_allowed': 'Error, method not allowed. This action is not permitted.',
|
|
102
|
+
'filter_error': 'An error occurred during filtering: {error}',
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def merge_phrases(base: dict[str, dict[str, str]], extra: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]:
|
|
108
|
+
merged = {lang: phrases.copy() for lang, phrases in base.items()}
|
|
109
|
+
|
|
110
|
+
for lang, phrases in extra.items():
|
|
111
|
+
if lang in merged:
|
|
112
|
+
merged[lang].update(phrases)
|
|
113
|
+
|
|
114
|
+
return merged
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class LanguageManager(abc.ABC):
|
|
118
|
+
language: str | None
|
|
119
|
+
|
|
120
|
+
languages: ClassVar[Dict[str, str | TranslateText] | None] = None
|
|
121
|
+
languages_phrases: ClassVar[dict | None] = None
|
|
122
|
+
|
|
123
|
+
def __init__(self, language: str | None):
|
|
124
|
+
self.language = language
|
|
125
|
+
_active._language_manager = self
|
|
126
|
+
|
|
127
|
+
if not self.languages_phrases:
|
|
128
|
+
self.languages_phrases = {}
|
|
129
|
+
|
|
130
|
+
self.languages_phrases = merge_phrases(self.languages_phrases, DEFAULT_PHRASES)
|
|
131
|
+
|
|
132
|
+
def get_text(self, text) -> str:
|
|
133
|
+
if self.languages_phrases and isinstance(text, TranslateText):
|
|
134
|
+
default_lang = list(self.languages_phrases.keys())[0]
|
|
135
|
+
|
|
136
|
+
phrases = self.languages_phrases.get(self.language or default_lang)
|
|
137
|
+
if not phrases:
|
|
138
|
+
phrases = self.languages_phrases[default_lang]
|
|
139
|
+
|
|
140
|
+
translation = phrases.get(text.slug) or text.slug
|
|
141
|
+
if text.translation_kwargs:
|
|
142
|
+
translation %= text.translation_kwargs
|
|
143
|
+
return translation
|
|
144
|
+
|
|
145
|
+
return text
|
admin_panel/utils.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from pydantic import TypeAdapter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_logger():
|
|
8
|
+
try:
|
|
9
|
+
# pylint: disable=import-outside-toplevel
|
|
10
|
+
import structlog
|
|
11
|
+
return structlog.get_logger('admin_panel')
|
|
12
|
+
except ImportError:
|
|
13
|
+
return logging.getLogger('admin_panel')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DeserializeAction:
|
|
17
|
+
CREATE = 0
|
|
18
|
+
UPDATE = 1
|
|
19
|
+
TABLE_ACTION = 2
|
|
20
|
+
FILTERS = 3
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DataclassBase:
|
|
24
|
+
def model_dump(self, *args, **kwargs) -> dict:
|
|
25
|
+
adapter = TypeAdapter(type(self))
|
|
26
|
+
return adapter.dump_python(self, *args, **kwargs)
|
|
27
|
+
|
|
28
|
+
def to_dict(self, keep_none=True) -> dict:
|
|
29
|
+
data = self.model_dump()
|
|
30
|
+
return {
|
|
31
|
+
k: v for k, v in data.items()
|
|
32
|
+
if v is not None and not keep_none
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def humanize_field_name(name: str) -> str:
|
|
37
|
+
# Convert snake_case / kebab-case / mixed tokens to Title Case with acronyms preserved
|
|
38
|
+
s = name.replace("-", "_")
|
|
39
|
+
parts = [p for p in s.split("_") if p]
|
|
40
|
+
|
|
41
|
+
def cap(token: str) -> str:
|
|
42
|
+
# Keep common acronyms uppercase
|
|
43
|
+
if token.lower() in {"id", "ip", "url", "api", "http", "https", "h2h"}:
|
|
44
|
+
return token.upper()
|
|
45
|
+
# If token contains digits, capitalize first letter only (e.g. "h2h" -> "H2h")
|
|
46
|
+
if re.search(r"\d", token):
|
|
47
|
+
return token[:1].upper() + token[1:].lower()
|
|
48
|
+
return token[:1].upper() + token[1:].lower()
|
|
49
|
+
|
|
50
|
+
return " ".join(cap(p) for p in parts)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brilliance-admin
|
|
3
|
+
Version: 0.42.0
|
|
4
|
+
Summary: General-purpose admin panel framework powered by FastAPI. Some call it heavenly in its brilliance.
|
|
5
|
+
License-Expression: AGPL-3.0
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: asgiref>=3.11.0
|
|
10
|
+
Requires-Dist: fastapi>=0.115.8
|
|
11
|
+
Requires-Dist: jinja2>=3.1.6
|
|
12
|
+
Provides-Extra: example
|
|
13
|
+
Requires-Dist: uvicorn>=0.34.0; extra == "example"
|
|
14
|
+
Requires-Dist: faker>=38.2.0; extra == "example"
|
|
15
|
+
Requires-Dist: pyjwt>=2.10.1; extra == "example"
|
|
16
|
+
Requires-Dist: structlog>=25.5.0; extra == "example"
|
|
17
|
+
Requires-Dist: rich>=14.2.0; extra == "example"
|
|
18
|
+
Provides-Extra: tests
|
|
19
|
+
Requires-Dist: pytest>=8.4.2; extra == "tests"
|
|
20
|
+
Requires-Dist: pytest-asyncio>=1.2.0; extra == "tests"
|
|
21
|
+
Requires-Dist: httpx>=0.28.1; extra == "tests"
|
|
22
|
+
Requires-Dist: pytest-mock>=3.15.1; extra == "tests"
|
|
23
|
+
Requires-Dist: sqlalchemy>=2.0.41; extra == "tests"
|
|
24
|
+
Requires-Dist: aiosqlite>=0.22.1; extra == "tests"
|
|
25
|
+
Requires-Dist: factory-boy>=3.3.3; extra == "tests"
|
|
26
|
+
Requires-Dist: pyjwt>=2.10.1; extra == "tests"
|
|
27
|
+
Provides-Extra: scalar
|
|
28
|
+
Requires-Dist: scalar-fastapi>=1.5.0; extra == "scalar"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
<img src="https://github.com/brilliance-admin/backend-python/blob/main/example/static/logo-outline.png?raw=true"
|
|
33
|
+
alt="Brilliance Admin"
|
|
34
|
+
width="600">
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div align="center">
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/brilliance-admin/)
|
|
40
|
+
[](https://github.com/brilliance-admin/backend-python/blob/main/LICENSE)
|
|
41
|
+
[](https://github.com/brilliance-admin/backend-python/actions)
|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
General-purpose admin panel framework powered by FastAPI. Some call it heavenly in its brilliance.
|
|
46
|
+
|
|
47
|
+
- Serves a prebuilt SPA frontend as static files
|
|
48
|
+
- Generates schemas for frontend sections on the backend
|
|
49
|
+
- Provides a backend-driven API for admin interfaces
|
|
50
|
+
- Designed for fast data management and data viewing from any sources
|
|
51
|
+
- Inspired by Django Admin and Django REST Framework
|
|
52
|
+
- Focused on minimal boilerplate and simplified backend-controlled configuration
|
|
53
|
+
|
|
54
|
+
### [Live Demo](https://brilliance-admin.com/) | [Example App](https://github.com/brilliance-admin/backend-python/tree/main/example) | Documentation (todo)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Features:
|
|
58
|
+
|
|
59
|
+
* Tables with full CRUD support, including filtering, sorting, and pagination.
|
|
60
|
+
* Ability to define custom table actions with forms, response messages, and file uploads.
|
|
61
|
+
* SQLAlchemy integration with automatic field generation from models.
|
|
62
|
+
* Authorization via any account data source.
|
|
63
|
+
* Localization support with language selection in the interface.
|
|
64
|
+
|
|
65
|
+
## How to use it
|
|
66
|
+
|
|
67
|
+
Installation:
|
|
68
|
+
``` shell
|
|
69
|
+
pip install brilliance-admin
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You need to generate `AdminSchema` instance:
|
|
73
|
+
``` python
|
|
74
|
+
from admin_panel import schema
|
|
75
|
+
|
|
76
|
+
admin_schema = schema.AdminSchema(
|
|
77
|
+
title='Admin Panel',
|
|
78
|
+
auth=YourAdminAuthentication(),
|
|
79
|
+
groups=[
|
|
80
|
+
schema.Group(
|
|
81
|
+
slug='example',
|
|
82
|
+
title='Example',
|
|
83
|
+
icon='mdi-star',
|
|
84
|
+
categories=[
|
|
85
|
+
CategoryExample(),
|
|
86
|
+
]
|
|
87
|
+
),
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
admin_app = admin_schema.generate_app()
|
|
92
|
+
|
|
93
|
+
# Your FastAPI app
|
|
94
|
+
app = FastAPI()
|
|
95
|
+
app.mount('/admin', admin_app)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### SQLAlchemy integration
|
|
99
|
+
Brilliance Admin supports automatic schema generation from SQLAlchemy and provides a ready-made CRUD implementation for tables.
|
|
100
|
+
|
|
101
|
+
``` python
|
|
102
|
+
from admin_panel import sqlalchemy
|
|
103
|
+
from admin_panel.translations import TranslateText as _
|
|
104
|
+
|
|
105
|
+
from your_project.models import Terminal
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
|
|
109
|
+
db_async_session = async_sessionmaker
|
|
110
|
+
model = Terminal
|
|
111
|
+
title = _('terminals')
|
|
112
|
+
icon = 'mdi-console-network-outline'
|
|
113
|
+
|
|
114
|
+
ordering_fields = ['id']
|
|
115
|
+
search_fields = ['id', 'title']
|
|
116
|
+
|
|
117
|
+
table_schema = sqlalchemy.SQLAlchemyFieldsSchema(
|
|
118
|
+
model=Terminal,
|
|
119
|
+
list_display=['id', 'merchant_id'],
|
|
120
|
+
)
|
|
121
|
+
table_filters = sqlalchemy.SQLAlchemyFieldsSchema(
|
|
122
|
+
model=Terminal,
|
|
123
|
+
fields=['id', 'created_at'],
|
|
124
|
+
created_at=schema.DateTimeField(range=True),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
category = TerminalAdmin()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Now, the `TerminalAdmin` instance can be passed to `categories`.
|
|
132
|
+
|
|
133
|
+
### Can be used both via inheritance and instancing
|
|
134
|
+
|
|
135
|
+
For `SQLAlchemyFieldsSchema`
|
|
136
|
+
|
|
137
|
+
``` python
|
|
138
|
+
class TerminalTableSchema(sqlalchemy.SQLAlchemyFieldsSchema):
|
|
139
|
+
model = Terminal
|
|
140
|
+
fields = ['id', 'created_at']
|
|
141
|
+
|
|
142
|
+
created_at=schema.DateTimeField(range=True)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
|
|
146
|
+
table_schema = TerminalTableSchema()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
And `SQLAlchemyAdmin` category schema itself
|
|
150
|
+
|
|
151
|
+
> If `table_schema` is not specified, it will be generated automatically and will include all discovered fields and relationships of the table in the output.
|
|
152
|
+
|
|
153
|
+
``` python
|
|
154
|
+
category = sqlalchemy.SQLAlchemyAdmin(db_async_session=async_sessionmaker, model=Terminal)
|
|
155
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
admin_panel/__init__.py,sha256=uvAJW6E2Sjmsk7PzicJ3vywgjeEq_irxlnxGOwbHOIk,173
|
|
2
|
+
admin_panel/auth.py,sha256=XYqcSqUVXqkqON4bASMXPYsJY-rbOU-D7QzI2k1CXek,668
|
|
3
|
+
admin_panel/docs.py,sha256=fKeJKuiCCi1jHRmNcmkuDD6_2di7bwc6-w8V1VCTu0s,1231
|
|
4
|
+
admin_panel/exceptions.py,sha256=klGOg64g0NQoc8KTdMhN94o5NbGGjURiJpoWFzHj1qU,962
|
|
5
|
+
admin_panel/translations.py,sha256=fh60w2MfC4Aqq_rV33Y9SGUHPpoAMtOI1eZu5iAqpp4,6211
|
|
6
|
+
admin_panel/utils.py,sha256=5mnwSiaMTbqGDTXTGmcoRf15uKw7oeJDKBI67O2Ruvk,1425
|
|
7
|
+
admin_panel/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
admin_panel/api/routers.py,sha256=j86iF7TnHeBGA1CfLQTECgHEtyoHq3qBOrqTu3M8rFs,770
|
|
9
|
+
admin_panel/api/utils.py,sha256=TBktoVG2xmCftfd40XC84nb0F_yYLsw51Bvv8spn17g,994
|
|
10
|
+
admin_panel/api/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
admin_panel/api/views/auth.py,sha256=CAcSSBEksY5CCYbOZa22IpKYqnOOgxopFvGZveYBxSg,1113
|
|
12
|
+
admin_panel/api/views/autocomplete.py,sha256=0Hh63P3aEmAc4l9sRLbBIVI2fbb3YH7purGjTtNC1CM,1478
|
|
13
|
+
admin_panel/api/views/graphs.py,sha256=ZiwgJSSUCARBRXgGZI8ha3h74mZSGbS40SDsBsTWftM,1321
|
|
14
|
+
admin_panel/api/views/index.py,sha256=Q3pgBwCLwwdolWQp6Pmc0Cexc2FfQQGu6TaNOLFyLp4,1233
|
|
15
|
+
admin_panel/api/views/schema.py,sha256=NdutSK4ydidJtegaOvdoK1-W8Mk2xzf6vtB0Nwq7uPg,1004
|
|
16
|
+
admin_panel/api/views/settings.py,sha256=k2QyHfEIT9EBXQ2R7nDfNhhCSShOIPNO2Sd6dTRviQc,1151
|
|
17
|
+
admin_panel/api/views/table.py,sha256=5VjD0Ss2leGmUa37bUaSafr97gwXxy4ZGJZKwR-5qJo,5892
|
|
18
|
+
admin_panel/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
admin_panel/integrations/sqlalchemy/__init__.py,sha256=tIegMU2eIW_zA-PdblCXCjenHAY4sGBUWKuz97ns4gE,275
|
|
20
|
+
admin_panel/integrations/sqlalchemy/auth.py,sha256=EJmNNEpsjROnTgL1q304_i6Al3a2lKSmQh7YUzRcl5w,4890
|
|
21
|
+
admin_panel/integrations/sqlalchemy/autocomplete.py,sha256=ao2WgJ10tlegizLUMHaoz0225BCX6hRGARAZDP0O67I,1401
|
|
22
|
+
admin_panel/integrations/sqlalchemy/fields.py,sha256=tlxsBlRAMeL9IzXG445FrAxws49wu7-VYtMuTp2oGXY,9372
|
|
23
|
+
admin_panel/integrations/sqlalchemy/fields_schema.py,sha256=raEd2zFseQHpdJS8SqYcOcTk1pNq7g0zwYdpwwHmFBg,10884
|
|
24
|
+
admin_panel/integrations/sqlalchemy/table/__init__.py,sha256=g_in2pLTi8UQnT5uNFA8mLW5mxlT84irQ7yVaP_OSS4,605
|
|
25
|
+
admin_panel/integrations/sqlalchemy/table/base.py,sha256=r8LtEZH5AhIVRsi2AcrJ8kQWk8qAUSCLDmOXNAn4qnE,4853
|
|
26
|
+
admin_panel/integrations/sqlalchemy/table/create.py,sha256=qjdfqC4KP7Ktxz_I2M7xm-8nRyvsmcqXw4QESO1trs0,2663
|
|
27
|
+
admin_panel/integrations/sqlalchemy/table/delete.py,sha256=2MJ-gE0yRTRwQRKNuXIL78tiZscu_2qzs7sqyOIlHok,716
|
|
28
|
+
admin_panel/integrations/sqlalchemy/table/list.py,sha256=80wl9Ja1_Dc6p_6zTi-trxg0Rq69YCJ9_4dJvcyTJZw,6662
|
|
29
|
+
admin_panel/integrations/sqlalchemy/table/retrieve.py,sha256=kuHyMGvJhTlWyHuB1wXNl6HelAkMzQjhcMGZwOHtSK0,2115
|
|
30
|
+
admin_panel/integrations/sqlalchemy/table/update.py,sha256=O1hpzEP04tUv_HUD8ChCBqYbjjnrRc2houvJbNUYEzY,3518
|
|
31
|
+
admin_panel/schema/__init__.py,sha256=X-izShvv84jkFU47WfpUwtvRh3NOv570iUB3NRNEIDU,248
|
|
32
|
+
admin_panel/schema/admin_schema.py,sha256=_bQmDTnBzq_uxEAgQ-Dqra5rTewtaLY_HboB3__n80o,6224
|
|
33
|
+
admin_panel/schema/category.py,sha256=ph7rndUK21lxRDToYpcYnvIyfGwPP-HESST7DxObdqU,4108
|
|
34
|
+
admin_panel/schema/group.py,sha256=h4AuiGEsy3yE1Oz5YlJ6lOhDEzIgi4WgtmLZ4JRgrgg,2238
|
|
35
|
+
admin_panel/schema/graphs/__init__.py,sha256=qvmZv9QWdiutPtN5VYQLYbsjY2SOg8p_XRaz2rUlIxY,44
|
|
36
|
+
admin_panel/schema/graphs/category_graphs.py,sha256=KlJhnMTT9fNKSJqqMea9WpIHSnWuuG-usnyMoVIEPrM,1469
|
|
37
|
+
admin_panel/schema/table/__init__.py,sha256=vuRw8HBuak2LaTZi2dNn5YOrJPalQps-O3Ht-d0AZV4,378
|
|
38
|
+
admin_panel/schema/table/admin_action.py,sha256=wDDvJSr8f4jvCgf2DHmBUMZTFdNIdSQ8EZtBOG-FhC0,2016
|
|
39
|
+
admin_panel/schema/table/category_table.py,sha256=wGU1-n4_DvZLLfJaDXjcwPKdnfXH4oYU_L0RCXgp3f0,6414
|
|
40
|
+
admin_panel/schema/table/fields_schema.py,sha256=fmRG7chuvSl94Zw-oN1ljqsan9sGbGPHN_KVnCfi8UI,8241
|
|
41
|
+
admin_panel/schema/table/table_models.py,sha256=7UQyqZcA-2sz0731EAkvhPKDuZbxCqUTrdykWZdxyvc,1013
|
|
42
|
+
admin_panel/schema/table/fields/__init__.py,sha256=RW-sIFTAaSQo4mMR6iWtnefogWPjmg6KAsDwe9mKW1k,291
|
|
43
|
+
admin_panel/schema/table/fields/base.py,sha256=Po4M04oF4OuPFqYRH28SlIpH8C3BEBlGuaR_K6MdFRs,7789
|
|
44
|
+
admin_panel/schema/table/fields/function_field.py,sha256=MlPCHtc10-WWcnZ0jX7rGoTr6mG1HhloAKW_cB69GAI,1765
|
|
45
|
+
admin_panel/static/favicon.jpg,sha256=_np-jvdgK1qgkqbbz9yecZ-PoR4iMwltHthQ2gYebYk,12991
|
|
46
|
+
admin_panel/static/index-BeniOHDv.js,sha256=U5N_dXe2QOlSKVVwJ0Ghu8hNIiBD9QF-bK_04MAgucc,3201882
|
|
47
|
+
admin_panel/static/index-vlBToOhT.css,sha256=hoVCpcStTHdAVRm37k1umrNdXjOwIIveu9lxk4ocpEc,983028
|
|
48
|
+
admin_panel/static/materialdesignicons-webfont-CYDMK1kx.woff2,sha256=5S1g9kJnzaoIQitQurXUW9NeZisDua91F5zq4ArF_Is,385360
|
|
49
|
+
admin_panel/static/materialdesignicons-webfont-CgCzGbLl.woff,sha256=SNPuxqtw3HoZCPm6LyCOClhxi57hbj9qvbXbT0Yfolg,561776
|
|
50
|
+
admin_panel/static/materialdesignicons-webfont-D3kAzl71.ttf,sha256=vXJaejiTnltZkE4benJlkZ7OwlYWbs5p1RXCEAUWWQc,1243500
|
|
51
|
+
admin_panel/static/materialdesignicons-webfont-DttUABo4.eot,sha256=hhrqBUjMya-Y3sUgvjQLji1L65e6NIk6ya9RGkSeP6I,1243720
|
|
52
|
+
admin_panel/static/vanilla-picker-B6E6ObS_.js,sha256=aVVU5PLkFXbK22gMndNsa5QNWtZxQBEqtd2Fp8HYfwE,18804
|
|
53
|
+
admin_panel/static/tinymce/tinymce.min.js,sha256=4KlST3LusLGTHzCqhWmoENGfb4aeSvmj9IlL-v6ATeg,1171290
|
|
54
|
+
admin_panel/static/tinymce/dark-first/content.min.css,sha256=tCWU8dU3Y4eejwO9fjZ22eIwrq8q5iFSlyR8FlQ8-dU,4788
|
|
55
|
+
admin_panel/static/tinymce/dark-first/skin.min.css,sha256=XaCMxHAKdwqA0HyM2l_vvA1KRwhOC7bbX3Bz2z0f1jw,50376
|
|
56
|
+
admin_panel/static/tinymce/dark-slim/content.min.css,sha256=fyLDj0yRBtk8ypK6LL3aI2PHyH6x7ISDncpbHOuvwPI,4730
|
|
57
|
+
admin_panel/static/tinymce/dark-slim/skin.min.css,sha256=tzEwzhO0ToVn_l1AkBRK6vwz9dSqjEpQbTNao12Kqy0,49965
|
|
58
|
+
admin_panel/static/tinymce/img/example.png,sha256=R18xquUdQyQvA6dFZUIXkLucVO2mMJpgV1oHI4cFQJ0,14708
|
|
59
|
+
admin_panel/static/tinymce/img/tinymce.woff2,sha256=_XmHMFsFYH9PlgREy6LOzYYFgvs2fGIMcr2vXt2Q-ik,15764
|
|
60
|
+
admin_panel/static/tinymce/lightgray/content.min.css,sha256=UgkDCoTokZ99doSjtoycaZAZVjO00I1XikWjBpWf9NI,3193
|
|
61
|
+
admin_panel/static/tinymce/lightgray/skin.min.css,sha256=ypP9oqgJwhKl2-B_ATE6uC77pqPjVYP6vz7EzBuiiSU,38232
|
|
62
|
+
admin_panel/static/tinymce/lightgray/fonts/tinymce.woff,sha256=o69L1waoOPHkqgihbpfjT_qTB8PJsZJeJTR7aL8IH8M,7664
|
|
63
|
+
admin_panel/static/tinymce/plugins/accordion/plugin.js,sha256=jf3gA0MkE2Gshhe8pTJP5JUVI56G3YlBZQSoevPWeuc,1251
|
|
64
|
+
admin_panel/static/tinymce/plugins/accordion/css/accordion.css,sha256=u5UQkMNA9fboQgOOuJCgoJ3Dm-9vDYDJDAmpPbB-yWM,282
|
|
65
|
+
admin_panel/static/tinymce/plugins/codesample/css/prism.css,sha256=exAdMtHbvwW7-DEs567MX65Fq1aJQTfREP5pw8gW-AY,1736
|
|
66
|
+
admin_panel/static/tinymce/plugins/customLink/plugin.js,sha256=illBNpnHDkBsLG6wo_jDPF6z7CGnO1MQWUoDwZKy6vQ,5589
|
|
67
|
+
admin_panel/static/tinymce/plugins/customLink/css/link.css,sha256=gh5nvY8Z92hJfCEBPnIm4jIPCcKKbJnab-30oIfX7Hc,56
|
|
68
|
+
admin_panel/templates/index.html,sha256=7a3DWqQxaHhm0_NfPmae5D55rRKPkrZx8fmsvDaxy8Y,1294
|
|
69
|
+
brilliance_admin-0.42.0.dist-info/licenses/LICENSE,sha256=PjeDRXGbVLtKul5Xpfco_6CyB6bYGWVVPrO0oubquuM,727
|
|
70
|
+
brilliance_admin-0.42.0.dist-info/METADATA,sha256=kGlOILv4XWlso252it6QiogEp9XtOco_2i_N0NlXj2M,4996
|
|
71
|
+
brilliance_admin-0.42.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
72
|
+
brilliance_admin-0.42.0.dist-info/top_level.txt,sha256=saSuhWhjU9d5_tZnnBG6GYVQY7ywThehjbEePImWik8,12
|
|
73
|
+
brilliance_admin-0.42.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2025 Your Name
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published
|
|
8
|
+
by the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
admin_panel
|