diff --git a/jc/cli.py b/jc/cli.py index 29e16e53..a76b9fbe 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -88,6 +88,7 @@ parsers = [ 'tracepath', 'traceroute', 'uname', + 'upower', 'uptime', 'w', 'wc', diff --git a/jc/parsers/upower.py b/jc/parsers/upower.py new file mode 100644 index 00000000..dac139f3 --- /dev/null +++ b/jc/parsers/upower.py @@ -0,0 +1,173 @@ +"""jc - JSON CLI output utility `upower` command output parser + +<> + +Usage (cli): + + $ upower | jc --upower + + or + + $ jc upower + +Usage (module): + + import jc.parsers.upower + result = jc.parsers.upower.parse(upower_command_output) + +Compatibility: + + 'linux' + +Examples: + + $ upower | jc --upower -p + [] + + $ upower | jc --upower -p -r + [] +""" +import jc.utils + + +class info(): + version = '1.0' + description = 'upower command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux'] + magic_commands = ['upower'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured data with the following schema: + + [ + { + "upower": string, + "bar": boolean, + "baz": integer + } + ] + """ + + # rebuild output for added semantic information + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = [] + device_obj = {} + device_name = None + detail_obj = {} + detail_key = '' + # last_detail_key = '' + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()): + if line.startswith('Device:') or line.startswith('Daemon:'): + if device_obj: + if detail_obj: + device_obj[detail_key] = detail_obj + + raw_output.append(device_obj) + device_obj = {} + + if line.startswith('Device:'): + device_name = line.split(':', maxsplit=1)[1].strip() + device_obj = { + 'type': 'Device', + "device_name": device_name + } + + elif line.startswith('Daemon:'): + device_obj = { + 'type': 'Daemon' + } + + detail_obj = {} + detail_key = '' + continue + + # history lines are a special case of detail lines + # set the history detail key + if line.startswith(' History ('): + continue + # detail_key = line.strip().lower().replace('-', '_').replace(' ', '_').replace('(', '').replace(')', '')[:-1] + # device_obj[detail_key] = {} + # continue + + # history detail lines + # if detail_key.startswith('history_'): + if line.startswith(' ') and ':' not in line: + continue + # detail_obj['history_line'] = 'history detail line' + # continue + + # general detail lines + if line.startswith(' ') and ':' in line: + # since there could be multiple detail objects, reset the object key + # if detail_key != last_detail_key: + # device_obj[last_detail_key] = detail_obj + # last_detail_key = detail_key + + key = line.split(':', maxsplit=1)[0].strip().lower().replace('-', '_').replace(' ', '_') + val = line.split(':', maxsplit=1)[1].strip() + detail_obj[key] = val + continue + + # top level lines + if line.startswith(' ') and ':' in line: + key = line.split(':', maxsplit=1)[0].strip().lower().replace('-', '_').replace(' ', '_') + val = line.split(':', maxsplit=1)[1].strip() + device_obj[key] = val + + continue + + # set the detail key + if line.startswith(' ') and ':' not in line: + detail_key = line.strip().lower().replace('-', '_').replace(' ', '_') + device_obj[detail_key] = {} + continue + + if device_obj: + if detail_obj: + device_obj[detail_key] = detail_obj + raw_output.append(device_obj) + + if raw: + return raw_output + else: + return process(raw_output) diff --git a/tests/fixtures/generic/upower-i.out b/tests/fixtures/generic/upower-i.out new file mode 100644 index 00000000..476abe45 --- /dev/null +++ b/tests/fixtures/generic/upower-i.out @@ -0,0 +1,32 @@ + native-path: /sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT0 + vendor: NOTEBOOK + model: BAT + serial: 0001 + power supply: yes + updated: Thu Feb 9 18:42:15 2012 (1 seconds ago) + has history: yes + has statistics: yes + battery + present: yes + rechargeable: yes + state: charging + energy: 22.3998 Wh + energy-empty: 0 Wh + energy-full: 52.6473 Wh + energy-full-design: 62.16 Wh + energy-rate: 31.6905 W + voltage: 12.191 V + time to full: 57.3 minutes + percentage: 42.5469% + capacity: 84.6964% + technology: lithium-ion + History (charge): + 1328809335 42.547 charging + 1328809305 42.020 charging + 1328809275 41.472 charging + 1328809245 41.008 charging + History (rate): + 1328809335 31.691 charging + 1328809305 32.323 charging + 1328809275 33.133 charging + diff --git a/tests/fixtures/generic/upower-i2.out b/tests/fixtures/generic/upower-i2.out new file mode 100644 index 00000000..a2c71e63 --- /dev/null +++ b/tests/fixtures/generic/upower-i2.out @@ -0,0 +1,24 @@ + native-path: BAT0 + vendor: Samsung SDI + model: DELL 7XFJJA2 + serial: 4448 + power supply: yes + updated: Tuesday 01 October 2019 12:50:41 PM IST (101 seconds ago) + has history: yes + has statistics: yes + battery + present: yes + rechargeable: yes + state: fully-charged + warning-level: none + energy: 33.4443 Wh + energy-empty: 0 Wh + energy-full: 33.4443 Wh + energy-full-design: 48.84 Wh + energy-rate: 0.0111 W + voltage: 12.446 V + percentage: 100% + capacity: 68.4773% + technology: lithium-ion + icon-name: 'battery-full-charged-symbolic' + diff --git a/tests/fixtures/ubuntu-18.04/upower-d.out b/tests/fixtures/ubuntu-18.04/upower-d.out new file mode 100644 index 00000000..8e15e91b --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/upower-d.out @@ -0,0 +1,27 @@ +Device: /org/freedesktop/UPower/devices/line_power_ACAD + native-path: ACAD + power supply: yes + updated: Thu 11 Mar 2021 06:28:08 PM UTC (441975 seconds ago) + has history: no + has statistics: no + line-power + warning-level: none + online: yes + icon-name: 'ac-adapter-symbolic' + +Device: /org/freedesktop/UPower/devices/DisplayDevice + power supply: no + updated: Thu 11 Mar 2021 06:28:08 PM UTC (441975 seconds ago) + has history: no + has statistics: no + unknown + warning-level: none + icon-name: 'battery-missing-symbolic' + +Daemon: + daemon-version: 0.99.7 + on-battery: no + lid-is-closed: no + lid-is-present: no + critical-action: HybridSleep + diff --git a/tests/fixtures/ubuntu-18.04/upower-i.out b/tests/fixtures/ubuntu-18.04/upower-i.out new file mode 100644 index 00000000..e3bee4d1 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/upower-i.out @@ -0,0 +1,11 @@ + native-path: ACAD + power supply: yes + updated: Thu 11 Mar 2021 06:28:08 PM UTC (442049 seconds ago) + has history: no + has statistics: no + line-power + warning-level: none + online: yes + icon-name: 'ac-adapter-symbolic' + +