From cce2b9bc39025c678f6806b9c2f0fbec466336d1 Mon Sep 17 00:00:00 2001 From: Manuel Fehren Date: Mon, 20 Apr 2026 08:26:37 +0200 Subject: [PATCH] first commit --- .gitignore | 3 + README.md | 75 + crawler/.gitignore | 7 + crawler/README.md | 74 + crawler/app/app.py | 114 + crawler/app/config/cameras_sample.yaml | 15 + crawler/app/requirements.txt | 4 + crawler/app/timelaps.py | 61 + crawler/dockerfile | 13 + docker-compose.yml | 57 + website/.gitignore | 7 + website/README.md | 75 + website/app/app.py | 59 + website/app/config/cameras_sample.yaml | 15 + website/app/requirements.txt | 5 + website/app/static/404.jpg | Bin 0 -> 34018 bytes website/app/static/favicon.ico | Bin 0 -> 15406 bytes website/app/static/images/background.svg | 3997 ++++++++ website/app/static/images/gras.svg | 10272 +++++++++++++++++++++ website/app/templates/index.html | 78 + website/dockerfile | 13 + 21 files changed, 14944 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 crawler/.gitignore create mode 100644 crawler/README.md create mode 100644 crawler/app/app.py create mode 100644 crawler/app/config/cameras_sample.yaml create mode 100644 crawler/app/requirements.txt create mode 100644 crawler/app/timelaps.py create mode 100644 crawler/dockerfile create mode 100644 docker-compose.yml create mode 100644 website/.gitignore create mode 100644 website/README.md create mode 100644 website/app/app.py create mode 100644 website/app/config/cameras_sample.yaml create mode 100644 website/app/requirements.txt create mode 100644 website/app/static/404.jpg create mode 100644 website/app/static/favicon.ico create mode 100644 website/app/static/images/background.svg create mode 100644 website/app/static/images/gras.svg create mode 100644 website/app/templates/index.html create mode 100644 website/dockerfile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e95cca9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv +__pycache__ +data/* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..219f6fe --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# CameraCrawler + +This is a flask website to display Images from Webcams and timelaps, dependen on a .yaml config. + + +## Sample cameras.yaml +```yaml +- name: "Dummy" # name of camera + url: "" # camera-image url + interval: 5 # Capture intervall in minutes + timelapse: True # create timelapse every night + website: + picture: True # display camera on website + timelapse: True # display timelapse on website + +- name: "Dummy" + url: "" + interval: 5 + timelapse: True + website: + picture: True + timelapse: True + +``` + +## Sample docker-compose.yml +```yaml +version: '3.4' + +services: + cam-flask: + build: ./website + restart: unless-stopped + + volumes: + - ./cam/logs:/logs:rw + - ./cam/output:/static/output:ro + - /etc/localtime:/etc/localtime:ro + environment: + - TZ="Europe/Berlin" + + command: gunicorn -w 1 + -b :8000 app:app \ + --access-logfile ./logs/log.txt \ + --log-level info \ + --timeout 90 \ + --workers 25 \ + --worker-class gevent + + labels: + - "traefik.enable=true" + - "traefik.http.routers.cam.rule=Host(`cam.domain.com`)" + - "traefik.http.routers.cam.entrypoints=websecure" + - "traefik.http.services.cam.loadbalancer.server.port=8000" + - "traefik.http.routers.cam.service=cam" + - "traefik.http.routers.cam.tls.certresolver=production" + networks: + - traefik_default + + + cam-crawler: + build: ./crawler + restart: unless-stopped + + volumes: + - ./cam/timelapse:/archive:rw + - ./cam/output:/output:rw + - ./cam/config:/config:ro + - /etc/localtime:/etc/localtime:ro + command: python app.py + +networks: + traefik_default: + external: true +``` \ No newline at end of file diff --git a/crawler/.gitignore b/crawler/.gitignore new file mode 100644 index 0000000..9e85c42 --- /dev/null +++ b/crawler/.gitignore @@ -0,0 +1,7 @@ +venv +__pycache__ +timelapse +static/output +archive +output +app/config/cameras.yaml diff --git a/crawler/README.md b/crawler/README.md new file mode 100644 index 0000000..f311998 --- /dev/null +++ b/crawler/README.md @@ -0,0 +1,74 @@ +# CameraCrawler + +This is a python script, to crawl images from network cameras and creat a timelapse every night. + +## Sample cameras.yaml +```yaml +- name: "Dummy" # name of camera + url: "" # camera-image url + interval: 5 # Capture intervall in minutes + timelapse: True # create timelapse every night + website: + picture: True # display camera on website + timelapse: True # display timelapse on website + +- name: "Dummy" + url: "" + interval: 5 + timelapse: True + website: + picture: True + timelapse: True + +``` + +## Sample docker-compose.yml +```yaml +version: '3.4' + +services: + cam-flask: + image: registry.mnl-fhrn.de/lsrg/camerawebsite:latest + restart: unless-stopped + + volumes: + - ./cam/logs:/logs:rw + - ./cam/output:/static/output:ro + - /etc/localtime:/etc/localtime:ro + environment: + - TZ="Europe/Berlin" + + command: gunicorn -w 1 + -b :8000 app:app \ + --access-logfile ./logs/log.txt \ + --log-level info \ + --timeout 90 \ + --workers 25 \ + --worker-class gevent + + labels: + - "traefik.enable=true" + - "traefik.http.routers.cam.rule=Host(`cam.domain.com`)" + - "traefik.http.routers.cam.entrypoints=websecure" + - "traefik.http.services.cam.loadbalancer.server.port=8000" + - "traefik.http.routers.cam.service=cam" + - "traefik.http.routers.cam.tls.certresolver=production" + networks: + - traefik_default + + + cam-crawler: + image: registry.mnl-fhrn.de/lsrg/cameracrawler:latest + restart: unless-stopped + + volumes: + - ./cam/timelapse:/archive:rw + - ./cam/output:/output:rw + - ./cam/config:/config:ro + - /etc/localtime:/etc/localtime:ro + command: python app.py + +networks: + traefik_default: + external: true +``` \ No newline at end of file diff --git a/crawler/app/app.py b/crawler/app/app.py new file mode 100644 index 0000000..91c5293 --- /dev/null +++ b/crawler/app/app.py @@ -0,0 +1,114 @@ +from apscheduler.schedulers.background import BackgroundScheduler +import datetime +import time +import os +from pathlib import Path +import shutil +from PIL import Image +import requests +import atexit +import yaml +import timelaps + +os.environ['TZ'] = "Europe/Berlin" +time.tzset() + +sched = BackgroundScheduler(daemonic=True) + +with open("config/cameras.yaml", "r") as yamlfile: + yaml = yaml.load(yamlfile, Loader=yaml.FullLoader) + print("camera config read successful") +#print(yaml) + +def compress(src,dst): + + filepath = os.path.join(os.getcwd(), src) + + image = Image.open(filepath) + + image = image.resize((image.width // 2, image.height // 2), Image.LANCZOS) + + image.save(src, + "JPEG", + optimize = True, + quality = 90) + + shutil.copyfile(src, dst) + + return + + + +def crawl_image(url, name): + ct = datetime.datetime.now() + + file_path = "archive/" + name + "/" + ct.strftime("%Y%m%d") + file_name = name + "_" + ct.strftime("%Y%m%d_%H%M%S") + ".jpg" + + staticPath = "output" + + print('Started Image Downloaded: ',url) + Path(file_path).mkdir(parents=True, exist_ok=True) + Path(staticPath).mkdir(parents=True, exist_ok=True) + + # Download image + try: + res = requests.get(url, stream = True, auth=('admin', 'hackerspace')) + if res.status_code == 200: + with open(os.path.join(file_path,file_name),'wb') as f: + shutil.copyfileobj(res.raw, f) + print('Image sucessfully Downloaded: ',os.path.join(file_path,file_name)) + compress(os.path.join(file_path,file_name), staticPath + "/" + name + ".jpg") + + original_size = os.path.getsize(os.path.join(file_path,file_name)) + compressed_size = os.path.getsize(staticPath + "/" + name + ".jpg") + + print("Original Size: ", original_size) + print("Compressed Size: ", compressed_size) + print("New image at:-", ct) + print() + + else: + print("URL - Error:", url) + return -1 + except Exception as e: + print(e) + + + +def createTimelapsYesterday(name): + yesterday = datetime.datetime.now()- datetime.timedelta(days=1) + timelaps.createTimelaps(name=name,date=yesterday.strftime("%Y%m%d")) + + +def addJob(name, url, minutes): + sched.start() + + #create backgroundTask for each camera + for camera in yaml: + sched.add_job(crawl_image,'interval',[camera["url"], camera["name"]],minutes=camera["interval"],max_instances=1, id=camera["name"] + " - crawler") + crawl_image(camera["url"], camera["name"]) + if camera["timelapse"]: + sched.add_job(createTimelapsYesterday,'cron',[camera["name"]], hour='01',max_instances=1, id=camera["name"] + " - timelapse") + createTimelapsYesterday(camera["name"]) + + #atexit.register(lambda: scheduler.shutdown()) + print(camera["name"], "--- was added with", camera["interval"], "minutes interval") + + +def initCrawler(): + atexit.register(stopCrawler) + url_kantine = "http://192.168.187.80/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=wuuPhkmUCeI9WG7C&user=smart&password=DwyU8AfK2p2PKT" + addJob("kantine", url_kantine, 5) + + +def stopCrawler(): + sched.shutdown(wait=True) + +if __name__ == '__main__': + initCrawler() + while(True): + time.sleep(1) + pass + + diff --git a/crawler/app/config/cameras_sample.yaml b/crawler/app/config/cameras_sample.yaml new file mode 100644 index 0000000..3e41289 --- /dev/null +++ b/crawler/app/config/cameras_sample.yaml @@ -0,0 +1,15 @@ +- name: "Dummy" # name of camera + url: "" # camera-image url + interval: 5 # Capture intervall in minutes + timelapse: True # create timelapse every night + website: + picture: True # display camera on website + timelapse: True # display timelapse on website + +- name: "Dummy" + url: "" + interval: 5 + timelapse: True + website: + picture: True + timelapse: True diff --git a/crawler/app/requirements.txt b/crawler/app/requirements.txt new file mode 100644 index 0000000..ecb4495 --- /dev/null +++ b/crawler/app/requirements.txt @@ -0,0 +1,4 @@ +APScheduler +pillow +requests +pyyaml \ No newline at end of file diff --git a/crawler/app/timelaps.py b/crawler/app/timelaps.py new file mode 100644 index 0000000..e2d0e65 --- /dev/null +++ b/crawler/app/timelaps.py @@ -0,0 +1,61 @@ +#import numpy as np +import os +import shutil + + +def createTimelaps(name, date = None, path = None): + staticPath = "output" + if date: + path = 'archive/' + name + '/' + date + elif path == None: + print("Error-Timelapse-missing args for folder") + return -1 + + if not os.path.exists(path): + print("Error-Timelapse-can't finde folder", path) + return -2 + + ## Check if folder exists and is not empty + if os.path.exists(path) and not os.path.isfile(path): + if not os.listdir(path): + print("Error-Timelapse-Empty directory", path) + return -3 + + #print('creating writer') + #videoSize = ( + # img_array[0].shape[1], + # img_array[0].shape[0] + #) + #writer = cv2.VideoWriter( path + '/' + name + '.mp4', cv2.VideoWriter_fourcc(*"mp4v"), 10, videoSize) + #print('writer created') + #print() + + + print('creating video') + + if os.path.exists(path + "/" + name + ".mp4"): + print("video allready exists") + os.remove(path + "/" + name + ".mp4") + imgWidht = 2304 + imgHeight = 864 + os.system('ffmpeg -hide_banner -loglevel error -stats -framerate 10 -pattern_type glob -i "' + path + '/*.jpg" -s:v ' + str(imgWidht) + 'x' + str(imgHeight) + ' -c:v libx264 -crf 17 -pix_fmt yuv420p ' + path + '/' + name + '.mp4') + + shutil.copyfile(path + "/" + name + ".mp4", staticPath + "/" + name + ".mp4") + print('video created') + print() + + try: + shutil.make_archive(path, 'zip', path) + print('zip created') + except: + print('Error while creating .zip') + + try: + shutil.rmtree(path) + print('folder cleared') + except: + print('Error while removing folder') + + +if __name__ == "__main__": + createTimelaps("kantine", path="timelapse/kantine/20230828") \ No newline at end of file diff --git a/crawler/dockerfile b/crawler/dockerfile new file mode 100644 index 0000000..21b5ea0 --- /dev/null +++ b/crawler/dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.11 + + +COPY app/ . + +#COPY requirements.txt requirements.txt +RUN apt-get update && apt-get install ffmpeg -y +RUN pip3 install -r requirements.txt + +CMD ["python", "app.py"] +#CMD ["gunicorn" , "-b", "0.0.0.0:8000" , "app:app"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6d370ee --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,57 @@ +version: '3.4' + +services: + birdstalker-flask: + build: ./website + restart: unless-stopped + stdin_open: true # docker run -i + tty: true # docker run -t + + volumes: + - ./data/logs:/logs:rw + - ./data/config:/config:ro + - ./data/output:/static/output:ro + - /etc/localtime:/etc/localtime:ro + #ports: + # - 5020:8000 + environment: + - TZ="Europe/Berlin" + #command: python app.py + + command: gunicorn -w 1 + -b :8000 app:app \ + --access-logfile ./logs/log.txt \ + --log-level info \ + --timeout 90 \ + --workers 25 \ + --worker-class gevent \ + --debug=True + + + labels: + - "traefik.enable=true" + - "traefik.http.routers.cameraWebsite.rule=Host(`camera.manuel-fehren.de`)" + - "traefik.http.routers.cameraWebsite.entrypoints=websecure" + - "traefik.http.services.cameraWebsite.loadbalancer.server.port=8000" + - "traefik.http.routers.cameraWebsite.service=cameraWebsite" + - "traefik.http.routers.cameraWebsite.tls.certresolver=production" + networks: + - traefik-net + + + bird-stalker-crawler: + build: ./crawler + restart: unless-stopped + stdin_open: true # docker run -i + tty: true # docker run -t + + volumes: + - ./data/timelapse:/archive:rw + - ./data/output:/output:rw + - ./data/config:/config:ro + - /etc/localtime:/etc/localtime:ro + command: python app.py + +networks: + traefik-net: + external: true diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 0000000..9e85c42 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,7 @@ +venv +__pycache__ +timelapse +static/output +archive +output +app/config/cameras.yaml diff --git a/website/README.md b/website/README.md new file mode 100644 index 0000000..219f6fe --- /dev/null +++ b/website/README.md @@ -0,0 +1,75 @@ +# CameraCrawler + +This is a flask website to display Images from Webcams and timelaps, dependen on a .yaml config. + + +## Sample cameras.yaml +```yaml +- name: "Dummy" # name of camera + url: "" # camera-image url + interval: 5 # Capture intervall in minutes + timelapse: True # create timelapse every night + website: + picture: True # display camera on website + timelapse: True # display timelapse on website + +- name: "Dummy" + url: "" + interval: 5 + timelapse: True + website: + picture: True + timelapse: True + +``` + +## Sample docker-compose.yml +```yaml +version: '3.4' + +services: + cam-flask: + build: ./website + restart: unless-stopped + + volumes: + - ./cam/logs:/logs:rw + - ./cam/output:/static/output:ro + - /etc/localtime:/etc/localtime:ro + environment: + - TZ="Europe/Berlin" + + command: gunicorn -w 1 + -b :8000 app:app \ + --access-logfile ./logs/log.txt \ + --log-level info \ + --timeout 90 \ + --workers 25 \ + --worker-class gevent + + labels: + - "traefik.enable=true" + - "traefik.http.routers.cam.rule=Host(`cam.domain.com`)" + - "traefik.http.routers.cam.entrypoints=websecure" + - "traefik.http.services.cam.loadbalancer.server.port=8000" + - "traefik.http.routers.cam.service=cam" + - "traefik.http.routers.cam.tls.certresolver=production" + networks: + - traefik_default + + + cam-crawler: + build: ./crawler + restart: unless-stopped + + volumes: + - ./cam/timelapse:/archive:rw + - ./cam/output:/output:rw + - ./cam/config:/config:ro + - /etc/localtime:/etc/localtime:ro + command: python app.py + +networks: + traefik_default: + external: true +``` \ No newline at end of file diff --git a/website/app/app.py b/website/app/app.py new file mode 100644 index 0000000..aff715b --- /dev/null +++ b/website/app/app.py @@ -0,0 +1,59 @@ +from flask import Flask, send_file, render_template, request, abort +from prometheus_flask_exporter import PrometheusMetrics + +import logging +import time +import os +import yaml + +os.environ['TZ'] = "Europe/Berlin" +time.tzset() + + +logging.basicConfig( + filename='logs/flask.log', + encoding='utf-8', + level=logging.INFO, + format='%(asctime)s %(levelname)s:%(message)s' + ) + +with open("config/cameras.yaml", "r") as yamlfile: + config = yaml.load(yamlfile, Loader=yaml.FullLoader) + print("camera config read successful") +#print(yaml) + +app = Flask(__name__) + +metrics = PrometheusMetrics(app) + +metrics.info('app_info', 'Application info', version='1.0.3') + +#@app.errorhandler(500) +#def internal_error(error): +# +# return "500 error" + +@app.errorhandler(404) +def not_found(error): + filename = 'static/404.jpg' + return send_file(filename, mimetype='image/jpg'), 404 + +@app.route('/error',methods = ['POST', 'GET']) +def errorTest(): + abort(500) + +@app.route('/',methods = ['POST', 'GET']) +def index(): + return render_template('index.html', config=config) + +#@app.route('/BirdStalker') +#def get_image(): +# filename = 'static/BirdStalker.jpg' +# return send_file(filename, mimetype='image/jpg') + + + +# main driver function +if __name__ == '__main__': + app.run() + diff --git a/website/app/config/cameras_sample.yaml b/website/app/config/cameras_sample.yaml new file mode 100644 index 0000000..3e41289 --- /dev/null +++ b/website/app/config/cameras_sample.yaml @@ -0,0 +1,15 @@ +- name: "Dummy" # name of camera + url: "" # camera-image url + interval: 5 # Capture intervall in minutes + timelapse: True # create timelapse every night + website: + picture: True # display camera on website + timelapse: True # display timelapse on website + +- name: "Dummy" + url: "" + interval: 5 + timelapse: True + website: + picture: True + timelapse: True diff --git a/website/app/requirements.txt b/website/app/requirements.txt new file mode 100644 index 0000000..d2778dc --- /dev/null +++ b/website/app/requirements.txt @@ -0,0 +1,5 @@ +Flask +gunicorn +pyyaml +requests +prometheus-flask-exporter \ No newline at end of file diff --git a/website/app/static/404.jpg b/website/app/static/404.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0331311678a5332fd388d312cfaafde0839de804 GIT binary patch literal 34018 zcmeFZbyQYe(=dMMknZkQy1TnukOrj`Y3c6n?v#+0M!FlNLFoo52?@W;`w>O&=efV% zTHm{Vf4uA2YjK^uXV2`JJ$q*MoPEyW+swBm07XVZS^@wA1Hd1HKL&tr^MEJ-3k?ke z4FwAW0|N&K3y*+-i16S60xlXlG6o?YF%cmi0RagG12qZRBXR-)8cy0rOw4R-Y{b;u z{9G)246JM{-#UN?u#oUDUSMFTz_%`d000dS4gm)IDp1g1Fc6Ti;NRu|SP*kinVner z)%|a3L5V5<2KoyG$wB4ytM&I97-DeN-=X&)qDE%-T7R#i%=~W?0HW}*|Bid#=nS{e zZ_|Z+E`P5L{Wb#Hk)i@(Rd>flu+42?&%MNxsWs7j3E+o&>jmsMtJMAz$)tz#UnGdKn}-0>c^M@Dmh}Y| z+P!Gt*SNnK19;0i(rcK}lKrgROCk&atnu6lP;%?{Lby_m`$ZDaHTJ84gv|IJAQmXE z_0@HL4YycnxRhs6?j`GTo(6;-yGi|nco4E%V}B{ZdGxW*=ngKa@ClB9yP^ZYxA^9t zIAS8xTL&_txP5&$hfF9A@g5(R$ZXnc(X|<@*uQNFSV!&u`Cyw|3|f8}vG z{ZAu^UkXt0>XlWR@Bbx2K+3q#Lh1~H)B7BpGgCKALAoQq%~IEQ4wK9OSp`Hdrj|b8 z2N%}-OA0|5-&a;`M0`OAuJ9&2@6_Hv^NYGT|DzO>?_NO-e&E~Z&-VRp!sCrP_@j1S z#~3Ka)9P{2M;|2>&9}t}8_?Y)bF0t2Py?P5@c8)YQv+gS> z{j`POJQvSY+-G@*q3wMNiGaJV6{7lP)(d?R@6MIaUTbHGq??Flh$UNck^eZ8Hk45{F5>D$p14{z2P*o>@4)Y(?QEb_ivgM&x_fU{sj z;(Xb+YR9k8(CCmi=jTD#qWgacR$+6n`Hrj+gG29pLD-zm?pyj03ckfjLX*^TLT~g zSN|^rq(uPO=0Szq!R2Jm7&~KT8^HUnAr8}}9*7+C6wVaAVK^cA$?Cou0RZQyTu8uO z{{=+(`hXWG9{J4!0IVmW`#<<2z`UyG_1zW0kq5_wfcd6V00=yqo-v(E%lgUXHw6L^ zMC@CCg-4Wk4!Jh~_bbQsuK+-L?Qn*BR!H-c=#L$^H{1oJ-77PQRh=Cd$vz4WiBFYY z0Hk2VyUu@z_lT5jIP{&Y9=-SrOTe9f)iJ!TC2j>q7UjK=$$5J`UJ_>E18{ zxXf)%I8pu)@y(UG`FF%Ww5oJlE(Cy0<$fvE@cIq$ck2Z#1bPPcK?tRq z+@JZge#87-MsVt3hlei))@?z%K8#FwuVv6n^0GCTZveINaJmwul_qa`W)c8~EqmE8=yjjyH`4_^bRbn;Kay30TnAH`;GB$_$7#n$ic>A4UNpY93|BzeoV! zcRgK9qt}iBFdF3=C6kuA){hx9dDG5GfZYimNBm!#$sbf;xaH-?SpdNGaMGehyPPl< zEfGJzXL1D4C~>PR8@tEtH&tUkMhyHO%aORW-{_TS`hRsh1I;bHvk~k=u8r27&@Z~* zTopp^)qxkN4=kwot@ayDughnXwhasGRZuc8EsmSNh=BV_gvc*rR2BN(3C$C4@m9M; zd-bwHrz{a4nM7jZofYdnviqA2c!SzZ%fGw{!CI4UQv2-Cf5{+xu~a|r&tn_(U+Q7>-ce&AM)%)VLs6aT(c7#VM`e|8v`YZZ6isO`-Q zd|!fpm3uP>o1S)j!u1{OmMLU&ZF2-FHitGgN7vS%%F)rajrFyS&9$|gwVRs_2>dF~ z#naVd;>SS1eceOyKQKCTy5+jKsCROzuLo6bJ+$1f4Nu#)$GMy?>fdo))L%GV+&T$x z)nC}09Dcq0+6Tb&K2v$7VWh36XY{1<1hhN-DEH^{y6eXM_~z!=&i2Ii*w)te=H_Op z|JWUgk$TDtgwAVwe0+j7aeTtU_r3X}{APh5*yODPDBDQ8=^q#zk{uizlsm`0Lj_%b zTOm*zM!#Q42xf#=rRrZpqcwjLG`NQk+?Ve=oLw+e(nDL@NDowuTl?;#{w+8H4sV;A zn|m!ue#iP#hTnTZt$WM%x4!RyKV|w8>3#%&k_erNnrW%{frFV*Zdy4TbuaqgcSZ0_ ziATI7KTuKQxReKL)`WgEL6`fs>N_QJ_54Q{7ws2h@APn^Uzqo&2sZuxLZCLRg79UT z^)z230T%)i5+EPYIuyJIW5*WFkEQupz|UE>~mvM(Q;9O)f1wqU>RyF9!@000KgJJNe=241eX z`{kB$-%-E-h%B2nx0dYy)wR5L*nbQQAgAOLhyAM)?Bh(#(ht1* zEA<@&`xSu%0Gq2i`O>)PQKuOg32L2WjM`N%>^r);_cI9npu2BU-$BSB`Q%{$#E#22 z_W;Z&%#%0MyAJg{iwdpSy}i`af-fuQ_Z_cv-%XSJp#NKVg>--hoxIrByb=O{9?;*9 za(_v`gNQL@1ls_xw`*SCTR+RarTh*<{QzqKfPJaEcgye{{71Cp+KG$w0yZ<>7|`QG ze((QT{+J!)3vdF!$m+epM!Awp=`3UO(of9ql|NLyS~TGSfKPiMaorMaI{k+E$82B+ z5V!#BRIlG~{?rUtaa?2+k^78F)aAz?LZGI9!bC1ni}6*f^ZaaBrcLmLSb?;mIJUP^k$gmz$4IZvxsFqExs!}q3 znEB=`BQkZMT(ZNXZq7yEC0z?K+}BU$TbIqnr*71{?uEG<$Nz6=tCBGYx#P#>gjutk zZi@M=8!#DGS#Nsge4e3@nk01EOf^EUJ)RYYtl!oWoMMirVqYa>?{A>lfB!tPS)CFA z(v+`llh&&n0z*i&$MJ`(PA;CIW9pZ6s3Nng-TZDs`)JO)D==oUqxvYkVi_(M-g6Wu z(_Kj8)EVzc9E17396{7buOU>sLZVAQ>rx83%0>ZDQ3pA(b`PO>pBb@4Qggk1k&2Fr z{WQ#KdEhe^W;#Rh&MV2ND@g7&-2e-eC9{6baNE{iT#lBw!X&CMJU5MKW7C7;{A9W3 zqOa9dSpu15#4pzMggc6;I)pnselguF4S(*&44v2f9lka#xJ2L9vr$)7*ZA3pY_N^D|3`gXidb51C2^T2-sU%VsHU zl2|u;_XeUB~>V+J6w!yw%>j=GUd3fO1Jt>KZ#kegwdb= zZbqA!R;wmSr>dCF-L7620oL#LreGFmveD#rXbecXeC;(>1&qn;AoUlxBwz8ZnP=rC z?n}9zzr?RFX{wWnr#iPZ4vi1i+;I$=UclZ!P}~_@mQ|D-Uf0ZB=Hu)xY0W0){GwO2hk|v?Sg~y`h-H5l43Kd05eGZ_#Yq+Ibao z-5z|SkTgnv}j7Cg5Z zS2IT}o0)`upo3He8^37Yeshj`kpEgJPkG^8y{Zj0GH&>I0Z(?U;oU{lsNt_nRm zxlu{SS|g`H3>9WSz}5RWES6{NWzcn*cD>QIM%kD$4B<}A_-;bbwo#C`ic&~{I;;UR z|H4oS7fqr{`G?~drBm1mMdvK#lF=>W9tK^vrSC9mPTfALy9l6n@)CVQOMA8J4U_P_=$Xzmk+LzGwUaj@y(c3T}o&<)l1SvmRTzMwfOeYVC@oaMwYMrL&APJNdVe9Gt2oY&c$>kuuEi6PfTZQsgd z?p2tGpIG6sMDy=6tvHMhWWAl zyL^E;7BQo3&N$p&0PK}(Wtbu@-3Y&cd+iq0WYvoa#-{@3_$%Iv%y8gw;tJ<5CQyl} zPsm53`G<2lCf?}VJUVnv(AHYpYbxU0xs>Fed!gI?uFuaxH;24OLP}ze9G6Y1i1Q%v z4=ZQvdDxoC%WlN2zudGym>;HNHuEXBf0L*+y7QIq3T~%j^<$!QEL~p(yaDV?@>lh0 zUBot26bJ-^pIVqs(~%rr!!Uzw5OlW4z1rYR;soaly>RtSLnDna29xbd%`U?NBUg>g zY04v=R4OVECAj7;jLCdLXNi$Z*~?IG`jK%6Cv!5fBjGOaJ_+U&`Cxz$Yn(Vyz#L(W z(}RxR)G1>&Un=y9HmEx;O9V^?0P8JZvtC^KVb?9a>{l2CW?omW3kc8Q7m3P94+u$+ zv>$oDgC@x{rV=FDeJT!P_6VX+ip0p>6otW=s|RDBRtz}@CYleEBDh?V;U(D6Ub9RIiKQ4Qr(@(Mm>b7@IdI7ck% zsec@T1S=3m3ZE?GPlM4a`MU`bd#)_;UW1h3U2&C~sXNouc}P%rsz&S@_R5pT+M>qx zQ^RL#+r({{ugPEaJ8t-FcLUs4{)nG``&d-*7FWTV&o;EQBx7Sn@;;TU6P!P0wxcHT z#WII4Gk;q!DKqcX#Z9PK_{WOmolTE^T0(6NsKj2Fm!EGOO>GvWtt!>)mcU8>NWRdK zt&dj6Euppv+H5~Chk5KP)b7k1zr>hNmL%_4_6`@-xqH$|FneHobed-oHgXqm7us~)m6#> zAy{&FIVpP~G0q$YV@LitL;h_>*YTu*4c(zw?jo&N4|p7;WPsXI(KK)%MHF{nGgyXu z9{NYTLt7=B9>!1kz9(NT@Dvg)o6uQCx#Ogmh_e)k#N{2>>|PQue&6%iuQt%=Lg^>kz_|@sHem;c*`RQf#*(;~@X@slpKlXpv1ygygBEdv5J#|fmM7JYTt9X_X-th$PK8+0# z2CaEEhsD!+Ey$& zPff6#1SPTXAl~82ATvDsZ0*0qwjzrL*8sfQFBjoSXW5e<1h_FET4c;Or(aX85DXA2 zV$m~Ls{3#nQIVH-z8m>u67eimLNzkAcG~e9a4gODPnYO~=oD*Bw3mrab*{P#g8dXH zy7S+F@0Y(C0V>E(U!#sv5WHR=_4}ElL}CjJtqUBr39j3<$|vwnOqy!|1YO+6U?^++ zbJs~7Gr4(f?!m9cRq6bga9R#nWv~PS9G`N5w;UizCVxhK@@94eWX_%gOScEO9gNjn zt+02g^fD2Hd-8z$s+Z@$rfWjbcwnupWUw=gt6#0ksH(DAaoWf8`X&>KFKt@2g68F} zUX{MHsqxH`iSK(`*R4HYAuMChrs?~=xJ#z)3EDEkDdrsCQWFhNqVR=dVk6y1Mj8bZ zH7L=L9mmhl)rVoep=Q|$i-txXF+NbBBG=Vm zmE&fZPorQ=(rC0Bj&Zh*DlIu8(cZJo*-r>dB_2s0_<+|sH<0di)y+;b>7HZIR*ct~ zid-<2+It#|Ohuw^Kz#en0;`|?GaC=oyELSOPv?p??y466^^TlliF3lz`Q$+qT6$@e zo4KQS7%4VbbJUU`KPrP;T>W%hFW3}d9lAHd*uinPM@h6pP7K1YP+k-x-bku_1DGFX z7Tu)|Y$8Lab#Q1!a2%q61e)<~28|3X3cA*_Iy zO-hdTT$GN0vP|&rDXhnlkEOnIC zD8Q->38C8jri^tLsB{a=W?5t!`fJj=EI4qpF-5=mM*?`|uR^v>NC~SN8^qRaUJHRr zhGTRCvZE1HioLdv z{>i#EAkd@46=8=z!PBnB-fs$f9qG@#wDV+9p7G<4T+sc)jU&q_j?<+Yn2D7eTVfI2 zD>#hLIh~8O7R$SF^X$Un3(8yqsjB3(X0+;$0e~B(LSXa%lYo@~6D+LV13}m<>Nws6 zzLg9G>zr9=DxT*Z;*DF6CY7ZJWXQ_x-Mxq0ld-{=*|N}B=;xcu4|&u-J=N;%XI5xY z@++x5R+bA&zTN##4A<(3nzl_N(KF@zzCiC@2NV3~-_M}l($4Lj4{nl)#~&}(jBzQN zW6xeJQtkSm7N&KLN##9tB2`fmlPXQ&vVs(X4NB0E17m>FeDI};CB#?{Q57FQB|N>M z2ZJY1t6d`Mk&G=$9tlQ1c;MnXE@YT$4`C$<43G{078G_L@XL??qd>pI;Q_Ij^q&!4I!q#e`@2^DYoF{rnw?~>15_2=M_)2$>Za9{ zYY21D1|s8vVuYt7@9xi!_zTtvHHBH|)Cr}!-{D(*o5+bekNwZ|9FN~DxM^E&slg2-!8lywL!%>X4u z-3J$ydT2sKHdsY_j^p@pZp5gT)g_ljx}ns!oOhA9%hA$aSKf=^Mww-3=L9>kXi!&% zKKH#?5QSQdTv6dUZeuBCJgR~=I}Ca58}-PKYR@ICCy^+>(%aTFh&1`?`i0U;_;34s-@8q#q{aS zueMj}8|Y*GBbDVJOA=l#_DU6Q3b6bXk58pxu#?Xc$(um3cjAQ`XJEubn!5F8xtwo^ z$=9_T#5+8lGIFOMc~M74$mu}2oTwvBdYHvNe|*HlO)YM0YIM+i+2luYl=iQ)fgF6*%%}Qfe?vXF5NY$l*#-;p|vZ> zhzlPk*+P-#4nIkYNm+A4$~ZNxml0PXQtLUAU2O8rS835~D9YmYe0^U5-fH#5d*y(= zAq$91HL1J|Tr}l+rIVpQqV|lQ*f|o_BK!J57d`5cj)Je_7LH7O7eR~#stCp!6HW%* zosK&Lm0lMZr*)I+&r>llCiv$Dp;ilEPvT2JNJa}tIDA(>>@fsZ#MB!#U{!6eS!%JsTcAM$u{vq z%_R^Qb36$9+S3MAN*Rp3c~2+p+v2C1o77fJSDqs_uh0oM)m$szxmv(RA-QE`v6KuC z8DigvYsdqiV}ljELSmu`+8DVcPWdLQO_F~2A%sf#&BGy`2M0*1dUnTh3oO{zkaDgP z;hEev-vDTaN4=@GEN)ux?ir6D^@OD;RACG{R6Y8eKB24$j2S*8$0)SozA6X^TUj6B ztx_kZxa1n)ixSF9@a4AU3^|<@5JJl^%cb61Lp?5C%vu+$$H}q{mIFv1c5=ZSi0`7k zL8e@H3RgR$i*;4Ar$*h4Rp1*tP$VP)hd4Z9zdJF(t67}YL`rjkBa*N}i#bzKO2BW- zbEQ1EG@fzlX9*IFFxw)w&Pce16rsRNKL=Y?Lr1WmuVFriq^I|i8~)%r<}Oqx0oS|& zwiR!p{@ez%xuc(_mQ{-jYYfu(M5oC&blXA}#Op+-IeBD~4RqA$kaGF6cJMO=)UHTaPrOL zll-{`kV1f%WP;4XnrYsoS58zJ!DNy0m$*2adF8#jg|sS*FM?Ft#Sddd1L9^;nB#q( zM}InGfHFWeq`1k>JfyQB8movm+A6O%c8J)Z6E3aPt#Br?A)kuC$KiiBc6Kf#RsaDU zv*~V3BhNzHmCH!I-uj=I$$V6<~tc5Wx z*8eCOLq;?Jm2B%6^IW0im?)`r5+-g)TLTLPdu~1Iqj;RRs>2HYU8H5Ce$)fnTJm%Y zW?vTxc9T1*oCIn6iLtp6*eV}|!_L-yL<+G)QC0KjCLSwRN$dUqfy#OdJGtQPpEEj( z`walgj7{np;e1Sdn6T4XLy99Q<_^P%MTWfc@ogIlv6t65B;Y{;5uLz2V~8v%+w%PJ z2h;8f9~rckd=cZ`L5GK3|a1&0B=i_&h&D z7N4$Gz>L?A@9!X>vx5JWem`yxi_lYW^GS1b(l;c(9X; zT7}AQ0Ic~-FIVP5Gjk;HYC*H{_ho3k*BG^LH|Q}8PKn1~RHkP;02lDtNWonpas8QF z!cU*j!DnTUtFQ)h(v%5>&F!KwMxsgEKFVV?1;g$Mi9P7oC0uY`eWX;)7r>#OTDo~a z{Tj0gEvG4gWN|Z>%U7NFhD98o!p#(YS;J8S-K@boT3Y2%vYOO7Hy`VrQ}}&sd4V^q z`BGCJdPiE}h}rI~ud-Q47osD>TO18kc^MzUiA&?8DHg~*{%4E*#D|T^q;t&6XhO#X zZ~;$}dI#QFFkavqzMNylg+>}U4>;%g=%EcY;BPxVNC_D+U%Ifok7Ve*$ArTl5ZXAq z+(gpjunmXXi;u4-9%a&fM9#IYXV+JSkjc!a`>~vbFKL8@yG=+m37P4jU$ke4ozUmrIM02s3=uCF0d~>s0>?@PQmb>7DRyA)_{9M=>N^Fp| zWay(_A-w}_s$`yz8I@WzsjA}F$VXa@45}E@;_Omaf$?wxWA$Fopgr4m-g}{%90(_8 zT9c`hGWE){S!-U?HTHTX8A0_EbxBL$$lP+zE1wYvLO?#h{=jt-p5XFkXK+HK@ukunkg}Rz;YK+i2cipc%D7uqE+W3^ zDTcY>FXO=vR^OH+dba$OY)wc=2ofgA>F4ti*x8rRmSh$;FDm|(rd$?MFM`sPul+%) zaWY2UJ`#yq@f(Z=Ni?R-m_@w%53F7KMlax(vZ%r{cYLqPUt zy5Alb+BezsoTzM4YUJS}IX8yMz!7M7NaM!RMt~vbs*{aggNfoLuZoY1RUBY)n;jPj zcG+VOuE?gxTbyO1_o)@oNHkPuLtA03SBIb51>d1CrXqia=bJRW#VgsNahLY;clv2( zIV{kLMcWyswl=D8WEofbznLN{w#k}cM;i6gpIXY7A-N?DjdpGSYWdM9m2x)u983v? zIoC9vN^q&AAt|U9no(va`-Pv9x;B<{LvDSjZGd2`!i#<%)`*F|Gg(@s$$q1Y_v)^* z4_kR7`JQILn|W`|I$DW~#cGjL6~Ws|TX*1^8r`CrGO>_Sci|PDNUc4uJjC8v3>Fry zJ`~>kb?)Ne(~qqY#=5OZ5{=@QzCzdkt~&bh+=;}SnYZ%QYO3PM;KygieL=Hi!TJ>z zTjdw3vL4XHVwja54~gi7nR?}#Uq)UKKYdQ-sIdCc$fjAN*cFAMAI-EU^tp07t|Ouj zf+@k0ju&K@>y(7!=YZ*BFoMQ#>jsND_dHiv0~%F>9Wk7WPp|gFaD zQuupp>Q_ba<5I+gIx*ZOpipUR(yWA{-cq--%+_u-^u*ElQ?y488qXfGBui$FI|rVR z7~Y&Dd3st?)7WhoAYhI&cB^$lU9nt`L?PfhO$62oXBmukVs9iE;c!TSpX|%`1mMkT zQx6-2BCt=rR!r%}z~S8S_Tb0>(7aAlVEavdwW{9%jyY;1o8*ZEG-P{zrH-3>O}1A z)!#15!Qpd>CQbl*ZCHQ8B>0FNXkTqUud7DI!{KwRjO<{bVOp@QiENKkwqAHqm%0>Y zV0bYJ%E{c!nVOB7O8H`RlR{m;0hq+F&LxuYE4FUtQPjpm7~tH?(=JmnMOh z*~mve^?{%4kvdbm+gSFMLNo$fsf~v@bha-qihhcEkipE7>``hs0()7l&`r@rMd!kf z-{xGUc!1@OH}^@EguxMBf_``mFHGL{ zg0GG8Su+F@F`WG(RjK?2Ba!``gTO{hHS%C*QS|W3mz;y0!3dM*zHVu}I;?6ou-^bm zz<*e@E7z?i;OVHmjgSW>PR?e&)9aW1R3N?X(!?wsH7@X7 zl4Idmu`L8HF?V;`#Ow&dAc(?*ae7|}TIdild<0hlDo}6CkH=uz$8r7A zS)KfeuN8Pdeqy>Jno^>V()YIszz($UxDu(u_sda}KAu`RteLA6&^~=^ zXz^tNX0NR~0Lm7j^%0x3p&^;;li?%_Q?BQ2OLB>{KuStg&gR;Y&(7`iF9UfBDax5(}gMsl% zDlpd}ZohDbcbF2P-#sk7bgmJFwDzZcFiR_xy&QU%TWa?3j26(fKUx&9y4@Xw6cQi< z3odX7j1gn;nR_d6o|r1!|1iAX+|gmMQr}kYMSE(_>eUCFx}Iy8R8B_j9S`Fl(}_`wImu?aoVYj7MNROSVv)aFo#2_j#xI1Pjs<qU}lG{GO!;}jqNzQ%Vs!^ zKh^Z-Mj@^1S&onJjkhTB2Azgh7{-QB&#bt z#W=ty;P%P~V=k!>53yX=0I)r9?)1=Jh6*)T>U9m7BMK*f^z}(u$TSTF^Lwo$<1cK zouMAu&wH|`yCgCQFZjl*5D8%+8HLalG*YUQgT)>&EMvV+R-dFZA6$qiG>5G~!`Ngg zWd6XEcI_nqHb`!gF3Brzf+6A#bdNsavWx_|Ovcn5-sL@BOpemFD_}au%h| zp^vyf=P@mDEAu=Qrn5;o<@kZ0W$f+lc0zScI0QwMZy*a=B&Ei2L_9jod+pL>QL39u zvAo*^<9HUh2m{)u2XN$b5^P{k2^w`*Hnau`)D`V?g@#LpV!3QNf6d2Xz12Kbl7#TU zh-i#R>v1xigh)uiE&X+@R2P({j5oOUhXc`?SgZHXK1n@wten~7kEY)Dl#Q=o4yK70 zbrxTzCK@CVHa_fv9- zp8Cw@*n~}KJ#*u)!}CKt{k(j19C#&EcE>7&D@Oe|gw?OAiEn1Io~A!ii%qI8+pGz* z#)b6jFop{wBR{}na=cNbnrsY7dT2*xHJU_&ODtoX-8MQ&NAaw`3A)>AmZcz*>2^82 zDzK|PLyPGfpk+6hLyW09%40>0Inkzz?uIF64GGEZb@J==7K9i#shKZ}Y23ckWfTO- zo$-)6t^+$`%4-kntOiU|3Qz=hLsQDoPwF}Pl)eEa1e0T}$y^&10U$pqu*9+c2BcJM zRDqHx+sij(f;VAPm4f?rDRU1G<)NxX4duj2hC*)j!rS!|adG@sW zTl!9Rzuv2{VSQH6!#aY=_xhI;!%x^RW1f|!UC-L7%SQ^XvZ~1=-&k)Hq+DN~C=W&6 zwCmV^dH+RNQa0V#HK(-b!7N!Z#yefZZ$MgsvpUSJ;YqE{McV8f@yi;I4B;DJZkRKK zHR2hjkmAg~?7^U+{<^P)2M{RrgE^$@2q8-o$KDMPwH8#53XJo(Axx!{J|h3L^u$-q zG0(el_Yc$DBNTX}rRt00p0U=WwVpc@5riwnDWFNfL zr(0bR^5;!NlQy+p^J>3W(8V(W`33D;_2L|zz^axbk;!Eo0R^#SQrcp1$3l}YH;(xGV;;5GHDJy zV%`-^{o3NR*WDI%$d^}Q7Vu8X!^m7Z(e8m`BIi?vU7F1pI!8QCo(4i08e6`Q#cZ?o zYRKkPHP~qP&c0gY&WnHGhEnoa!QlbH7h$Fg=sRUW%w;e1=lO&KNZCvI?>9%ddx*Gq zbgpwbp*m5zE<=o#JjRY0YaVOnQy_p#^hk5~(C5#gNf@qmxQ0x*XH`9uo6d zr1%_1cwZ)m<`yvQ+72}YrzESu!pTuG!6N(07=m7JMTHP~cN!=9O7!Fc{Ej&kPCk>z zxRCYh0!4FlT92(2?$oih?P8hL6gArpvRU;f$-@lND5%0}nlQ{Pj~XsJ*@K84afVf zX1*X#kKRvN#iet#c_Gg-8qxe)j6KqCFr=`Z~Z|8&hr(CLFc$` zpdi&32*Yiayi0|d3zxich+B+;&glZUqeva=v160V+(l>vyQPaNy9}d4exLCkNXrv9 zTyBC#eA%~;H%0x*hKQGmDvTU(W3QFulG4e<7W@wI(b2P$M9-^8aME$3J>JgyuBdI< zy7iY9pj9Iz4iZJJ*gQx3S}Q;3M=C`SqD1rSyaTgupe9swpi#3r=1kJI!z75zSHD60 z{gZUa1wPxS!g;O|aA$aRWWp;y53NCYBM1ZQ%}(CIGAENz+@?QA3+cs zlN0+yjC~h>SAuahqRpH&WzKB%dX68r73JN>z+RWiJ@%Fna|Hd&9j@A5|Wn#pH2yS3e=<+ z$QtNPR_RQ>`qS-XDIZNxBGYXvfhATpC!kY-)3BfWgE51$OLV5<0f9B+Jj7T-#R$v3 z173wVX)>}Ts$_T(lXV9;qf3)6RL3*sJqsVr7b8L!fz?|yaA8mYDvm{LqAA_zfC+w1 z$cu40X%yJZF#5Y24V)x+WmFU(r7kbf9QNbDKw0BRBFhLSONi?nt&ko+Dh=$pSe5|_ z6W|kNBO4SFz29ZuMJ?cAAZLl!pLoqGrb3LX{o~!(64B4i9ELq-OmW35H+bB6B~=Zs+4S0?C!G*y4i)b}VwOl_M+?W#2S z4apJ5o55iTL^OkbtRlAz-a9YvJhmNLiQaEejm|G+qzp6ZatnQFy;yYsdPjMjr_fN= zrIox{Ej*~B0h_q5zYa>>rMuZzwAMC>kXxo{Yc;74W8}#-OycUPM3+Fk68t18nn*h- zf|s}R>(5o+H#NY_}Fz%uyyDaPmCX-W!iobYanB$ZjxzEKR`*t+MT zV%M?uQqjB-_AZC54nLS!@)KHmDXeh{8)~6!b zF41$$>ezjpnSFPo@;~u9#ee>oLu6RM?}`0*T8vyuL3gXnv!cqE6UoKTpPUw0q+;FB z{ulUPh5XkT{_6<;8zMteONzVG+gNQ|X%SD*r@{&-pl^f0A>p79U|_*P|CRHvPlW+g zNM`$SGz?N`EwfGA0%g6(hT<_6c$!Yg?a~4irpLB~{;?L*r#uF_T<2 zwUr;A3?m8xx^VIImN~UD!9vMYT5^x)8y~$`#Ns#z#eXFp-jvg`IBsmURas~M-;V#P z~vNNxv(o+ z$+KIt+fT#9^Ow0-o@|~kHzN11$8TCwXyswh5&?Mhp{J{8Cd!gZpUrqM8XNqUJ}e@W zHB7lN1Pc#n91l!RI6pDKU8>z~Y!bL4RrF&7MD^z5n>dY_~7Gc5>cn-D^VU*^|L(zG$ zB%GZEQqKv(Nt|i%fo)dqYXv%9#b8SkspB-|B3j)3Z$P6TmAO$wEE`KYgf#bv`veic z7^hf1mu-y&tk$U=L`re-*U)OKpmBjGF{NK~s>d^kv}}4YY>UK(V*g<3Q$-aUeJ<9n zF~;IOVi$?|eO_pAD#0La$tg>Vqqa(!T8Go^py7Gsii*wIm_8f|OSUiK2AiO;zY&hZ zCa^IjQ-SPfTA0saaqHRC`VwxM^z4Wgm9#j?_nKXTq!*E=PG0zPsPsT5H7PGfY1Z@P z@s%UxIX6jrG}$hK*9N$#7ZLL9`YO3xSkZhTxD$@3Os~3W^Xk*d9DHkVeVckKvdjmi zj~8%7U1PQ8NmIM!!~%+qkPjzudwUi33AML)F+eiY@!Ei5(u6IA{Zn4)uPLU|IYS$12^le%BW?~%7|OBwu0(czX6XHS zgU@MV)a3f9suV+#f)PQV@GTcJLvxu=#`)yaN(g~zQPKG2gk&mCSnqS!z`Zz10`Yja z^;$WaJP}yey3WxL?IP(P5DkPXI4|Lzp)%&7d5xH7S8Vb{GOzea><$?#);6WC3^cUf z;jyTdtl_crRzSSZsscs6G!HCaBX3(hwBv|)yUTiTYz=UC^ z&%9lddF5K3)h;A_B^VB0V>U!Je%;Dt= ze9dZ(8LI2Gk}HohmH2AmKMDFn>zX+EifT{rn}m^0C2>1T7t~-l(mtjx(xW6Zi6w!K z#b8mEz)cgd3-oxAI!bFoNh_7C%nd-uspRd`8$XILfygGOx=cH|Y9ac1se0c@-^f8> z*rZo}k8yKywe1u!-<`l&jf@~KRyA<=Wu`!zeUMZXU;@tk_9`$D6eI>5Ga~%5pB+5G zK&Ri)mNXC`o%-NALJ3Prok~qxhgR{5hS(34d4*VtZ8Gl?F(tZ1>qhswktf7VTB)xN zTBN1_^^1(i*}n@?A}OpAVV8wSkqodU+4tAB-`RZHb+M>-?HFrpp%7+l7LHJd^XyZH z&ULzd0PO0gP`ad(rxeJOgR*kD`Lcq@Xp8h~r}R2SIJOaUUsaIlM{{$k6cd(lZ;E$K@_*%Ki$Mdrt}11As+=Tib|Rttd`c+F|PPfF=b{Z`84**^HuIA{on1J zwvWpK6i+2dHM$>ye1d6x9M(3jbnx`awaQ=xVN^5;DE>+A_e>uUeYft1V7DJ^uTBJZ zniW68+?)*FeC(~~mZ$k1G2Vt{w{%q3iZZW{Yp9CK1c*02)LYy5QfDjpH>PUyIP|p$ zp=HFB5|KNTi(m>_F9kY-SVx5IKcXTO0fO@z_v4t zDE}gM5hTGtwBOic;v4g|iFR>Z%q4T{aJb>`fj6F3hLs|vpT}8->t!S4)qCRsjq6Tx zRLSQYbI>M{5g#QFPF;7`e+&Onkn!lhTYb!gvwd9L*hF?bBHnb1R*UX++6#~6^_AS3 z<7g=d;$)KB46PUbrNq(saFcX!+8EoIr|}TOFAqlE^x3U0mV(N3 zp7Wc}YK>riUUB(kLSRY|u|M5f?bM$30k-Wz)-vMuYi9)>Kd=@d zHv^M3B@Ne=30uCyK7|j3H2BZ#ZMQ7jbMn=>4o~W+KQ0d3e2jE=D+-GGm8mEj0vxd# zctJAKGjNaxfeAV`-5UAmEY6l1cu}26B*iMp^HGv@%=<&Gc0>!kbxac#QHg0Ca4d1X zob-81wePK>;=+Ny5LmuKSU5ixCn==K_&@D?c{r4B`?rwoTf`XO#xBD!Q?h4dSF>4S zj6G2iV<|);J7vi_h7ekHC`XCf(>1*+TVh$$t3s($OPA@4P+;QruXzpC(_w`abjbwD!|z@*xGC zrV{{VmqzE6<(>b!VeTzIdSobH)C|Df?pdm_uN}zIeL!6sy6_s`4~n^{m+oy!cZGRg z`guyhaLC(Wg_72AEQ%IoKDDO5qRio&;yo1hiZ|5ZXpF?!sc_BF4!YPH=ZGTdYZ=aG zUu7Y=Nzl-$1hoZmL&0L!&^eV0=AzBh5lBA(F+Y%J#?)j^t9cq+$psgjQ?V6^<=%#5 z82McrZgjVH4?N!1Jh4d!e6Vy+I?%JUFDkDuj40iE-qumvPv_rbNv(+tdsUoK@M-R3 z6!WUuH}PxLlfalX)p7`hKQm$Sj4UBel1mc{6|ZxztoKp8`K?$dI(6 zd&0SmVL2<~%LAeTnSt`!ij4RipVvTnj2gitxY6*X2I3msGOz0hn4Nke|FB_d!r2AB zN0+rxGo7H;S2agPsLF#=|_*|8_i`5D3+ zAd6Q+Bv48vizb*^3;A~6AQVMu5c-yUE5c_5`eSJpDEHY@E#8p6B0PH_UAB_P=Yiv6dh|KLFG&+$KWgAzvt$E~Xw zpL=?)g=LRrgL~u3+dRT4?hUDglLCxNBHG*>yr@Y&5NGT2+vk1%C|-jW452_#{}I0+ z!TOx{H0V796Z9%6k2c6JYj4xl4LPlxo|4OIcwuOXiPbn>+$)s(zYUuvA1?vBUnB8L z3Lq+e`Jqh-8Xnu^de_7lE1<=hMsd?bT#uR@FeGANQLuL9A#KqK*NW2J;y=wm`4;{A zI3r9fEVklLW{9`KWp$#gT)@+czD0z0TC(&Q0qAG*aq9%{&ZDZ4RJreLC%NSt*{7pL zU$@OjSe2_pto|7l6&n6;#8-CEL)*J9P;$P|NXRwDj6lRTMsO(bZLn0Y28c&Y-=LBA zlGPaeof|QcLGx?rvb|#Zv>^}d-x{5<*#x<`)ddeM9bfhQznEW%8TpB!QHWpUvW#J; ze4KT)Z29_x41c{&**rBC;NP_I7Hf73ehxzU9qKVDBV)H(C5 zk?RWmb$#BTus9X!I4K7_<#m~Mv(%eCoK-L63ZZoz#(G-=eZqGpVvpt3|7jlpNI&+* zV(Tq?-GSKr@nRp{|1W2DKMkrvH%S|(G*t4&WDhu{PI6-4S!q*kkldI%w(-^lm9oW( zHIwTlryuZo>vyQTyw@CCO4K2)GEGcy~N_IxnK&|Rl2N=n4I;0oSOP%!;ywDoVc zeF&BxPcJP*Bx;(Xf=xE>Y*?z;nQ0A85B8?Lp61OL z$fZ~HIDvK-iwUcu-vM2u=S%R=p9PUSAbaEL;tJkd8AqPPx9dDVuqlmh^%(_%DPD@a ze|;RldS9X@%(-1N{gj6Qq@Q!a4$sMrTu3rYWjE#rWaAxr9fP{-q@@_lHVY-L;3$c- zl8b|%Svql=zIVFwXHz=@lbQ}pi;(^jw7Lg)TxMXgRC+AG5#s&cyv6jc*3B|k7qf5y zq9h8;6V6(&DvwqW?8JYlBKo!ZCQS#}&+(t)_I9~owGLs&DjLgO24riYRim_p`)qWWT<4R$l5e(A=fTQYa-#&P4@aV&z=BiVk2x@&hLuAQ^ zWpT<8%kHK)IKU6{YL>%n1+faVGg!YbM~X?_*)*}?jtOCb+4Tl zaQ<#0#E5vCKt(RQvvDIl$OK=RAcRxJ)r=SKpk~U_B!I<@)6K;NHzVvGLS2xx{`!wf zDjA%bzN`8rVk^8!6qF)E%IV^c9A+*4lYQ;zz-Nre}YD zBLx~nw{SQmW{>U%U|HN@3^pO0wc1$STR!B2k1k>)Wt39>T%0JE{@jRaV%k_^7hfh4 zvqSeZ&CXBlDQREXDn7@v$0DV|UGB%@c*lGjmN4(|ce7+D_@iL`eIvtfwFOG^5k%|W zBO3NFgGjS9}&}1U(Gj++i|Xo z7h{Ao)`r;^aO$|k&lizFLY$sFBOurX%r(hta6V)#Xe{UIWy!{=IrDDinb^7mE9e?2P znPIp;!nygKgP1fs-ASk@{cV9lgNeswsiBqpOmUAmd2P-NsJ)S#H7a5nOAr@>w9Y5Y z7dG1Krdx=4d(c5LDq9_yQCSZN$0roFD=h_9cE_eb&Zo$=GC%wcCGk;+#0}_=e z`G;E#(un>oC3Gu6&C-!S>GU5(MqB+BYJg>++lqPRP@Ug*k~{J)X5p@MtN(yUst#&= zP`6}Lp~B~3G3mAuF$?hnET&_JttJzE&0+Z0c12U1J6 z=%Zr@(qM+QPMSNN6qiI(%VGH^oU3OI_E?~lpeEmF;$4bhYOV0Z)MD-7l)DnPjj>}g z+!a|L`oq_Sai2mj_W^}vzyZbj{gEEiq^f~l(_i2+<_d3gA1A&oDWJ$TCc)l&o zs89@l&J1|ObnwBvZ0#ds@v9tl^`GxpW1O9aj{WqJ&T=9(^kf@BJy$5C4sn0rN@*}1 zS;`TRiA5SyMfjvW`IMfdM{Ssz(#qztUs_KaKa6E&tv66i=b_BZ zv?l3+wb4g7EuLOIZ7f?cvt|zsfBOJQH{jL7%R?`_GU@2Smc}Jq!$2z`fIrRp02Q{| zM0Yke?$LC!lpaW4p?6fwxdr7TW8_)bO8Rcr5OeITxVh2GQI{bA+_-0>yZc-+1)d%) ztt^iV8%p~bK}trkkX$BS{_5tD-)_BH!Qr_bksE_cYGdoARtsMEDqwxl)i-u!kL9&> z%P)B^p*@yvXwcUtSusqz`4VRNq5mPY7M&OS9;#r@)Jem;-=nQRihsL}6~>KsX%=1r zfF~MqXW{+<`ypqEK+b{RBpU)v+-a#~(=>tvAe6*p$q^!-8oc(G^RB7KX~lGL0)+ET zVH3q)0ExSHUWVA9X%Tr<(U4l_CudXvuzP=_xtI!wn119$irPAFpkPXBb0A{go$?~atk^X_V$RTvjjSnTTix!Ezq2vzHc>O?#G=?~^aGrmpUlv2wo%snCRt*TzL>Wb18&r|jn+1U z8TAsyD|MY{Gsm(Y+UG7wq~AOSGdrA4@=+S*s1^{@n=!paQ&WF(_T3*;JIzcSx3uS|LB<_}pg$OSpb^6)SlWUwakaZlVWgnxg|0jzQ3 zx>Hi49Mt;!{J?g}QI=lS=9$w?kHo-7VhZ_pvh6I!$OBH!mVidNMHr_ZL~1VKUD0xB1#85Si9jj{R?u%(k|>nm6oV~_$9L3Tr0@p*+~iQ z^t*?eEpIYN2Oci2Cr}&e*r}3MXf-ZOyu8DEI}~gnAN zXLjBKGpk`(eKcevM>Y9X+;9C~_JJ~v8O<~^0-;I~@zx{0ZKv-ND|$86T!g(qcS}`* zOgTg~8LGIR$$=@vwll#lp!N&!uL?x2g&W9<@mvrBX9`*w8^6%Wf^!<{J;jMv16*8c zCkpBZ1eisWqewV-OWx6q3FeCyA$r}VDl)$ZW*IGt(4WMC%@#55glhcl?ywAZF*Dvv zn*+23hh4?sZ!F848mWv(Ens=bmzCV;PP3eng)sjlaT6{-7cnN0O?Lhv32VUakGp{i zs+AOyPs!MRWh8rj6AD0Feqq}-ov{4ayPF5#C8KgBRz;?ESgDwd9{PNK=rC^-XCFC| zIb9?mIc;lHBKnFMT$F(J<9Uj$#{4o;UQ!bU>s;FMQB%So7@4-&s{u&!nU1e&XO5!qbx?az*Ro2np3Y?0lo*y;Yl&Zl0mEclGUb_2vi>JX)eieUP(L z1pi`>g}*){6!ji$zN1t(mHDoikA6lY40i{jbGwGN+fXABpFe&59&ky~eKDj9@`a;v z^=LF(6ZELZqGx?Y#-Ij=Q@yAi;TDJsdYGyiCq!VcyoQG4FfJXyl`0MHb%zsD?c&~^ zaC&0M$&qRTito!#yVwZ1sG22J&)n~|rsZXpSRuRqZuv%HZM35Z-sH0oeeu5C=|mzi z^Y=^oYrxOMINKeist0^?$yNz-J4)5DQ~lMEQ~iuJZ6C%O+?xJoM&xVD9*fVF2XyuR zm&rC@#Au2R|Gb601lQ`b!m)MahUmsHi})yt@LM7zt+`cL2lZQ1{KVy^E zDfG})Bz6mxu=9f`UGEKBBxbQ~=tbnt2sIDuy^*8kq4^8AxW5k{$bSquzknOR9Nm$X zcIxcak>aP~b!y#{Bh+WV5bq3&R}rHZbxYOL^Af!Db&vmMJ=xiKGPY6O?0uuUxw)Sw zxo~!@3)!kJiFlPv<3Di}A#`U)aLx-Uxh;#I-Zjj3iQ3WB93GRL@u~Q&+YzffOqKL! zy{cz75=4+TR7J0*BZ7HGxPIh>b)uy|vT_7iJ<?=pl1)sEP5Uwl9vCBCC=&O39GOQS3_lzCybLhqGKOT`@d7}z0cdbNt z_X56J{!F$+=`qmB8+aq_XuVx>$gfX1d~^M8NiA%OqZbd11|EKL%pHqL730^6z=gBd z-j?U!2*~pZD>YNeR>lRPl=fIe@E4p$xBvM>Uj2V}mTMBmRgPCy7UOn>jD348RCby%Oe6wQPvp&Eq5_tscd?H5kYHm4u$>R$TxxW%7MW3b8KB^C#jT*k`cY9vn-Zr-z4 zLwHRKUc-@<1NT^*%vx`75g)m-hbi>HpqeawKx2uchWOqFzje}D+b*3UBy}* zS`%Fh7BRE9;9-d~K0iKpfD{FS1@mmSVG-Z*oiPD?E6ay?@M(6n^7g8HQ{l$Ux#jq0 z%Q4|+@ZUdk)XA-Sio>3N;a^MG)vVixa7l-Eer`Nq-FKWtA@p`n+3&@K)@pM8k)OWK zI>Pq`A9{b=FsUrC8ul9BuC@i~clQ-SQi*|Do2Jad$vBUHAa!<8QO#pNeOUOF>zN+H zq@=+^$CWdY>(joeva z=Hi8$6A4j)Gz5XtT@Oh=nj<3~6mwBFUnLiU-g!8=$hFm~vz{0)e4vg>5{plSNSlc% z1_`Jh%Dg>@HFB3KABVk{@UceLSoVKU_q!wGAl_;IJ>ibDc=zt)_#VrLJ4(SJEx{o} zpGDGwLq#ToZX`ElZp|gTDI~iqBzr0(d+o89|9HXBu4>dSY1A%g)Y&92LeG@0e<+(= zC|%!*Z`S$Jtew@Y{a~lPnmNn(+dw-d**Wt!x>1{;klgT)ydXRK;bA^EYmL>Hhp$H^ z3)w?{N!+IY64?-W;aXZKt#4~Kt5o2YIwz38@X|NT?`ZkO-D0iZU3z>!^=B43*o-Oj zK3#Fo-xX;-Hmw#5mnEx8ynaaHHA94$97M{ivgc%9-&;M5`dbguITqY@3B6%{Ge3q7 zsKW@uh5Q8Pv>lEh9;az4m!I8{8f^w*3D?wM`(F$DUfz~X5AN@_fr4Ypzv>o|Qyu+g zK3<~im!=rD9r`r7rO+o_B*$yF&uoZtoM<`z^Bix3B?i66;zC>BZ}x-fd>3p|3rp`w z%C}GC9LASrf-lvcejBbR7z~uaZNr8<_vr@%;>wFm+%)a5bpCymEyMj-@EXG6soF{x zOZVqvis>>21+lRNS0*B6}w~C+{ErY zy+$ceQJRIc+OG39>=tgzhzEc8kc^6hQCyPs`=d2IeniaFd`XJm%(qfA(s(52CaonJ z0bYmcl&p(CH$3pEKS-)}q-J*}JeY$JEcofxa!m?2wP{Ft)5KYn(Wq^H>6h)2PO5-4 zs#`pY%;h)wo~_TdO$@lI_sBiITA7h#y9qIE>!sQ3J0Wr{!-_EGRiLqBoBOz+%CiXOdQ835^Jwo*2ZjxySVf4mg3dh{!xqc^OUE z{n-&j&p-GuR{bIZoWrW+lBDcOJq`RUJZSRVPrgg04dS6`c!eTeRDkREz^N#DiAztP!q2Hw>eMAJlK6#yX%boXt4r{#tiRIvJ>0#z5 zec0b{Qbd%q7WpXpA$i0tsZGQ?rd8V-Nf$z)TRD6`gpuTWF)J21ZFp|fm>TCkye@JW z%w*O<*CiQweupo-#2@khy*|2TA&aPG!tbBo5I_OPaGbFqFsn-29_eL@+;bQ7!kz!Jkvvr2M7~ z0)mjGKs#b+$tV8tiUa0&8%#V>YV}28aNkgwl)Q+k{?P(C(N?=J`+WbX?Ig|tZkGEg zm2uaK3d$49V|NzmHBdwwUTw{KYhsw~9f_r6w@fLACWZR0EQmEu9z0pxsn4GkVAbQ1>xgkRukhm@~0 zv88%!ATLUybO)LT% Y|A66c|Hr>DbJveOW*_11me?ErUqxaxRF0^FCTO94j!SjrIk!IH z*>Z#;(q@uZY*Mv|&dj4}ukPpe&uO=}vUj1T_!qS=|BQq&1ES=0O ztb~s$=>H4f;q=s34`Qq?#PXX1zP&?+p?wzh>z6|27OkjY-&gW(`sO2)yHeUU zV(Jl{3b`jx&elsb^X)ImwQqmw89RXjMi!BQk;xu3vAWyrFL#)UszR4uaeqclTtTfn zdQxi_Pm0cZo}9d+|G>LEK<_UtD*q8^VeN3a|ICflf5v)h+N#61sMe?Qs#QL130~jR zH#FXk8qKS$^@uOn&e#He0NbYrC@!dT8(1Jid8ic#u5^3^+TSdHns^$jOO zN}}mXm{5Kdfbe zlvK1vY_K+Obwz*Rtfit8?Ys6RTi0H4KXl%@N^Eo8jNfNqiqITZ{4OQV-Yob1hG#!E z`cvOHNsXJeAeD{|XzP%9OB>30=k#Nv&)<38p{ta#^i3M`+*?$9L!DMJ?LKfDxR8m0~YMXv?>3SJ!w{GDS7dKUd zh=_^g?CdA+QkHGe$lw9#@5S2p0gwdHpxnh`tHu6n+25zjHVF++&lnBusHkiT4xT`c zj%qtJcKO>H`fMkI$4sZpHSf^sFNtEa3#C2i^aqXsTY;|te*QaOVk6-RQ-eDJC%o`{zsw37J4aL22af9!UlUM$ijKIj2( zUuc2eXTVn$*7mYLW%|6gXSuob3b}{A(r@v>YvkNL05ltrzP_RK*{+KhJ+ti#&Dgmv z&k24Wg?TdXFxvPAOWh70fz;%w)_gDYTiSJ{tW95%Z^9JP)zioM#*JDzxXSuF@6Zpl z@FVqJTcOX_8E>DWfwPuTLhf^v{mP+x)*qIA;rmx*{cGdeom#YW!o4B+51U2Vn=WFn zZLiROg~#0`Xc%R_be0nHUMEwWOZXYD|Fd^qrK0`nJ$ps^)!p;{z7TDm+4i;2=f0XW zZ%soA-lmLoUr;Cifzm&ouL5*|X~6BL+IFN73-`gEd_?Wt`$--5&>^tx|Hhf^JDPv| zUK^v$M=9!XUG@1OUixtG45PHwpVHH%yD)CC9lQt40CWJQ_~7P}p>ZSV$zqX_U(!s{ z#s1|fTPI3eb%5r)cb$q4|9FqAnFp44@u453PU?z1um$Z&5Bkw>L_TG|dkHm2(tPY4bI;JU9sWw4bD%CX!fpaknIheS+OsIUNBw0FMR)o zybDh+CVfLAtcmSt=)7%|{^IA*yI7`s2lxlT?uY6M%Onf<&9b#44WIuWWOs_}dWdg< z+yVwbwijib`Ma;<3=cpqGq#*3m)=n#ZzrD^(EW^t&3l)cTD9kAzW}K~O-c_`zF^S6 zghEowAj6B)_vzUpQ%f5=8n<$<&}G^wOLt(rwivfD@i~#}28=8eIiQa(0C-^@s=$k* zdJVwX;wgRY=h*ALL*^~oO5YI~rQj_eJQ?D9+f#DsPMniJCC8o-ioDmOt?5HizT1eu z$9UiuoHr@CcsppE#=cW6Ydpu9djMsu{eqGTw~|?NTc&*z7zxy7aYtuoShoU4%@v&v zPhX1lf2GK~Pja5v7u5L_=vbSCEA-VE_nUw_JbsqL_*I{fUAG`b-mCGhVTJm2bvqgz z-!f}qi~WE#)BXwI_49ESR}f=g&k2B!`+t{*kN^vgjYBfV^xLc(N2IGHZh02@~?k*`jJUvGewS@!5iD&s)~kx=6%V>H)!c6-`6V^eRv&q z$fw5qyZ8pk{*--AtIq#H&z`tK3yysASipWMrpdHlIQKg_;u}EVj0cu~MgFJ1et;B@s&D>z?s6>Ixp$~W_nB`U z9bNdpVyIyMH&x1>zeRoXujm8kBe4HHc+3n69y?R~(51)VF9VO&;Ys1YIv%Tg_c#A4 z&HlgQ{<9|QSJ{W~6Cwv>`dgpSq*o3T=asNeHGePiDee<uz-RJmk;>ky#6C z@eLXpo50uBMtsaMlS^p!dl#x)hwGDn&i`rG)l1_SHiXa4(y}!*Yt}}5_-uzbX6)b< zK)jczY#!7X{~YIY@(C21$?*nfXJ3kl$k4=r!ow$!gM*hQCeS%BR`R8^$53DVKRtgV zc-J94>~p$w2^PL9#*z3wJUo+}ocv@h(ACvLobPTtM;XT>@UQsWA8P)3^&cs+H#2LF z*w0u^3`!Y8Ow43FPbYtWti>ixq^|$a^m}xuhWsl$=Rh|(Ry7mo0Eqv@*(ZQjUAQA> zp!?Y$-^8)PQ$F8kB;Aoh7%pIJ1QfTOS zVtGeLPm+AJu&@l_Juq+_+1YtPf1qcW$7ySJ-oroZ5$6+@9lZ>?e+ca*pZ*O0NpF!4 z_&N>j2RLTI&sf$c%kTL&KbLp*o_)m5ahxxDV!pJm>ik#+yI%PIHwfnf0XaArV&i_eFyX+)#=seUh(2D$VKfo?P_;1n9 z4SQc_iSzM0Yp2f0iDX-Slfu$+tL0yrf4m0dZaqVNqQ;StQ6tfHj)U|5jr(k1(ulf8 zB~d=|Zy9&y&@%c!LhaQ2}{sV zBgrFSol(Zm{*(OOSJm;xN@A}H|NPB)50SU?ynyd{jo|O2)Twyo=NzAWX4@r=|H0a+ z3o$(&i%}zUiKk?~bVhjOyhwgt&OgUJZgb9yN7zV8U2#I{B$w?`B39vzPT;;!Lh>J z9hYfv!3NFzc|=#a*TejRmUfy&|lo$&l1a9dZ;0Qxiv=cuFV@y~ffa;y0p zu^s3UJqr8dQ;02Jl~`}_5$F@=?^dDX%s;>5Iwh#fGUptEtW6(Nw}@oX751}O*>|O( zbN{K<7tnSK7^3V6IR};3C+4xbVuzQDfm_^PTQ@%%nDesaQSjXN2pdfe8mjjIZ8~{E zH#bx6_RGSLqEFhgR(`AX<$I}{2Yne*unGCX-Q~L)n>NKb#?aX1$K<>BpRqyorQDD7 zU+}XKumv8Z@V5yAc#X!qZA$%y&!LIy&j}(jmr+BrW*U78Ni9%+2gsD?q4F)1JOH-q zoS(q&Sng~Cf>LrdI>9;Gv6I)R`-i^t9y}d1Ex3PPOV0zLKm(wrr6(F30DfnkaSt7# z(Wm5+ov`s9__pdi8MnUiG;!mHq7%#`qX79D`Q2Qrm4*4E7%y8Ei+ zTPR~tuB_`xMK6P25AiMX`r;XrCVgaW{F36PuEjdjO_4e0*%t#Xf!ZjB=wtxEdFhC! zSxC+V+hoq|iJ!KPOv?4CLBq!6HzZ4ApP2ugcP^1{;sop;OhxB74~XYfCiIc>leFy1 zWzYx*m}YGk&L8*y;I#v}SaGtR^S2+h_ye_lhMuvF>JmH%V>>SB=oc;f5w>ZZKQwC5 ze%KeZ!4-M_ip+mTpL+m%pf1Z(=$!TG2i}+2xOm}z1~v#kDN7H-u8tEq^E&w{X$0Y}e|1ZMI9=fF7+hN^N6Z-e2{=IC#$RJKz$)|0_`!`hsq&Ep73Z&k(<|&UErm5Iy2~!Fv|F z9-)E}3-`);#5%_LC0rM6egilsi~Fe0!uzK}K=i4VLpS7ut%fbVAaj%F2Co~@+0Ro; z{BIQN66*3Efc2%e>9zQ7sa4%;A~gh`?+8QAmoh#8MHTk?5%J>uLJuB#6` zX@&PD<@%&6`E`7*D3>|sy6pgXfAS=kW?(KCU^~w8W_%6g0Or8|g~GbQ_UZF-^v3l` O)EBVzclrM;f&T$Do?rj~ literal 0 HcmV?d00001 diff --git a/website/app/static/images/background.svg b/website/app/static/images/background.svg new file mode 100644 index 0000000..489ca9f --- /dev/null +++ b/website/app/static/images/background.svg @@ -0,0 +1,3997 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/app/static/images/gras.svg b/website/app/static/images/gras.svg new file mode 100644 index 0000000..34d9703 --- /dev/null +++ b/website/app/static/images/gras.svg @@ -0,0 +1,10272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/app/templates/index.html b/website/app/templates/index.html new file mode 100644 index 0000000..a67442f --- /dev/null +++ b/website/app/templates/index.html @@ -0,0 +1,78 @@ + + + + + + {% block title %}Birdstalker{% endblock %} + + + + + + + + + + + + + {% for camera in config%} + {% if camera.website.picture %} +
+

{{ camera.name }}

+ + + {% if camera.website.timelapse and camera.timelapse %} +

Gestern:

+ + {% endif %} +
+ {% endif %} + {%endfor%} + + + + + \ No newline at end of file diff --git a/website/dockerfile b/website/dockerfile new file mode 100644 index 0000000..a645861 --- /dev/null +++ b/website/dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.10 + + +COPY app/ . + +#COPY requirements.txt requirements.txt +#RUN apt-get update && apt-get install ffmpeg -y +RUN pip3 install -r requirements.txt + +CMD ["python", "app.py"] +#CMD ["gunicorn" , "-b", "0.0.0.0:8000" , "app:app"] \ No newline at end of file