gas.py
Скрипт для рассчета среднего потребления газа (и не только) в месяц, если известны показания счетчика на определенные даты. для каждого интервала считает среднее потребление в сутки, потом суммирует по дням месяца - выводит потребление за месяц.
Сначала нужно ввести показания, и они будут сохранены в том-же каталоге, где и скрипт в файл "имя_скрипта_raw.json" - таким образом скрипт можно переименовать и считать отдельно потребление разных вещей - воды, газа, электричества...
Показания вводятся по одному за каждый запуск скрипта:
eking@eking-notebook:/home/eking$ ./gas.py3 2016-01-20 13456 eking@eking-notebook:/home/eking$ ./gas.py3 2016-02-13 13656 eking@eking-notebook:/home/eking$ ./gas.py3 2016-04-1 14000 eking@eking-notebook:/home/eking$ ./gas.py3 2016-09-1 15000
После чего нужно запустить скрипт без параметров:
eking@eking-notebook:/home/eking$ ./gas.py3 | Raw counters: | Average by mounth: | 2016-01-20 13456 | 2016-02 222 | 2016-02-13 13656 | 2016-03 222 | 2016-04-01 14000 | 2016-04 196 | 2016-09-01 15000 | 2016-05 203 | 2016-06 196 | 2016-07 203 | 2016-08 203 | 2016-09 0 ============================================================ | Total sum: 1544 | Total average sum: 1445
Разница в полной сумме и сумме усредненных - из-за округления. Собственно скрипт предназначен для оценки насколько изменяется среднее потребление. Чем чаще сделаны замеры - тем меньше будет ошибка округления.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- ''' gas.py: для расчета потребления газа ''' import sys import json import datetime __author__ = 'Nikolay Gatilov' __copyright__ = 'Nikolay Gatilov' __license__ = 'GPL' __version__ = '1.0.20161118' __maintainer__ = 'Nikolay Gatilov' __email__ = 'eking.work@gmail.com' DEBUG = False def load_data(df): try: with open(df, 'r') as f: data = json.load(f) except Exception as e: data = {} print ('Unable to read data from file:\n%s\n' % str(e)) return data def date_from_iso(s): (y, m, d) = map(int, s.split('-')) return datetime.date(y, m, d) def convert_data(raw_d): raw_d_t = {} for i in raw_d.keys(): raw_d_t[date_from_iso(i)] = int(raw_d[i]) return raw_d_t def get_first_date(raw_d): fd = min(raw_d) if fd.day > 1: return (fd.replace(day=1) + datetime.timedelta(days=32)).replace(day=1) else: return fd def get_last_date(raw_d): fd = max(raw_d) nm = fd + datetime.timedelta(days=1) if fd.month != nm.month: return fd else: nm = fd.replace(day=1) + datetime.timedelta(days=32) return nm.replace(day=1) - datetime.timedelta(days=1) def get_month_dates(k, month): md = [] tmax = max(k) tdmax = tmax - month tmin = min(k) tdmin = month - tmin for i in k: if i.year == month.year and i.month == month.month: md.append(i) elif i > month and i - month < tdmax: tdmax = i - month tmax = i elif i < month and month - i < tdmin: tdmin = month - i tmin = i if tmax not in md: md.append(tmax) if tmin not in md and month not in md: md.append(tmin) return sorted(md) def compute(md): k = md.keys() now = get_first_date(k) last_date = get_last_date(k) mad = {} while now < last_date: mda = get_month_dates(k, now) if DEBUG: print('\n', now, mda) nxt = (now + datetime.timedelta(days=32)).replace(day=1) mad[now] = 0 h = mda.pop(0) for i in mda: r = (md[i]-md[h]) if DEBUG: print('==> ', md[i], md[h], r) if h < now: mad[now] = mad[now] + r*abs(min(i, nxt) - now)/abs(i - h) if DEBUG: print(' from h < now (', min(i, nxt), '-', now, ')/(', i, '-', h, ') = ', mad[now]) elif i > nxt: mad[now] = mad[now] + r*abs(nxt - max(h, now))/abs(i - h) if DEBUG: print(' from i > nxt (', nxt, '-', max(h, now), ')/(', i, '-', h, ') = ', mad[now]) else: mad[now] = mad[now] + r if DEBUG: print(' only add r ', h, i, ' = ', mad[now]) h = i mad[now] = round(mad[now]) if DEBUG: print('----- ', mad[now]) now = nxt return mad def save_data(df, data): try: with open(df, 'w') as f: json.dump(data, f, sort_keys=True) except Exception as e: print('Unable to save data in file:\n%s\n' % str(e)) if __name__ == "__main__": # main RAW_DATA_FILE = '%s_raw.json' % sys.argv[0] AVG_DATA_FILE = '%s_avg.json' % sys.argv[0] raw_data = load_data(RAW_DATA_FILE) if len(sys.argv) > 2: di = sys.argv[1].split('.') count_in = int(sys.argv[2]) da = date_from_iso(sys.argv[1]) raw_data[da.isoformat()] = count_in else: by_month_data = convert_data(raw_data) avg_data = compute(by_month_data) avg_i = iter(sorted(avg_data.keys())) raw_i = iter(sorted(by_month_data.keys())) a_i_n = next(avg_i, None) r_i_n = next(raw_i, None) ma = by_month_data[max(by_month_data.keys())] mi = by_month_data[min(by_month_data.keys())] total_raw_sum = (ma-mi) total_avg_sum = 0 print('\n| Raw counters: | Average by mounth:\n') while a_i_n is not None or r_i_n is not None: if a_i_n is not None: ain_d = '{:%Y-%m}'.format(a_i_n) ain_n = '{:6d}'.format(avg_data[a_i_n]) ain = '| %s %s' % (ain_d, ain_n) total_avg_sum = total_avg_sum + avg_data[a_i_n] else: ain = ' ' * 14 if r_i_n is not None: rin_d = '{:%Y-%m-%d}'.format(r_i_n) rin_n = '{:6d}'.format(by_month_data[r_i_n]) rin = '| %s %s' % (rin_d, rin_n) else: rin = ' ' * 24 print(rin, ain) a_i_n = next(avg_i, None) r_i_n = next(raw_i, None) s = '\n{:s}\n| Total sum: {:7d} | Total average sum: {:7d}\n' s = s.format('=' * 60, total_raw_sum, total_avg_sum) print(s) avg = {} for i in avg_data.keys(): avg[i.isoformat()] = avg_data[i] save_data(AVG_DATA_FILE, avg) save_data(RAW_DATA_FILE, raw_data)