#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) 2019. Tobias Kurze import hashlib import json import logging import os import re from datetime import datetime from json import dumps from pprint import pprint from sqlalchemy import and_ from sqlalchemy.exc import IntegrityError from backend import db from backend.models import Room from backend.models.recorder_model import RecorderModel, RecorderCommand, Recorder from backend.recorder_adapters import get_defined_recorder_adapters from backend.tools.helpers import calculate_md5_checksum logger = logging.getLogger("lrc." + __name__) KNOWN_RECORDERS = {re.compile(r'(?PSMP)[\s]*(?P[\d]+)[\s]*.?[\s]*(?P[\S]*)'): 'SMP', re.compile( r'(?PLectureRecorder X2|LectureRecorder|VGADVI Recorder|DVI Broadcaster DL|DVIRecorderDL)'): 'Epiphan'} def create_recorder_commands_for_recorder_adapter(command_definitions: dict, recorder_model: RecorderModel): logger.debug("Received {} command definitions to create " "for recoder model {}".format(len(command_definitions), recorder_model.record_adapter_id)) existing_recorder_commands = RecorderCommand.query.filter( and_( RecorderCommand.name.in_([recorder_model.record_adapter_id + ":" + k for k in command_definitions.keys()])), RecorderCommand.recorder_model == recorder_model) existing_commands = set() for existing_command in existing_recorder_commands: existing_commands.add(existing_command.name) args = command_definitions.get(existing_command.name) if dumps(existing_command.parameters) != dumps(args): logger.warning( "The function definition {} collides with an existing definition of the same name " "but different parameters!".format( existing_command.name)) existing_command.last_time_modified = datetime.utcnow() existing_command.parameters = args db.session.commit() for c_d in set(command_definitions.keys()) - existing_commands: args = command_definitions.get(c_d) # print(args) # create new recorder command(s) r_c = RecorderCommand(name=recorder_model.record_adapter_id + ":" + c_d, parameters=args, recorder_model=recorder_model) db.session.add(r_c) db.session.flush() db.session.commit() def update_recorder_models_database(drop: bool = False): if drop: for r in RecorderModel.get_all(): db.session.delete(r) db.session.commit() r_as = get_defined_recorder_adapters() for r_a in r_as: if r_a.get('class') is None: # skip modules without class (helpers.py, e.g.) continue try: r_m = RecorderModel.get_by_adapter_id(r_a["id"]) model_checksum = calculate_md5_checksum(dumps(r_a["commands"])) if r_m is None: r_m = RecorderModel(record_adapter_id=r_a["id"], model_name=r_a["name"], checksum=model_checksum, **r_a.get('class').get_recorder_params()) db.session.add(r_m) db.session.flush() db.session.refresh(r_m) logger.debug("Creating command definitions for rec mod adapter {}".format(r_m.record_adapter_id)) create_recorder_commands_for_recorder_adapter(r_a["commands"], r_m) else: if r_m.model_name != r_a["name"]: r_m.model_name = r_a["name"] r_m.last_time_modified = datetime.utcnow() if model_checksum != r_m.checksum: r_m.last_time_modified = datetime.utcnow() r_m.checksum = model_checksum r_m.last_checksum_change = datetime.utcnow() logger.debug("Updating command definitions for rec mod adapter {}".format(r_m.record_adapter_id)) create_recorder_commands_for_recorder_adapter(r_a["commands"], r_m) except IntegrityError as e: logger.error(e) db.session.rollback() db.session.commit() def get_recorder_room(rec: dict) -> Room(): rooms = Room.get_by_building_number(rec.get('building', None)) if rooms.count() <= 0: logger.warning("Building {} unknown! Can not find room for recorder {}.".format(rec['building'], rec['name'])) return None if rooms.count() == 1: return rooms.first() room_name = rec.get('room') room_name = room_name.replace('–', ' ') for room in rooms: if all([r_n in room.name for r_n in room_name.split()]): return room logger.warning("No room found for recorder {}".format(rec['name'])) def create_default_recorders(): f = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'models', 'initial_recorders.json')) with open(f, 'r') as json_file: recorders = json.load(json_file)['recorders'] for r in recorders: type = r.get('type') firmware_version = r.get('firmware_version', None) room_rec_name = r.get('name') username = r.get('username', None) password = r.get('password', None) mac = r.get('mac', None) ip4 = r.get('ip', None) additional_camera = r.get('additional_camera', False) description = r.get('description', '') for k_r in KNOWN_RECORDERS: if match := k_r.search(type): name = match.groupdict().get('name') model_number = match.groupdict().get('number', None) options = match.groupdict().get('options', None) if options is not None and options != "": options = options.split(',') if model_number is not None: model_name = name + " " + model_number model_search_name = name + model_number[:-1] # just get prefix (remove last digit); SMP 35x -> 35 else: model_name = name model_search_name = KNOWN_RECORDERS[k_r] rec_model = RecorderModel.get_where_adapter_id_contains(model_search_name) rec = Recorder.get_by_mac(mac) if rec is None: rec = Recorder.get_by_name(room_rec_name + " Recorder") if rec is None: rec = Recorder() rec.mac = mac db.session.add(rec) rec.name = room_rec_name + " Recorder" rec.model_name = model_name rec.recorder_model = rec_model rec.username = username rec.password = password rec.firmware_version = firmware_version rec.ip = ip4 rec.additional_camera_connected = additional_camera rec.additional_note = description rec.configured_options = options rec.room = get_recorder_room(r) db.session.flush() db.session.refresh(rec) db.session.commit() if __name__ == '__main__': # for r in Room.get_all(): # print(r) # exit() commands = RecorderCommand.get_all() for c in commands: print(c) print(c.recorder_model) for r_m in RecorderModel.get_all(): print(r_m.recorder_commands) exit() recorders = Recorder.get_all() for r in recorders: if r.room is None: print("{}: {}".format(r, r.room)) # db.drop_all() # db.create_all() update_recorder_models_database() create_default_recorders() db.session.commit() # print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "10.50"})) # print(get_recorder_room({"room": "Grosser Hörsaal Bauingenieure", "building": "30.95"}))