From 50ab23e033d849b51fd225195459cc3ea63a0dca Mon Sep 17 00:00:00 2001 From: kirklholub Date: Wed, 26 Jul 2023 18:40:36 +0000 Subject: [PATCH] email work around and cwd stuff --- backends/email.py | 20 +++ requirements.txt | 61 ++++++---- requirements.txt.orig | 46 +++++++ sites/management/commands/diagrams_test.py | 9 ++ sites/management/commands/send_email_test.py | 17 +++ sites/management/commands/web_service | 10 ++ sites/models.py | 43 +++---- sites/views.py | 74 ++++------- ssop/settings.py | 12 +- templates/admin/base.html | 122 +++++++++++++++++++ templates/critical_weather_day.html | 27 ++-- upgrade_pip.py | 40 ++++++ uploads/ncocwd.txt | 8 +- uploads/ssopuid_35.txt | 1 + uploads/ssopuid_36.txt | 1 + uploads/ssopuid_38.txt | 1 + 16 files changed, 369 insertions(+), 123 deletions(-) create mode 100755 backends/email.py create mode 100755 requirements.txt.orig create mode 100755 sites/management/commands/diagrams_test.py create mode 100755 sites/management/commands/send_email_test.py create mode 100755 sites/management/commands/web_service create mode 100755 templates/admin/base.html create mode 100755 upgrade_pip.py create mode 100755 uploads/ssopuid_35.txt create mode 100755 uploads/ssopuid_36.txt create mode 100755 uploads/ssopuid_38.txt diff --git a/backends/email.py b/backends/email.py new file mode 100755 index 0000000..8b7dcdf --- /dev/null +++ b/backends/email.py @@ -0,0 +1,20 @@ +# https://medium.com/@hhuysamen/sending-emails-using-django-4-2-3e5de1ff64dc + +import ssl + +from django.core.mail.backends.smtp import EmailBackend as SMTPBackend +from django.utils.functional import cached_property + + +class EmailBackend(SMTPBackend): + @cached_property + def ssl_context(self): + if self.ssl_certfile or self.ssl_keyfile: + ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT) + ssl_context.load_cert_chain(self.ssl_certfile, self.ssl_keyfile) + return ssl_context + else: + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + return ssl_context diff --git a/requirements.txt b/requirements.txt index f46d0cf..579b833 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,46 +1,59 @@ -asgiref==3.5.2 +aenum==3.1.15 +asgiref==3.7.2 backports.zoneinfo==0.2.1 -boto3==1.26.79 -botocore==1.29.79 -certifi==2022.12.7 +beautifulsoup4==4.12.2 +boto3==1.26.163 +botocore==1.29.163 +bs4==0.0.1 +certifi==2023.5.7 cffi==1.15.1 -charset-normalizer==2.1.1 -contourpy==1.0.6 -cryptography==40.0.1 +charset-normalizer==3.1.0 +contourpy==1.1.0 +cryptography==41.0.1 cycler==0.11.0 +diagrams==0.23.3 Django==4.2.2 -django-extensions==3.2.1 -fonttools==4.38.0 +django-extensions==3.2.3 +fonttools==4.40.0 +graphviz==0.20.1 gunicorn==20.1.0 idna==3.4 +importlib-resources==5.12.0 isodate==0.6.1 jdcal==1.4.1 +Jinja2==3.1.2 jmespath==1.0.1 kiwisolver==1.4.4 lxml==4.9.2 -matplotlib==3.6.2 -mysql-connector-python==8.0.32 -mysqlclient==2.1.1 -networkx==2.8.8 -numpy==1.23.5 +MarkupSafe==2.1.3 +matplotlib==3.7.1 +mysql-connector-python==8.0.33 +mysqlclient==2.2.0 +networkx==3.1 +numpy==1.25.0 oauthlib==3.2.2 -onelogin==3.1.0 -packaging==21.3 -Pillow==9.3.0 +onelogin==3.1.5 +packaging==23.1 +Pillow==9.5.0 protobuf==3.20.3 -pyclean==2.2.0 +pyclean==2.7.3 pycparser==2.21 +pydantic==1.10.9 pydot==1.4.2 -PyJWT==2.6.0 -pyOpenSSL==23.1.1 -pyparsing==3.0.9 +PyJWT==2.7.0 +pyOpenSSL==23.2.0 +pyparsing==3.1.0 python-dateutil==2.8.2 python3-saml==1.15.0 -pytz==2022.6 +pytz==2023.3 requests==2.31.0 requests-oauthlib==1.3.1 -s3transfer==0.6.0 +s3transfer==0.6.1 six==1.16.0 +soupsieve==2.4.1 sqlparse==0.4.4 -urllib3==1.26.12 +typed-ast==1.5.4 +typing_extensions==4.6.3 +urllib3==1.26.16 xmlsec==1.3.13 +zipp==3.15.0 diff --git a/requirements.txt.orig b/requirements.txt.orig new file mode 100755 index 0000000..f46d0cf --- /dev/null +++ b/requirements.txt.orig @@ -0,0 +1,46 @@ +asgiref==3.5.2 +backports.zoneinfo==0.2.1 +boto3==1.26.79 +botocore==1.29.79 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==2.1.1 +contourpy==1.0.6 +cryptography==40.0.1 +cycler==0.11.0 +Django==4.2.2 +django-extensions==3.2.1 +fonttools==4.38.0 +gunicorn==20.1.0 +idna==3.4 +isodate==0.6.1 +jdcal==1.4.1 +jmespath==1.0.1 +kiwisolver==1.4.4 +lxml==4.9.2 +matplotlib==3.6.2 +mysql-connector-python==8.0.32 +mysqlclient==2.1.1 +networkx==2.8.8 +numpy==1.23.5 +oauthlib==3.2.2 +onelogin==3.1.0 +packaging==21.3 +Pillow==9.3.0 +protobuf==3.20.3 +pyclean==2.2.0 +pycparser==2.21 +pydot==1.4.2 +PyJWT==2.6.0 +pyOpenSSL==23.1.1 +pyparsing==3.0.9 +python-dateutil==2.8.2 +python3-saml==1.15.0 +pytz==2022.6 +requests==2.31.0 +requests-oauthlib==1.3.1 +s3transfer==0.6.0 +six==1.16.0 +sqlparse==0.4.4 +urllib3==1.26.12 +xmlsec==1.3.13 diff --git a/sites/management/commands/diagrams_test.py b/sites/management/commands/diagrams_test.py new file mode 100755 index 0000000..0343734 --- /dev/null +++ b/sites/management/commands/diagrams_test.py @@ -0,0 +1,9 @@ +# diagram.py +from diagrams import Diagram +from diagrams.aws.compute import EC2 +from diagrams.aws.database import RDS +from diagrams.aws.network import ELB + +with Diagram("Web Service", show=False): + ELB("lb") >> EC2("web") >> RDS("userdb") + diff --git a/sites/management/commands/send_email_test.py b/sites/management/commands/send_email_test.py new file mode 100755 index 0000000..1497c87 --- /dev/null +++ b/sites/management/commands/send_email_test.py @@ -0,0 +1,17 @@ +# https://stackoverflow.com/questions/19475955/using-django-models-in-external-python-script +from django.core.management.base import BaseCommand +from django.core.mail import send_mail + +class Command(BaseCommand): + help = "Test send_email" + + def handle(self, *args, **options): + subject = "test from webstage8" + fromaddr = "noreply.gsl@noaa.gov" + toaddr = ["kirk.l.holub@noaa.gov"] + body = "this worked form gsl-webstage8" + try: + send_mail(subject, body, fromaddr, toaddr, fail_silently=False) + except Exception as e: + print("Exception: " + str(e)) + diff --git a/sites/management/commands/web_service b/sites/management/commands/web_service new file mode 100755 index 0000000..053ae43 --- /dev/null +++ b/sites/management/commands/web_service @@ -0,0 +1,10 @@ +digraph "Web Service" { + graph [fontcolor="#2D3436" fontname="Sans-Serif" fontsize=15 label="Web Service" nodesep=0.60 pad=2.0 rankdir=LR ranksep=0.75 splines=ortho] + node [fixedsize=true fontcolor="#2D3436" fontname="Sans-Serif" fontsize=13 height=1.4 imagescale=true labelloc=b shape=box style=rounded width=1.4] + edge [color="#7B8894"] + aa87ab6ec1a5404f8a7dfbf745fd8a60 [label=lb height=1.9 image="/opt/ssop/venv/lib64/python3.9/site-packages/resources/aws/network/elastic-load-balancing.png" shape=none] + "9c33105ee3a44262b91f00922e59e7a4" [label=web height=1.9 image="/opt/ssop/venv/lib64/python3.9/site-packages/resources/aws/compute/ec2.png" shape=none] + aa87ab6ec1a5404f8a7dfbf745fd8a60 -> "9c33105ee3a44262b91f00922e59e7a4" [dir=forward fontcolor="#2D3436" fontname="Sans-Serif" fontsize=13] + "48053bbab0ac4d80a16c6279e0859504" [label=userdb height=1.9 image="/opt/ssop/venv/lib64/python3.9/site-packages/resources/aws/database/rds.png" shape=none] + "9c33105ee3a44262b91f00922e59e7a4" -> "48053bbab0ac4d80a16c6279e0859504" [dir=forward fontcolor="#2D3436" fontname="Sans-Serif" fontsize=13] +} diff --git a/sites/models.py b/sites/models.py index b5230f2..2c9dbfb 100755 --- a/sites/models.py +++ b/sites/models.py @@ -1247,28 +1247,14 @@ def is_user_a_sysad(**kwargs): oukeylist = [] keys = kwargs['request'].session['samlUserdata'].keys() - msg = " samlUserdata keys: " + str(keys) - logger.info(msg) for k in kwargs['request'].session['samlUserdata'].keys(): if str(k).startswith('ou'): oukeylist.append(str(k)) if len(oukeylist) > int(1): oukeylist.sort() - msg = " oukeylist: " + str(oukeylist) - logger.info(msg) oukeylist = ['ou0', 'ou1', 'ou2'] for k in oukeylist: - msg = " k = " + str(k) - logger.info(msg) orglist.append(kwargs['request'].session['samlUserdata'][str(k)][0]) - msg = " samlUserdata = " + str(kwargs['request'].session['samlUserdata'][str(k)][0]) - logger.info(msg) - msg = " user: " + str(user) - logger.info(msg) - msg = " homeorg: " + str(homeorg) - logger.info(msg) - msg = " orglist: " + str(orglist) - logger.info(msg) get_or_add_sysadmin(user, homeorg, orglist) class Sysadmin(models.Model): @@ -1318,7 +1304,6 @@ def post_auth_user_has_authenticated(sender, **kwargs): is_user_a_sysad(**kwargs) user_has_authenticated_sendemail(**kwargs) - @receiver(user_login_failure) def post_auth_user_login_failure(sender, **kwargs): now = datetime.datetime.utcnow() @@ -1372,7 +1357,7 @@ def user_has_authenticated_sendemail(**kwargs): hashedtoken = hl.hexdigest() existingtoken = 'none' - fname = '/tmp/ssop_' + str(kwargs['user'].id) + '.txt' + fname = 'uploads/ssopuid_' + str(kwargs['user'].id) + '.txt' try: if os.path.exists(fname): fh = open(fname, 'r') @@ -1389,9 +1374,6 @@ def user_has_authenticated_sendemail(**kwargs): firstname = kwargs['user'].first_name ymdhms = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') subject = settings.USER_HAS_AUTHENTICATED_SUBJECT - #body = 'Hello ' + firstname + ',\nWe noticed you logged into SSOPSB at ' + str(ymdhms) + '.\n' - #body = body + 'If you did not login at this time, please immediately contact ' + str(settings.SSOP_ADMIN_EMAIL) - #body = body + '\n\nYou can also direct message @Kirk Holub on https://oar-gsl.slack.com' body = settings.USER_HAS_AUTHENTICATED_BODY body = body.replace( 'firstname', firstname ) body = body.replace( 'ymdhms', ymdhms ) @@ -1402,15 +1384,11 @@ def user_has_authenticated_sendemail(**kwargs): if settings.DEBUG: msg = "DEBUG -- running: send_mail(subject, body, fromaddr, toaddr, fail_silently=False)" msg = msg + ' -- toaddr: ' + str(toaddr) - logger.debug(msg) - send_mail(subject, body, fromaddr, toaddr, fail_silently=False) - else: - send_mail(subject, body, fromaddr, toaddr, fail_silently=False) - except SMTPException as e: - now = datetime.datetime.utcnow() - msg = str(now) + ":User has logged in email failed:" + str(email) + ":post_auth_user_has_authenticated:" + str(e) - logger.info(msg) - except ConnectionRefusedError as e: + msg = msg + ' -- fromaddr: ' + str(fromaddr) + msg = msg + ' -- subject: ' + str(subject) + logger.info(msg) + send_mail(subject, body, fromaddr, toaddr, fail_silently=False) + except Exception as e: now = datetime.datetime.utcnow() msg = str(now) + ":User has logged in email failed:" + str(email) + ":post_auth_user_has_authenticated:" + str(e) logger.info(msg) @@ -1430,3 +1408,12 @@ def fullname(self): def mode(self): return self.current_state + + def colors(self): + if 'normal' in str(self.mode()).lower(): + # light green used by NCO + colors = ('black', '#e7f4e4') + else: + colors = ('white', 'red') + return colors + diff --git a/sites/views.py b/sites/views.py index 53e8f71..aefd797 100755 --- a/sites/views.py +++ b/sites/views.py @@ -541,13 +541,10 @@ def fetch_cwd_page(): if not os.path.exists(settings.CWD_PREV): response = get_and_save_cwd_page() - # Assume page only refreshed at synoptic cadence of four per day oldtime = os.path.getmtime(settings.CWD_PREV) utcnow = datetime.datetime.utcnow().strftime("%s") timetoreload = int(utcnow) - int(oldtime) - msg = " timetoreload: " + str(timetoreload) - logger.info(msg) - if timetoreload > int(6 * 3600): + if timetoreload > int(settings.CWD_FETCH_INTERVAL): response = get_and_save_cwd_page() else: fh = open(settings.CWD_PREV, 'r') @@ -557,23 +554,20 @@ def fetch_cwd_page(): def get_cwd(request): now = datetime.datetime.now() - now = now.replace(tzinfo=pytz.UTC).strftime('%H%MZ %a %b %d %Y') + now = now.replace(tzinfo=pytz.timezone('US/Mountain')).strftime('%H%M %a %b %d %Y US/Mountain') cwd_response = fetch_cwd_page() soup = BeautifulSoup(cwd_response, "html.parser") cwd_page = soup.select_one('div#home_page_content') opt_normal = soup.find("div", {"class": "col-opt-normal"}) opt_critical = soup.find("div", {"class": "col-opt-critical"}) - msg = ' opt_critical: ' + str(opt_critical) - logger.info(msg) opt_cwd_header = soup.find("div", {"class": "opt-cwd-header"}) - msg = ' opt_cwd_header: ' + str(opt_cwd_header) - logger.info(msg) opt_cwd_text = soup.find("div", {"class": "text-opt-cwd-header"}) - msg = ' opt_cwd_text: ' + str(opt_cwd_text) - logger.info(msg) opt_cwd_text = str(opt_cwd_text).replace('

', '') opt_cwd_text = opt_cwd_text.replace('

', '' ) + opt_cwd_outlook = soup.find("div", {"class": "row-opt-e-di"}) + opt_cwd_outlook = str(opt_cwd_outlook).replace('
', '') + opt_cwd_outlook = opt_cwd_outlook.replace('
', '' ) normal = None if 'none' not in str(opt_critical).lower(): opt_status = 'Critical' @@ -618,24 +612,20 @@ def get_cwd(request): opt_cwd_header = str(opt_cwd_header).replace('

', '') opt_cwd_text = str(opt_cwd_text).replace('
', '', 10) normal = [] - normal.append((opt_status, textcolor, bgcolor, opt_cwd_text, str(now), opt_cwd_header)) + normal.append((opt_status, textcolor, bgcolor, opt_cwd_text, str(now), opt_cwd_header, opt_cwd_outlook)) rethtml = "It is now %s.
" % now rethtml = str(rethtml) + str(opt_critical) + "
" + str(opt_cwd_header) + "
" + str(opt_i) + "
" + opt_cwd_text + "
" + opt_reason + "
" + opt_start + "
" + opt_end - rethtml = rethtml + str(cwd_page) + "" + rethtml = rethtml + str(opt_cwd_outlook) + str(cwd_page) + "" #return HttpResponse(rethtml) rooms = [] for r in Room.objects.all(): - rooms.append((r.fullname(), r.mode())) - - msg = " get_cwd normal: " + str(normal) - logger.info(msg) + rcolors = r.colors() + rooms.append((r.fullname(), r.mode(), rcolors[0], rcolors[1])) attributes = [] - attributes.append((opt_status, textcolor, bgcolor, opt_cwd_text, opt_reason, opt_start, opt_end, str(now))) - msg = " get_cwd attributes: " + str(attributes) - logger.info(msg) + attributes.append((opt_status, textcolor, bgcolor, opt_cwd_text, opt_reason, opt_start, opt_end, str(now), opt_cwd_outlook)) return render(request, 'critical_weather_day.html', {'normal': normal, 'rooms': rooms, 'attributes': attributes}) #@ensure_csrf_cookie @@ -900,26 +890,6 @@ def at2uu(request, access_token = None): return render(request, 'at2uu.html', {'attributes': signedattributes}) - -# This is the Django view which produced the Data and Links above, plus this Source your currently are reading, using the Template below. -# Seems recusive in some way. -# -#def demopython(request, access_token = None): -# """ -# user has been authenticated by login.gov, so we can retrive their attributes via a JWT -# """ -# -# # return structure supporting a django template named demoapp.html -# data = {} -# data['request'] = str(request) -# if access_token is None: -# signedattributes = signedattributes.split(": ") -# signedattributes = signedattributes.replace("'", "") -# signedattributes = signedattributes.replace("'}", "") -# -# return render(request, 'signedattrs.html', {'attributes': signedattributes}) - - # This is the Django view which produced the Data and Links above, plus this Source your currently are reading, using the Template below. # Seems recusive in some way. @@ -2391,8 +2361,8 @@ def prepare_django_request(request): #@ensure_csrf_cookie def index(request): - msg = 'in index -- request = ' + str(request) - logger.info(msg) + #msg = 'in index -- request = ' + str(request) + #logger.info(msg) req = prepare_django_request(request) @@ -2405,8 +2375,8 @@ def index(request): #logger.info(msg) auth = noaaOneLogin_Saml2_Auth(req, custom_base_path=settings.SAML_FOLDER) - msg = ' auth: ' + str(auth) - logger.info(msg) + #msg = ' auth: ' + str(auth) + #logger.info(msg) errors = [] error_reason = None @@ -2415,14 +2385,14 @@ def index(request): paint_logout = False if 'sso' in req['get_data']: auth = noaaOneLogin_Saml2_Auth(req, custom_base_path=settings.SAML_FOLDER) - msg = ' SAML auth: ' + str(auth) - logger.info(msg) + #msg = ' SAML auth: ' + str(auth) + #logger.info(msg) login = auth.login() shortened = login[0:100] + '... ' + ' chars removed ...' + login[-15:] lenshortened = len(shortened) shortened = login[0:100] + '... ' + str(lenshortened) + ' chars removed ...' + login[-15:] - msg = ' sso login HttpResponseRedirect( ' + str(shortened) + ' )' - logger.info(msg) + #msg = ' sso login HttpResponseRedirect( ' + str(shortened) + ' )' + #logger.info(msg) return HttpResponseRedirect(login) elif 'sso2' in req['get_data']: @@ -2446,12 +2416,12 @@ def index(request): if 'LogoutRequestID' in request.session: request_id = request.session['LogoutRequestID'] - msg = " in slo -- request_id = " + str(request_id) - logger.info(msg) + #msg = " in slo -- request_id = " + str(request_id) + #logger.info(msg) url = auth.logout(name_id=name_id, session_index=session_index, nq=name_id_nq, name_id_format=name_id_format, spnq=name_id_spnq) - msg = ' slo HttpResponseRedirect( ' + str(url) + ' )' - logger.info(msg) + #msg = ' slo HttpResponseRedirect( ' + str(url) + ' )' + #logger.info(msg) return HttpResponseRedirect(url) # If LogoutRequest ID need to be stored in order to later validate it, do instead diff --git a/ssop/settings.py b/ssop/settings.py index 8638010..0cb3974 100755 --- a/ssop/settings.py +++ b/ssop/settings.py @@ -4,6 +4,7 @@ Generated by 'django-admin startproject' using Django 3.2.15. For more information on this file, see + https://docs.djangoproject.com/en/3.2/topics/settings/ For the full list of settings and their values, see @@ -136,6 +137,7 @@ def get_secret(key): EMAIL_USE_TLS = True SSOP_ADMIN_EMAIL = "ssopadmin.gsl@noaa.gov" + USER_HAS_AUTHENTICATED_SUBJECT = 'SSOPSB Login' body = 'Hello firstname,\nWe noticed you logged into SSOP sandbox admin at ymdhms.\n' body = body + 'If you did not login at this time, please contact ' + SSOP_ADMIN_EMAIL @@ -278,6 +280,8 @@ def get_secret(key): "django_contrib_auth.backends.ModelBackend" ] +EMAIL_BACKEND = 'backends.email.EmailBackend' + SESSION_ENGINE = 'django.contrib.sessions.backends.file' ROOT_URLCONF = 'ssop.urls' @@ -516,8 +520,10 @@ def get_secret(key): # CWD_PREV = "uploads/ncocwd.txt" CWD_URL = "https://www.nco.ncep.noaa.gov/status/cwd/" -PAGE_REFRESH_RATE = 30 +PAGE_REFRESH_RATE = "60" +# NCO likely only refreshes on synoptic times 0, 6, 12, 18 UTC. However, just in case, pull every hour +CWD_FETCH_INTERVAL = 3600 # -UPDATED = 1033 - +UPDATED = 1540 + diff --git a/templates/admin/base.html b/templates/admin/base.html new file mode 100755 index 0000000..f9bcc29 --- /dev/null +++ b/templates/admin/base.html @@ -0,0 +1,122 @@ +{% load i18n static %} +{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} + + +{% block title %}{% endblock %} + +{% block dark-mode-vars %} + + +{% endblock %} +{% if not is_popup and is_nav_sidebar_enabled %} + + +{% endif %} +{% block extrastyle %}{% endblock %} +{% if LANGUAGE_BIDI %}{% endif %} +{% block extrahead %}{% endblock %} +{% block responsive %} + + + {% if LANGUAGE_BIDI %}{% endif %} +{% endblock %} +{% block blockbots %}{% endblock %} + + + +{% translate 'Skip to main content' %} + +
+ + {% if not is_popup %} + + {% block header %} + + {% endblock %} + + {% block nav-breadcrumbs %} + + {% endblock %} + {% endif %} + +
+ {% if not is_popup and is_nav_sidebar_enabled %} + {% block nav-sidebar %} + {% include "admin/nav_sidebar.html" %} + {% endblock %} + {% endif %} +
+ {% block messages %} + {% if messages %} +
    {% for message in messages %} + {{ message|capfirst }} + {% endfor %}
+ {% endif %} + {% endblock messages %} + +
+ {% block pretitle %}{% endblock %} + {% block content_title %}{% if title %}

{{ title }}

{% endif %}{% endblock %} + {% block content_subtitle %}{% if subtitle %}

{{ subtitle }}

{% endif %}{% endblock %} + {% block content %} + {% block object-tools %}{% endblock %} + {{ content }} + {% endblock %} + {% block sidebar %}{% endblock %} +
+
+ + {% block footer %}{% endblock %} +
+
+
+ + + + + + + + + + + diff --git a/templates/critical_weather_day.html b/templates/critical_weather_day.html index 33fb4ed..c671a2b 100755 --- a/templates/critical_weather_day.html +++ b/templates/critical_weather_day.html @@ -16,20 +16,22 @@ Critical Weather Day Status {% if normal %} {% for attr in normal %} -
Status Dashboard
+
Status Dashboard
last modified: {{attr.4}}
Critical Weather Day Status

Status: {{attr.0}}

Reason: {{attr.3}} +

Outlook: {{attr.6}} +

Source: NCEP Central Operations Critical Weather Day Status (opens a new page)

{% endfor %} {% else %} {% if attributes %} {% for attr in attributes %} -
Status Dashboard
+
Status Dashboard
last modified: {{attr.7}}
@@ -38,6 +40,8 @@

Reason: {{attr.4}}

Start: {{attr.5}}

End: {{attr.6}} +

Outlook: {{attr.8}} +

Source: NCEP Central Operations Critical Weather Day Status (opens a new page)

{% endfor %} @@ -45,16 +49,15 @@ {% endif %} {% if rooms %} - Data Center Fire Detection / Suppression Status - {% for room in rooms %} -
-
    -
  1. - -- {{room.1}} -
  2. -
-
- {% endfor %} +
+
+ Data Center Fire Detection / Supression System Status + {% for room in rooms %} +

* {{room.0}} -- + {{room.1}} + {% endfor %} +

+
{% endif %}
diff --git a/upgrade_pip.py b/upgrade_pip.py new file mode 100755 index 0000000..7963fd3 --- /dev/null +++ b/upgrade_pip.py @@ -0,0 +1,40 @@ +import pkg_resources +import subprocess +import sys +from ssop import settings + +def run(cmdl, execute): + """ + prints cmdl or passes it to subprocess.run if execute is True + returns str + """ + cmd = " ".join(cmdl) + if execute: + print(" running: " + cmd) + result = subprocess.run(cmdl, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + status = str(result.stdout) + if result.returncode != 0: + print("Non-zero returncode: " + str(result.returncode)) + print(" result: " + str(result)) + print(" status: " + str(status)) + sys.exit(-1) + else: + print(" cmd: " + cmd) + status = 'SUCCESS' + return status + + +def basecmdl(pkgname): + """ + returns a basic commands suitable for subprocess.run() + """ + cmdl = ["pip3", "install", "--proxy", settings.HTTP_PROXY, "--upgrade", pkgname] + return cmdl + + +for dist in pkg_resources.working_set: + dist = str(dist) + print(str(dist)) + packagename = str(dist).split()[0] + cmdlist = basecmdl(packagename) + runstatus = run(cmdlist, True) diff --git a/uploads/ncocwd.txt b/uploads/ncocwd.txt index 1ba5b52..e88829b 100755 --- a/uploads/ncocwd.txt +++ b/uploads/ncocwd.txt @@ -284,18 +284,18 @@
Outlook -
Updated 1620Z Wed Jun 21 2023
+
Updated 0844Z Wed Jul 12 2023
-
12Z Wed Jun 21 - 12Z Thu Jun 22
12Z Thu Jun 22 - 12Z Fri Jun 23
12Z Fri Jun 23 - 12Z Sat Jun 24
+
12Z Wed Jul 12 - 12Z Thu Jul 13
12Z Thu Jul 13 - 12Z Fri Jul 14
12Z Fri Jul 14 - 12Z Sat Jul 15
Normal
Normal
Normal
-Critical Weather Day is not expected in the next 3 days. +Critical Weather Day is not scheduled for the next 3 days. However, NCEP will be monitoring the need for CWD with the threat of severe weather over the Middle Mississippi Valley on Wednesday.
Hazards
@@ -475,7 +475,7 @@ Critical Weather Day is not expected in the next 3 days.
- Page loaded: 17:10 UTC   |   10:10 am Pacific   |   11:10 am Mountain   |   12:10 pm Central   |   1:10 pm Eastern
+ Page loaded: 17:12 UTC   |   10:12 am Pacific   |   11:12 am Mountain   |   12:12 pm Central   |   1:12 pm Eastern diff --git a/uploads/ssopuid_35.txt b/uploads/ssopuid_35.txt new file mode 100755 index 0000000..2bbe1a2 --- /dev/null +++ b/uploads/ssopuid_35.txt @@ -0,0 +1 @@ +14023d654fc63a0dbfa204f57efffa0b89d722bef639a422a5f4220a35e572ac \ No newline at end of file diff --git a/uploads/ssopuid_36.txt b/uploads/ssopuid_36.txt new file mode 100755 index 0000000..1b0e742 --- /dev/null +++ b/uploads/ssopuid_36.txt @@ -0,0 +1 @@ +378008ffd244c88ca5162e93b6675a511ce3308217cc8a591f69efdfaf9c293f \ No newline at end of file diff --git a/uploads/ssopuid_38.txt b/uploads/ssopuid_38.txt new file mode 100755 index 0000000..c2275a6 --- /dev/null +++ b/uploads/ssopuid_38.txt @@ -0,0 +1 @@ +cf80d507082ce592490b6474f15cd95736696f9371e343a3d8f02fcba1a10191 \ No newline at end of file