commit 217aa7854a12e29a97fbeaad0e2a8207535af6bd Author: Markus Birth Date: Tue Oct 18 16:32:20 2022 +0200 Initial commit diff --git a/aremaproxy.php b/aremaproxy.php new file mode 100755 index 0000000..7a47626 --- /dev/null +++ b/aremaproxy.php @@ -0,0 +1,76 @@ + [ + 'method' => 'GET', + 'header' => ['Accept: application/json'], + ], +]); + +// default parameters for all requests +$params = [ + 'userid' => AREMA_USERNAME, + 'password' => AREMA_PASSWORD, + 'encrypted' => 'false', +]; + +// request-specific handling +switch ($_GET['fetch']) { + default: + case 'joblist': + $url = AREMA_URL . '/rest/jobs'; + $params['limit'] = 50; + $params['order_by'] = 'job_id_desc'; + $params['history'] = 'false'; + break; + + case 'jobdetails': + $url = AREMA_URL . '/rest/jobs/' . intval($_GET['jobid']); + break; + + case 'jobprogress': + $url = AREMA_URL . '/rest/jobs/' . intval($_GET['jobid']) . '/progress'; + break; +} + +$params_encoded = []; +foreach ($params as $k => $v) { + $params_encoded[] = $k . '=' . rawurlencode($v); +} +$params_str = implode('&', $params_encoded); +$url .= '?' . $params_str; +$cache_token = sha1($url); + +// Clean cache +$cached_files = glob(CACHE_DIR . '/*'); +foreach ($cached_files as $f) { + $mtime = filemtime($f); + if ((time() - $mtime) > CACHE_SECONDS) { + // cached file outdated --> delete + unlink($f); + } +} + +$cache_file = CACHE_DIR . '/' . $cache_token; +if (file_exists($cache_file)) { + // Get cached data + $data = file_get_contents($cache_file); +} else { + // Query Arema + $data = file_get_contents($url, false, $ctx); + + // Cache answer + file_put_contents($cache_file, $data); +} + +// Output JSON +header('Content-Type: application/json'); +echo $data; diff --git a/assets/custom.css b/assets/custom.css new file mode 100755 index 0000000..fec2e23 --- /dev/null +++ b/assets/custom.css @@ -0,0 +1,23 @@ +.uk-table th:last-child { + text-align: center; +} + +tr.state10 { + color: #ccc; +} + +span.state2 { + background-color: #fc0; +} + +span.state10 { + background-color: #0c2; +} + +span.state30 { + background-color: #82c; +} + +span.state11, span.state12, span.state13, span.state14, span.state20 { + background-color: #f22; +} diff --git a/assets/main.js b/assets/main.js new file mode 100755 index 0000000..e28fd25 --- /dev/null +++ b/assets/main.js @@ -0,0 +1,291 @@ +class AremaRest +{ + fetchPromise(url) + { + return fetch(url, { + method: 'GET', + headers: {'Accept': 'application/json'} + }) + .catch(err => { + console.error('ERROR fetching data: %o', err); + }) + .then(function (response) { + return response.json(); + }) + .catch(err => { + console.error('ERROR converting data to JSON: %o', err); + }) + .then((data) => { + //console.log('JSON data: %o', data); + return this.fixXMLJSON(data); + }) + .catch(err => { + console.error('ERROR fixing/enriching JSON: %o', err); + }); + } + + /** + * Arema converts XML to JSON, so: + * value1 + * value2 + * becomes + * "param": [ + * { "@name": "key1", "$": "value1" }, + * { "@name": "key2", "$": "value2" }, + * ] + * This method tries to fix that (assuming all names/keys are unique). + */ + fixXMLJSON(data) + { + if (!Array.isArray(data) && typeof data !== 'object') { + return data; + } + for (var i in data) { + if (!data.hasOwnProperty(i)) { + continue; + } + if (Array.isArray(data[i])) { + // console.log('Found Array: %o', data[i]); + var key = false; + if (data[i][0]['@id']) { + key = '@id'; + } else if (data[i][0]['@name']) { + key = '@name'; + } + if (!key) { + // This doesn't seem to be the correct structure --> ignore + //console.log('Ignoring structure: %o', data[i]); + data[i] = this.fixXMLJSON(data[i]); + continue; + } + var new_assoc = {}; + for (var j=0; j { + console.log('updateDetails got data: %o', data); + var cur = this.state; + cur.name = data.job.param.title; + cur.state_numeric = data.job.state; + cur.state = this.job_states[data.job.state]; + if (data.job.state == 10) { + cur.percent = 100; + } + this.setState(cur); + if (data.job.state < 10) { + // job still running, query progress in 1 second + setTimeout(() => { this.updateProgress(); }, 1000); + } + }) + .catch(err => { + console.error('ERROR processing updateDetails(): %o', err); + setTimeout(() => { this.updateDetails(); }, 1000); + }); + } + + updateProgress() + { + var req = new AremaRest(); + req.getJobProgress(this.state.jobId) + .then((data) => { + console.log('updateProgress got data: %o', data); + var cur = this.state; + + cur.state_numeric = data.state; + cur.state = this.job_states[data.state]; + + // calculate percentage + var num_steps = 0; + var overall_percent = 0; + for (var step in data.job_steps.job_step) { + if (!data.job_steps.job_step.hasOwnProperty(step)) { + continue; + } + var this_step = data.job_steps.job_step[step]; + //console.log('PCalc: Status: %o, Percent: %o', this_step.state, this_step.percent); + num_steps++; + if (this_step.state == 10) { + overall_percent += 100; + } else { + overall_percent += parseInt(this_step.percent); + } + } + cur.percent = Math.floor( overall_percent / num_steps ); + console.log('calculated: %o', cur.percent); + + this.setState(cur); + if (data.state < 10) { + // job still running, update progress in 5 seconds + setTimeout(() => { this.updateProgress(); }, 5000); + } + }) + .catch(err => { + console.error('ERROR processing updateProgress(): %o', err); + setTimeout(() => { this.updateProgress(); }, 1000); + }); + } + + componentDidMount() + { + this.updateDetails(); + } + + render() + { + //console.log('Obj: %o', this); + return React.createElement('tr', { className: 'state'+this.state.state_numeric }, + React.createElement('td', null, this.state.jobId), + React.createElement('td', null, this.state.name), + React.createElement('td', null, + React.createElement('span', { className: 'uk-label state'+this.state.state_numeric }, this.state.state) + ), + React.createElement('td', { style: { verticalAlign: 'middle' } }, + React.createElement('progress', { className: 'uk-progress uk-progress-striped uk-active', value: this.state.percent, max: 100 }, this.state.percent + '%' ) + ) + ); + } +} + +class JobList extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + jobIds: [] + }; + } + + updateJobs() + { + var req = new AremaRest(); + req.getJoblist() + .then((data) => { + console.log('updateJobs got data: %o', data); + this.setState({ + jobIds: data.jobs.job + }); + setTimeout(() => { this.updateJobs(); }, 11000); + }) + .catch(err => { + console.error('ERROR processing updateJobs(): %o', err); + setTimeout(() => { this.updateJobs(); }, 2000); + }); + } + + componentDidMount() + { + this.updateJobs(); + window.setJobList = (data) => { + this.setState(data); + } + window.appendJobList = (data) => { + var list = this.state; + console.log('Current state: %o', list); + for (var i=0; i + + + AREMA JobMonitor + + + + + + + + + + + +
+
+
+
+
+ + diff --git a/reactjs/babel.js b/reactjs/babel.js new file mode 100755 index 0000000..c86b8bd --- /dev/null +++ b/reactjs/babel.js @@ -0,0 +1,61686 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Babel"] = factory(); + else + root["Babel"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ((function(modules) { + // Check all modules for deduplicated modules + for(var i in modules) { + if(Object.prototype.hasOwnProperty.call(modules, i)) { + switch(typeof modules[i]) { + case "function": break; + case "object": + // Module can be created from a template + modules[i] = (function(_m) { + var args = _m.slice(1), fn = modules[_m[0]]; + return function (a,b,c) { + fn.apply(this, [a,b,c].concat(args)); + }; + }(modules[i])); + break; + default: + // Module is a copy of another module + modules[i] = modules[modules[i]]; + break; + } + } + } + return modules; +}([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.version = exports.buildExternalHelpers = exports.availablePresets = exports.availablePlugins = undefined; + + var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.transform = transform; + exports.transformFromAst = transformFromAst; + exports.registerPlugin = registerPlugin; + exports.registerPlugins = registerPlugins; + exports.registerPreset = registerPreset; + exports.registerPresets = registerPresets; + exports.transformScriptTags = transformScriptTags; + exports.disableScriptTags = disableScriptTags; + + var _babelCore = __webpack_require__(290); + + var Babel = _interopRequireWildcard(_babelCore); + + var _transformScriptTags = __webpack_require__(629); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + var isArray = Array.isArray || function (arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + + /** + * Loads the given name (or [name, options] pair) from the given table object + * holding the available presets or plugins. + * + * Returns undefined if the preset or plugin is not available; passes through + * name unmodified if it (or the first element of the pair) is not a string. + */ + function loadBuiltin(builtinTable, name) { + if (isArray(name) && typeof name[0] === 'string') { + if (builtinTable.hasOwnProperty(name[0])) { + return [builtinTable[name[0]]].concat(name.slice(1)); + } + return; + } else if (typeof name === 'string') { + return builtinTable[name]; + } + // Could be an actual preset/plugin module + return name; + } + + /** + * Parses plugin names and presets from the specified options. + */ + function processOptions(options) { + // Parse preset names + var presets = (options.presets || []).map(function (presetName) { + var preset = loadBuiltin(availablePresets, presetName); + + if (preset) { + // workaround for babel issue + // at some point, babel copies the preset, losing the non-enumerable + // buildPreset key; convert it into an enumerable key. + if (isArray(preset) && _typeof(preset[0]) === 'object' && preset[0].hasOwnProperty('buildPreset')) { + preset[0] = _extends({}, preset[0], { buildPreset: preset[0].buildPreset }); + } + } else { + throw new Error('Invalid preset specified in Babel options: "' + presetName + '"'); + } + return preset; + }); + + // Parse plugin names + var plugins = (options.plugins || []).map(function (pluginName) { + var plugin = loadBuiltin(availablePlugins, pluginName); + + if (!plugin) { + throw new Error('Invalid plugin specified in Babel options: "' + pluginName + '"'); + } + return plugin; + }); + + return _extends({ + babelrc: false + }, options, { + presets: presets, + plugins: plugins + }); + } + + function transform(code, options) { + return Babel.transform(code, processOptions(options)); + } + + function transformFromAst(ast, code, options) { + return Babel.transformFromAst(ast, code, processOptions(options)); + } + var availablePlugins = exports.availablePlugins = {}; + var availablePresets = exports.availablePresets = {}; + var buildExternalHelpers = exports.buildExternalHelpers = Babel.buildExternalHelpers; + /** + * Registers a named plugin for use with Babel. + */ + function registerPlugin(name, plugin) { + if (availablePlugins.hasOwnProperty(name)) { + console.warn('A plugin named "' + name + '" is already registered, it will be overridden'); + } + availablePlugins[name] = plugin; + } + /** + * Registers multiple plugins for use with Babel. `newPlugins` should be an object where the key + * is the name of the plugin, and the value is the plugin itself. + */ + function registerPlugins(newPlugins) { + Object.keys(newPlugins).forEach(function (name) { + return registerPlugin(name, newPlugins[name]); + }); + } + + /** + * Registers a named preset for use with Babel. + */ + function registerPreset(name, preset) { + if (availablePresets.hasOwnProperty(name)) { + console.warn('A preset named "' + name + '" is already registered, it will be overridden'); + } + availablePresets[name] = preset; + } + /** + * Registers multiple presets for use with Babel. `newPresets` should be an object where the key + * is the name of the preset, and the value is the preset itself. + */ + function registerPresets(newPresets) { + Object.keys(newPresets).forEach(function (name) { + return registerPreset(name, newPresets[name]); + }); + } + + // All the plugins we should bundle + registerPlugins({ + 'check-es2015-constants': __webpack_require__(66), + 'external-helpers': __webpack_require__(322), + 'inline-replace-variables': __webpack_require__(323), + 'syntax-async-functions': __webpack_require__(67), + 'syntax-async-generators': __webpack_require__(195), + 'syntax-class-constructor-call': __webpack_require__(196), + 'syntax-class-properties': __webpack_require__(197), + 'syntax-decorators': __webpack_require__(125), + 'syntax-do-expressions': __webpack_require__(198), + 'syntax-exponentiation-operator': __webpack_require__(199), + 'syntax-export-extensions': __webpack_require__(200), + 'syntax-flow': __webpack_require__(126), + 'syntax-function-bind': __webpack_require__(201), + 'syntax-function-sent': __webpack_require__(325), + 'syntax-jsx': __webpack_require__(127), + 'syntax-object-rest-spread': __webpack_require__(202), + 'syntax-trailing-function-commas': __webpack_require__(128), + 'transform-async-functions': __webpack_require__(326), + 'transform-async-to-generator': __webpack_require__(129), + 'transform-async-to-module-method': __webpack_require__(328), + 'transform-class-constructor-call': __webpack_require__(203), + 'transform-class-properties': __webpack_require__(204), + 'transform-decorators': __webpack_require__(205), + 'transform-decorators-legacy': __webpack_require__(329).default, // <- No clue. Nope. + 'transform-do-expressions': __webpack_require__(206), + 'transform-es2015-arrow-functions': __webpack_require__(68), + 'transform-es2015-block-scoped-functions': __webpack_require__(69), + 'transform-es2015-block-scoping': __webpack_require__(70), + 'transform-es2015-classes': __webpack_require__(71), + 'transform-es2015-computed-properties': __webpack_require__(72), + 'transform-es2015-destructuring': __webpack_require__(73), + 'transform-es2015-duplicate-keys': __webpack_require__(130), + 'transform-es2015-for-of': __webpack_require__(74), + 'transform-es2015-function-name': __webpack_require__(75), + 'transform-es2015-instanceof': __webpack_require__(332), + 'transform-es2015-literals': __webpack_require__(76), + 'transform-es2015-modules-amd': __webpack_require__(131), + 'transform-es2015-modules-commonjs': __webpack_require__(77), + 'transform-es2015-modules-systemjs': __webpack_require__(208), + 'transform-es2015-modules-umd': __webpack_require__(209), + 'transform-es2015-object-super': __webpack_require__(78), + 'transform-es2015-parameters': __webpack_require__(79), + 'transform-es2015-shorthand-properties': __webpack_require__(80), + 'transform-es2015-spread': __webpack_require__(81), + 'transform-es2015-sticky-regex': __webpack_require__(82), + 'transform-es2015-template-literals': __webpack_require__(83), + 'transform-es2015-typeof-symbol': __webpack_require__(84), + 'transform-es2015-unicode-regex': __webpack_require__(85), + 'transform-es3-member-expression-literals': __webpack_require__(336), + 'transform-es3-property-literals': __webpack_require__(337), + 'transform-es5-property-mutators': __webpack_require__(338), + 'transform-eval': __webpack_require__(339), + 'transform-exponentiation-operator': __webpack_require__(132), + 'transform-export-extensions': __webpack_require__(210), + 'transform-flow-comments': __webpack_require__(340), + 'transform-flow-strip-types': __webpack_require__(211), + 'transform-function-bind': __webpack_require__(212), + 'transform-jscript': __webpack_require__(341), + 'transform-object-assign': __webpack_require__(342), + 'transform-object-rest-spread': __webpack_require__(213), + 'transform-object-set-prototype-of-to-assign': __webpack_require__(343), + 'transform-proto-to-assign': __webpack_require__(344), + 'transform-react-constant-elements': __webpack_require__(345), + 'transform-react-display-name': __webpack_require__(214), + 'transform-react-inline-elements': __webpack_require__(346), + 'transform-react-jsx': __webpack_require__(215), + 'transform-react-jsx-compat': __webpack_require__(347), + 'transform-react-jsx-self': __webpack_require__(349), + 'transform-react-jsx-source': __webpack_require__(350), + 'transform-regenerator': __webpack_require__(86), + 'transform-runtime': __webpack_require__(353), + 'transform-strict-mode': __webpack_require__(216), + 'undeclared-variables-check': __webpack_require__(354) + }); + + // All the presets we should bundle + registerPresets({ + es2015: __webpack_require__(217), + es2016: __webpack_require__(218), + es2017: __webpack_require__(219), + latest: __webpack_require__(356), + react: __webpack_require__(357), + 'stage-0': __webpack_require__(358), + 'stage-1': __webpack_require__(220), + 'stage-2': __webpack_require__(221), + 'stage-3': __webpack_require__(222), + + // ES2015 preset with es2015-modules-commonjs removed + // Plugin list copied from babel-preset-es2015/index.js + 'es2015-no-commonjs': { + plugins: [__webpack_require__(83), __webpack_require__(76), __webpack_require__(75), __webpack_require__(68), __webpack_require__(69), __webpack_require__(71), __webpack_require__(78), __webpack_require__(80), __webpack_require__(72), __webpack_require__(74), __webpack_require__(82), __webpack_require__(85), __webpack_require__(66), __webpack_require__(81), __webpack_require__(79), __webpack_require__(73), __webpack_require__(70), __webpack_require__(84), [__webpack_require__(86), { async: false, asyncGenerators: false }]] + }, + + // ES2015 preset with plugins set to loose mode. + // Based off https://github.com/bkonkle/babel-preset-es2015-loose/blob/master/index.js + 'es2015-loose': { + plugins: [[__webpack_require__(83), { loose: true }], __webpack_require__(76), __webpack_require__(75), __webpack_require__(68), __webpack_require__(69), [__webpack_require__(71), { loose: true }], __webpack_require__(78), __webpack_require__(80), __webpack_require__(130), [__webpack_require__(72), { loose: true }], [__webpack_require__(74), { loose: true }], __webpack_require__(82), __webpack_require__(85), __webpack_require__(66), [__webpack_require__(81), { loose: true }], __webpack_require__(79), [__webpack_require__(73), { loose: true }], __webpack_require__(70), __webpack_require__(84), [__webpack_require__(77), { loose: true }], [__webpack_require__(86), { async: false, asyncGenerators: false }]] + } + }); + + var version = exports.version = ("6.26.0"); + + // Listen for load event if we're in a browser and then kick off finding and + // running of scripts with "text/babel" type. + if (typeof window !== 'undefined' && window && window.addEventListener) { + window.addEventListener('DOMContentLoaded', function () { + return transformScriptTags(); + }, false); + } + + /** + * Transform