commit 4d238c2972a9f9cd25d7de6e24c7b1e505ab07ec Author: Tony Marsella Date: Thu Jun 29 16:32:46 2023 -0700 Beta launch diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000..2e81544 Binary files /dev/null and b/__pycache__/app.cpython-310.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..819f0b1 --- /dev/null +++ b/app.py @@ -0,0 +1,108 @@ +from flask import Flask, render_template, request, jsonify +import xml.etree.ElementTree as ET +import logging + +app = Flask(__name__) + +# Configure logging +logging.basicConfig(level=logging.ERROR) + +# Parse the XML file and extract data +def parse_xml(file_path): + try: + tree = ET.parse(file_path) + root = tree.getroot() + + items = [] + run_time_element = root.find("run_time") + + if run_time_element is not None: + run_time = run_time_element.text + else: + run_time = 'N/A' + + for item in root.findall('.//item'): + title_element = item.attrib.get('title') + title = title_element if title_element is not None else 'N/A' + + link_element = item.attrib.get('link') + link = link_element if link_element is not None else '#' + + description_element = item.attrib.get('description') + description = description_element if description_element is not None else 'N/A' + + source_element = item.attrib.get('source') + source = source_element if source_element is not None else 'N/A' + + guid_element = item.attrib.get('guid') + guid = guid_element if guid_element is not None else 'N/A' + + pub_date_element = item.attrib.get('pubDate') + pub_date = pub_date_element if pub_date_element is not None else 'N/A' + + georss_point_element = item.attrib.get('{http://www.georss.org/georss}point') + georss_point = georss_point_element if georss_point_element is not None else 'N/A' + + items.append({ + 'title': title, + 'link': link, + 'description': description, + 'source': source, + 'guid': guid, + 'pub_date': pub_date, + 'georss_point': georss_point + }) + + return items, run_time # Return the extracted items and run time value + + except FileNotFoundError: + app.logger.error('XML file not found.') + + return [], '' + +def write_to_xml(file_path, items): + root = ET.Element("root") + + for item in items: + new_element = ET.SubElement(root, "item") + new_element.set("title", item['title']) + new_element.set("link", item['link']) + + tree = ET.ElementTree(root) + tree.write(file_path, encoding="utf-8", xml_declaration=True) + +@app.route('/') +def index(): + items, run_time = parse_xml('./indeed_output.xml') # Retrieve the items and run time + return render_template('index.html', items=items, run_time=run_time) # Pass the run time to the template + +@app.route('/search') +def search(): + query = request.args.get('query') + items, run_time = parse_xml('./indeed_output.xml') # Retrieve the items and run time + + # Filter the items based on the search query + filtered_items = [] + for item in items: + if query.lower() in item['title'].lower() or query.lower() in item['description'].lower(): + filtered_items.append(item) + + return render_template('index.html', items=filtered_items, run_time=run_time) # Pass the run time to the template + +@app.route('/report', methods=['POST']) +def report(): + title = request.form.get('title') + link = request.form.get('link') + + reported_items = list(parse_xml('./reported.xml')) + reported_items[0].append({'title': title, 'link': link}) # Append the item to the first element of the tuple + write_to_xml('./reported.xml', reported_items[0]) # Pass the first element of the tuple to write_to_xml + + response = { + 'message': 'Item reported successfully! Our team will review this listing to see if it is in line with our Statement of Faith. Appropriate filtering will be applied.' + } + return jsonify(response) + + +if __name__ == '__main__': + app.run() diff --git a/app_backup.py b/app_backup.py new file mode 100644 index 0000000..4e4e630 --- /dev/null +++ b/app_backup.py @@ -0,0 +1,108 @@ +from flask import Flask, render_template, request, jsonify +import xml.etree.ElementTree as ET +import logging + +app = Flask(__name__) + +# Configure logging +logging.basicConfig(level=logging.ERROR) + +# Parse the XML file and extract data +def parse_xml(file_path): + try: + tree = ET.parse(file_path) + root = tree.getroot() + + items = [] + run_time_element = root.find("run_time") + + if run_time_element is not None: + run_time = run_time_element.text + else: + run_time = 'N/A' + + for item in root.findall('.//item'): + title_element = item.attrib.get('title') + title = title_element if title_element is not None else 'N/A' + + link_element = item.attrib.get('link') + link = link_element if link_element is not None else '#' + + description_element = item.attrib.get('description') + description = description_element if description_element is not None else 'N/A' + + source_element = item.attrib.get('source') + source = source_element if source_element is not None else 'N/A' + + guid_element = item.attrib.get('guid') + guid = guid_element if guid_element is not None else 'N/A' + + pub_date_element = item.attrib.get('pubDate') + pub_date = pub_date_element if pub_date_element is not None else 'N/A' + + georss_point_element = item.attrib.get('{http://www.georss.org/georss}point') + georss_point = georss_point_element if georss_point_element is not None else 'N/A' + + items.append({ + 'title': title, + 'link': link, + 'description': description, + 'source': source, + 'guid': guid, + 'pub_date': pub_date, + 'georss_point': georss_point + }) + + return items, run_time # Return the extracted items and run time value + + except FileNotFoundError: + app.logger.error('XML file not found.') + + return [], '' + +def write_to_xml(file_path, items): + root = ET.Element("root") + + for item in items: + new_element = ET.SubElement(root, "item") + new_element.set("title", item['title']) + new_element.set("link", item['link']) + + tree = ET.ElementTree(root) + tree.write(file_path, encoding="utf-8", xml_declaration=True) + +@app.route('/') +def index(): + items, run_time = parse_xml('./indeed_output.xml') # Retrieve the items and run time + return render_template('index.html', items=items, run_time=run_time) # Pass the run time to the template + +@app.route('/search') +def search(): + query = request.args.get('query') + items, run_time = parse_xml('./indeed_output.xml') # Retrieve the items and run time + + # Filter the items based on the search query + filtered_items = [] + for item in items: + if query.lower() in item['title'].lower() or query.lower() in item['description'].lower(): + filtered_items.append(item) + + return render_template('index.html', items=filtered_items, run_time=run_time) # Pass the run time to the template + +@app.route('/report', methods=['POST']) +def report(): + title = request.form.get('title') + link = request.form.get('link') + + reported_items = parse_xml('./reported.xml') + reported_items.append({'title': title, 'link': link}) + write_to_xml('./reported.xml', reported_items) + + response = { + 'message': 'Item reported successfully! Our team will review this listing to see if it is in line with our Statement of Faith. Appropriate filtering will be applied.' + } + return jsonify(response) + + +if __name__ == '__main__': + app.run() diff --git a/gunicorn_config.py b/gunicorn_config.py new file mode 100644 index 0000000..c2a315f --- /dev/null +++ b/gunicorn_config.py @@ -0,0 +1,3 @@ +workers = 4 +bind = '0.0.0.0:8000' +module = 'app:app' \ No newline at end of file diff --git a/indeed_input.xml b/indeed_input.xml new file mode 100644 index 0000000..9d800a8 --- /dev/null +++ b/indeed_input.xml @@ -0,0 +1 @@ +it technology developer software bible christian jesus god .xml Jobs | Indeed.comhttps://www.indeed.com/jobs?q=(it+OR+technology+OR+developer+OR+software)+AND+(bible+OR+christian+OR+jesus+OR+god)+-LDS+-%22Latter-Day+Saints%22.xmlenCopyright (c) 2023 Indeed, Inc All rights reserved.Thu, 29 Jun 2023 21:32:50 GMThttps://www.indeed.com/images/indeed_rss.pngIndeed.com - one search. all jobs.https://www.indeed.com/Azure Full Stack Developer - RAKE Digital - Salem, VAhttps://www.indeed.com/viewjob?t=Azure+Full+Stack+Developer&c=RAKE+Digital&l=Salem,+VA&jk=0c032cb5f8eb4308&rtk=1h44h5fcni0ct800&from=rssRAKE Digital889a8ce29aa63896b5a3e20f5737f72eMon, 21 Sep 2020 21:33:20 GMTMicrosoft certifications for software development or cloud technologies. Bachelors degree in computer science or engineering. 4+ years of experience with . $70,000 - $90,000 a year<br>From RAKE Digital - Mon, 21 Sep 2020 21:33:20 GMT - View all <a href="https://www.indeed.com/jobs?l=Salem%2C+VA">Salem, VA jobs</a>37.29347 -80.05476Senior D365 - Power Platform Developer - The Christian Broadcasting Network - Virginia Beach, VAhttps://www.indeed.com/viewjob?t=Senior+D365+-+Power+Platform+Developer&c=The+Christian+Broadcasting+Network&l=Virginia+Beach,+VA&jk=646e19b3c9168b90&rtk=1h44h5fcni0ct800&from=rssIndeedc2d329c184ac30b9e947aac599e66414Mon, 19 Jun 2023 14:32:06 GMTWe are looking for an innovative, senior developer who will bring their extensive D365 experience to challenge the status quo while mentoring and influencing&#8230;<br>From Indeed - Mon, 19 Jun 2023 14:32:06 GMT - View all <a href="https://www.indeed.com/jobs?l=Virginia+Beach%2C+VA">Virginia Beach, VA jobs</a>36.85293 -75.97798Software Engineer - The Christian Broadcasting Network - Virginia Beach, VAhttps://www.indeed.com/viewjob?t=Software+Engineer&c=The+Christian+Broadcasting+Network&l=Virginia+Beach,+VA&jk=e62777e0d3ff7b2d&rtk=1h44h5fcni0ct800&from=rssThe Christian Broadcasting Network6970f9322b94a9a9cb17f93d8df1665dFri, 16 Jun 2023 15:10:51 GMTTechnical degree or equivalent job experience. Platform skills in one or more of the following areas are preferred: HTTP, HTTPS (SSL encryption), SOAP.<br>From The Christian Broadcasting Network - Fri, 16 Jun 2023 15:10:51 GMT - View all <a href="https://www.indeed.com/jobs?l=Virginia+Beach%2C+VA">Virginia Beach, VA jobs</a>36.801575 -76.17645Full Stack Developer - The Christian Broadcasting Network - Virginia Beach, VAhttps://www.indeed.com/viewjob?t=Full+Stack+Developer&c=The+Christian+Broadcasting+Network&l=Virginia+Beach,+VA&jk=70260b12c0ed0d6c&rtk=1h44h5fcni0ct800&from=rssThe Christian Broadcasting Network571180c82ab510bcbf46a332d3887226Tue, 20 Dec 2022 18:35:15 GMTForward thinker with desire to research, learn and implement new technologies. Strong 2D and 3D Illustration skills or fine art background are an advantage.<br>From The Christian Broadcasting Network - Tue, 20 Dec 2022 18:35:15 GMT - View all <a href="https://www.indeed.com/jobs?l=Virginia+Beach%2C+VA">Virginia Beach, VA jobs</a>36.801575 -76.17645React Developer - Global Evangelism Inc - San Antonio, TXhttps://www.indeed.com/viewjob?t=React+Developer&c=Global+Evangelism&l=San+Antonio,+TX&jk=f96c3f57c2f79376&rtk=1h44h5fcni0ct800&from=rssGlobal Evangelism Incdd35391fd68f0b278250214848deba9fWed, 19 Apr 2023 00:05:03 GMTLocation: On-Site or Remote. Diagnose bugs and/or performance issues recommending a solution and then implementing the solution if approved.<br>From Global Evangelism Inc - Wed, 19 Apr 2023 00:05:03 GMT - View all <a href="https://www.indeed.com/jobs?l=San+Antonio%2C+TX">San Antonio, TX jobs</a>29.665237 -98.5023Web Designer - The Crossing Church - Chesterfield, MOhttps://www.indeed.com/viewjob?t=Web+Designer&c=The+Crossing+Church&l=Chesterfield,+MO&jk=2ba373d18d21e7e5&rtk=1h44h5fcni0ct800&from=rssThe Crossing Churchcfd89a357df790d8a4ae5f6142ecc517Thu, 29 Jun 2023 01:38:13 GMTRegard the Bible as God&apos;s final authority in all areas of Christian life and desire to be wholly obedient to it. 3-5 years of web design experience.<br>From The Crossing Church - Thu, 29 Jun 2023 01:38:13 GMT - View all <a href="https://www.indeed.com/jobs?l=Chesterfield%2C+MO">Chesterfield, MO jobs</a>38.66311 -90.57707Software Developer - Indiana Wesleyan University - Marion, INhttps://www.indeed.com/viewjob?t=Software+Developer&c=Indiana+Wesleyan+University&l=Marion,+IN&jk=7b6b744d48462d7d&rtk=1h44h5fcni0ct800&from=rssIndiana Wesleyan Universitycb62374d7a67a62531fa23808e63def9Mon, 08 May 2023 17:57:56 GMTMicrosoft/Apple operating systems, Microsoft Office products, and a specialization in Ellucian Administrative software, report-writing tools or ASP.<br>From Indiana Wesleyan University - Mon, 08 May 2023 17:57:56 GMT - View all <a href="https://www.indeed.com/jobs?l=Marion%2C+IN">Marion, IN jobs</a>40.55837 -85.65914Enterprise Data Analyst - Moody Bible Institute - Chicago, ILhttps://www.indeed.com/viewjob?t=Enterprise+Data+Analyst&c=Moody+Bible+Institute&l=Chicago,+IL&jk=6eb3569ac0a03010&rtk=1h44h5fcni0ct800&from=rssMoody Bible Institute59eb222514a0292d718f55b524242bc0Mon, 15 May 2023 22:47:25 GMTDegree in Business Administration, Computer Science, Data Science, Information Technology, Information Management, Economics, or Statistics, or in a related&#8230;<br>From Moody Bible Institute - Mon, 15 May 2023 22:47:25 GMT - View all <a href="https://www.indeed.com/jobs?l=Chicago%2C+IL">Chicago, IL jobs</a>41.89899 -87.63645WEBMASTER - Association Of Christian Schools In - Remotehttps://www.indeed.com/viewjob?t=Webmaster&c=Association+Of+Christian+Schools+In&l=Remote&jk=5154045dbee288e3&rtk=1h44h5fcni0ct800&from=rssAssociation of Christian Schools International31888d1a5c1e16ac68723c939c2a72b0Thu, 22 Jun 2023 22:12:06 GMTBachelor&#8217;s degree in Computer Science or a related technology field. Ability and willingness to travel up to 5% by plane or automobile. $79,000 - $92,000 a year<br>From Association of Christian Schools International - Thu, 22 Jun 2023 22:12:06 GMT - View all <a href="https://www.indeed.com/jobs?l=Remote">Remote jobs</a>25.0 -40.0Senior ERP Developer - Pepperdine University - Remotehttps://www.indeed.com/viewjob?t=Senior+Erp+Developer&c=Pepperdine+University&l=Remote&jk=9525f49b41a5541f&rtk=1h44h5fcni0ct800&from=rssPepperdine University774bf61c1f56f15478ca415ad42c5bd6Wed, 21 Jun 2023 17:13:35 GMTDesigns custom processes and components, or adapts third-party integrations or other packaged applications. The role may provide mentorship to junior developers&#8230; $85,000 - $100,000 a year<br>From Pepperdine University - Wed, 21 Jun 2023 17:13:35 GMT - View all <a href="https://www.indeed.com/jobs?l=Remote">Remote jobs</a>25.0 -40.0Full Stack Developer - The Christian Broadcasting Network - Remotehttps://www.indeed.com/viewjob?t=Full+Stack+Developer&c=The+Christian+Broadcasting+Network&l=Remote&jk=e749a4891be9c8ef&rtk=1h44h5fcni0ct800&from=rssIndeede158c0d3047cc17c6f3a8bcead14c369Fri, 03 Mar 2023 21:23:18 GMTForward thinker with desire to research, learn and implement new technologies. Strong 2D and 3D Illustration skills or fine art background are an advantage.<br>From Indeed - Fri, 03 Mar 2023 21:23:18 GMT - View all <a href="https://www.indeed.com/jobs?l=Remote">Remote jobs</a>25.0 -40.0 \ No newline at end of file diff --git a/indeed_output.xml b/indeed_output.xml new file mode 100644 index 0000000..57f478d --- /dev/null +++ b/indeed_output.xml @@ -0,0 +1,2 @@ + +2023-06-29 14:32:50 \ No newline at end of file diff --git a/reported.xml b/reported.xml new file mode 100644 index 0000000..bdde3fe --- /dev/null +++ b/reported.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/static/heartily1.gif b/static/heartily1.gif new file mode 100644 index 0000000..77295e5 Binary files /dev/null and b/static/heartily1.gif differ diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..23619f9 --- /dev/null +++ b/static/style.css @@ -0,0 +1,229 @@ +body { + background-color: #272822; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #272822; +} + +h1 { + font-size: 24px; + font-weight: bold; + margin-bottom: 10px; + color: #f8f8f2; +} + +ul { + list-style: none; + padding: 0; +} + +li { + margin-bottom: 20px; +} + +h2 { + font-size: 20px; + font-weight: bold; + color: #a9dc76; +} + +p { + color: #999999; +} + +a { + color: #78dce8; + text-decoration: none; +} + +a:hover { + color: #e6a96b; +} + +header { + display: flex; + justify-content: center; + align-items: center; +} + + +.logo { + flex-direction: row; +} + +.logo-img { + width: 50px; + max-width: 100%; + margin-right: 10px; + +} + +.logo p { + font-size: 24px; + color: #f8f8f2; + margin: 10px; +} +.logo-name { + font-size: 24px; + font-weight: bold; + color: #f8f8f2; + margin: 10px; +} +.logo-link { + display: inline-flex; + align-items: center; + text-decoration: none; /* Add this line to remove underline */ +} +.search-form { + display: flex; + align-items: center; + justify-content: center; +} + +.search-input { + padding: 10px; + border: none; + border-radius: 4px; + font-size: 16px; + background-color: #F9F5DD; + color: #75715e; +} + +.search-input::placeholder { + color: #75715e; +} + +.search-button { + padding: 10px 20px; + border: none; + border-radius: 4px; + font-size: 16px; + background-color: #fd971f; + color: #f8f8f2; + cursor: pointer; + margin:10px; +} + +.search-button:hover { + background-color: #f4bf75; +} + + +.notification-overlay { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background-color: #ff5795c7; + color: #f8f8f2; + border: 1px solid #272822; + padding: 10px 20px; + border-radius: 4px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); + animation: fadeIn 0.5s, fadeOut 0.5s 5s forwards; + display: none; + } + + @keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + @keyframes fadeOut { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + + + .report-button { + position: relative; + display: flex; + text-decoration: none; + padding: 10px 20px; + border-radius: 4px; + color: #FF5794; + border:none; + background-color: transparent; + margin: 5px; + +} +.wrapper { + position: relative; + display: inline-block; + text-decoration: none; + color: #f8f8f2; + background-color: #fd971f; + padding: 10px 20px; + border: none; + border-radius: 4px; + margin: 5px; +} + +.wrapper .button-text { + display: inline-block; + transition: opacity 0.3s ease; +} + +.wrapper .button-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #ffffff; + font-size: 24px; + opacity: 0; + transition: opacity 0.3s ease; +} +.wrapper:hover { + background-color: #f4bf75; +} +.wrapper:hover .button-text { + opacity: 0; +} + +.wrapper:hover .button-icon { + opacity: 1; + font-size: 20px; +} +.report-button .button-text { + display: inline-block; + transition: opacity 0.3s ease; + opacity: 0; +} + +.report-button .button-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 24px; + transition: opacity 0.3s ease; + font-size: 20px; + opacity: 1; +} + +.report-button:hover .button-text { + opacity: 1; +} + +.report-button:hover .button-icon { + opacity: 0; +} + +.list-container { + display: flex; + align-items: center; +} + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..6a06b68 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,125 @@ + + + + Heartily + + + + +
+ +
+ +
+
+ + + + Refresh + + +
+

Latest Jobs:

+

This list is updated hourly. Last update: {{ run_time }}

+ +
+ + + + +
+ +
+ + + + diff --git a/xmldownloader.py b/xmldownloader.py new file mode 100644 index 0000000..d0e307c --- /dev/null +++ b/xmldownloader.py @@ -0,0 +1,73 @@ +import xml.etree.ElementTree as ET +import urllib.request +import random +import time +from fake_useragent import UserAgent +import requests +import datetime + +# Get the current date and time +current_datetime = datetime.datetime.now() + +# Format the date and time as a string +run_time = current_datetime.strftime("%Y-%m-%d %H:%M:%S") + +# Print the run time +print("Script run time:", run_time) + +# Define the URL of the XML file to download +url = "https://rss.indeed.com/rss?q=(it+OR+technology+OR+developer+OR+software)+AND+(bible+OR+christian+OR+jesus+OR+god)+-LDS+-%22Latter-Day+Saints%22.xml" + +# Set a custom user agent +ua = UserAgent() +headers = { + 'User-Agent': ua.random, + 'Referer': 'https://google.com', + 'Accept-Language': 'en-US,en;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Upgrade-Insecure-Requests': '1', +} + +# Create a session object to handle cookies +session = requests.Session() +session.headers.update(headers) + +# Download the XML file +response = session.get(url) +filename = "/home/gordon/Documents/Code/heartily/indeed_input.xml" +with open(filename, 'wb') as f: + f.write(response.content) + +# Introduce a random delay between 2 and 5 seconds +delay = random.uniform(2, 5) +time.sleep(delay) + +# Read the downloaded XML file +tree = ET.parse(filename) +root = tree.getroot() + +# Define the desired attribute names +attribute_names = ["title", "link", "source", "guid", "pubDate", "description", "{http://www.georss.org/georss}point"] + +# Create a new XML root element to store the extracted values +new_root = ET.Element("root") + +# Iterate over the "item" elements and extract the desired values +for item in root.findall(".//item"): + new_element = ET.SubElement(new_root, "item") + + # Extract the desired attributes from the "item" element + for attribute_name in attribute_names: + value = item.find(attribute_name).text + if value is not None: + new_element.set(attribute_name, value) + +# Add a new element for the run time +run_time_element = ET.SubElement(new_root, "run_time") +run_time_element.text = run_time + +# Create an ElementTree object and write it to a new XML file +new_tree = ET.ElementTree(new_root) +output_filename = "/home/gordon/Documents/Code/heartily/indeed_output.xml" +new_tree.write(output_filename, encoding="utf-8", xml_declaration=True)