commit 295f9e9700b392e2f92b214bab0d8478e60b0e3a Author: Bryan Cerrati Date: Fri Nov 1 13:08:24 2024 -0400 Upload diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..a4a8199 Binary files /dev/null and b/.DS_Store differ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..672653b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Use the official Python image from the Docker Hub +FROM python:3.8-slim + +# Set the working directory +WORKDIR /app + +# Copy the current directory contents into the container +COPY . /app + +# Ensure instance directory exists and is writable +RUN mkdir -p /app/instance && chmod -R 777 /app/instance + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Make port 5000 available to the world outside this container +EXPOSE 5005 + +# Run app.py when the container launches +CMD ["python", "app.py"] diff --git a/__pycache__/forms.cpython-38.pyc b/__pycache__/forms.cpython-38.pyc new file mode 100644 index 0000000..58393fc Binary files /dev/null and b/__pycache__/forms.cpython-38.pyc differ diff --git a/__pycache__/models.cpython-38.pyc b/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000..aec96f3 Binary files /dev/null and b/__pycache__/models.cpython-38.pyc differ diff --git a/__pycache__/routes.cpython-38.pyc b/__pycache__/routes.cpython-38.pyc new file mode 100644 index 0000000..fe76925 Binary files /dev/null and b/__pycache__/routes.cpython-38.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..7e80595 --- /dev/null +++ b/app.py @@ -0,0 +1,44 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_wtf import CSRFProtect +from models import db, Company +import os +from routes import register_routes + +app = Flask(__name__) + +# Construct the full path for the SQLite database +database_path = os.path.join(app.root_path, 'instance', 'daily_overview.sqlite') + +# Ensure the instance directory exists +instance_path = os.path.join(app.root_path, 'instance') +if not os.path.exists(instance_path): + os.makedirs(instance_path) + +app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{database_path}' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +app.config['SECRET_KEY'] = 'your_secret_key' + +db.init_app(app) +csrf = CSRFProtect(app) + +print(f"Instance path: {instance_path}") +print(f"Database path: {database_path}") +print(f"Database URI: {app.config['SQLALCHEMY_DATABASE_URI']}") + +with app.app_context(): + try: + db.create_all() + if Company.query.count() == 0: + company1 = Company(name="Hartford Funding") + company2 = Company(name="Alpha Tech Lending") + db.session.add(company1) + db.session.add(company2) + db.session.commit() + except Exception as e: + print(f"Error creating database: {e}") + +register_routes(app) + +if __name__ == '__main__': + app.run(host='0.0.0.0', debug=True, port=5005) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..049e92b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + web: + build: . + ports: + - "5005:5005" + volumes: + - .:/app + environment: + FLASK_ENV: development \ No newline at end of file diff --git a/forms.py b/forms.py new file mode 100644 index 0000000..fc50456 --- /dev/null +++ b/forms.py @@ -0,0 +1,36 @@ +from flask_wtf import FlaskForm +from wtforms import SelectField, StringField, TextAreaField, SubmitField +from wtforms.validators import DataRequired +from models import Company + +class TicketForm(FlaskForm): + company = SelectField('Company', coerce=int, validators=[DataRequired()]) + name = StringField('Name', validators=[DataRequired()]) + subject = StringField('Subject', validators=[DataRequired()]) + ticket_number = StringField('Ticket Number', validators=[DataRequired()]) + status = SelectField('Status', choices=[('Open', 'Open'), ('Closed', 'Closed')], validators=[DataRequired()]) + submit = SubmitField('Submit') + +class NoteForm(FlaskForm): + note = TextAreaField('Note', validators=[DataRequired()]) + submit = SubmitField('Submit') + +class CompanyForm(FlaskForm): + name = StringField('Company Name', validators=[DataRequired()]) + submit = SubmitField('Add Company') + +class OnsiteForm(FlaskForm): + company = SelectField('Company', coerce=int, validators=[DataRequired()]) + day_of_week = SelectField('Day of the Week', choices=[ + ('Monday', 'Monday'), + ('Tuesday', 'Tuesday'), + ('Wednesday', 'Wednesday'), + ('Thursday', 'Thursday'), + ('Friday', 'Friday') + ], validators=[DataRequired()]) + note = TextAreaField('Note', validators=[DataRequired()]) + submit = SubmitField('Submit') + + def __init__(self, *args, **kwargs): + super(OnsiteForm, self).__init__(*args, **kwargs) + self.company.choices = [(company.id, company.name) for company in Company.query.all()] diff --git a/instance/.DS_Store b/instance/.DS_Store new file mode 100644 index 0000000..11a63e1 Binary files /dev/null and b/instance/.DS_Store differ diff --git a/models.py b/models.py new file mode 100644 index 0000000..ee1b4ab --- /dev/null +++ b/models.py @@ -0,0 +1,35 @@ +from flask_sqlalchemy import SQLAlchemy +from datetime import datetime + +db = SQLAlchemy() + +class Company(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50), nullable=False, unique=True) + tickets = db.relationship('Ticket', backref='company', lazy=True) + onsites = db.relationship('Onsite', backref='company', lazy=True) + +class Ticket(db.Model): + id = db.Column(db.Integer, primary_key=True) + company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=False) + name = db.Column(db.String(100), nullable=False) + subject = db.Column(db.String(100), nullable=False) + ticket_number = db.Column(db.String(100), nullable=False) + status = db.Column(db.String(10), nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + closed_at = db.Column(db.DateTime, nullable=True) + archived = db.Column(db.Boolean, default=False, nullable=False) + +class Note(db.Model): + id = db.Column(db.Integer, primary_key=True) + ticket_id = db.Column(db.Integer, db.ForeignKey('ticket.id'), nullable=False) + note = db.Column(db.Text, nullable=False) + archive = db.Column(db.Boolean, default=False, nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + ticket = db.relationship('Ticket', backref=db.backref('notes', lazy=True)) + +class Onsite(db.Model): + id = db.Column(db.Integer, primary_key=True) + company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=False) + day_of_week = db.Column(db.String(10), nullable=False) + note = db.Column(db.Text, nullable=False) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bb5cc46 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask>=1.0 +Flask-SQLAlchemy +Flask-WTF diff --git a/routes.py b/routes.py new file mode 100644 index 0000000..a76f44d --- /dev/null +++ b/routes.py @@ -0,0 +1,151 @@ +from flask import render_template, redirect, url_for, request, jsonify +from models import db, Ticket, Note, Company, Onsite +from forms import TicketForm, NoteForm, CompanyForm, OnsiteForm +from datetime import datetime, date + +def register_routes(app): + + @app.route('/') + def index(): + today = date.today() + open_tickets = Ticket.query.filter(Ticket.status == 'Open', Ticket.archived == False).all() + closed_tickets = Ticket.query.filter(Ticket.status == 'Closed', Ticket.archived == False).all() + onsites = {day: Onsite.query.filter_by(day_of_week=day).all() for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']} + return render_template('index.html', open_tickets=open_tickets, closed_tickets=closed_tickets, onsites=onsites) + + @app.route('/overview') + def overview(): + today = date.today() + open_tickets = Ticket.query.filter(Ticket.status == 'Open', Ticket.archived == False).all() + closed_tickets = Ticket.query.filter(Ticket.status == 'Closed', Ticket.archived == False).all() + onsites = {day: Onsite.query.filter_by(day_of_week=day).all() for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']} + return render_template('overview.html', open_tickets=open_tickets, closed_tickets=closed_tickets, onsites=onsites) + + @app.route('/add_ticket', methods=['GET', 'POST']) + def add_ticket(): + form = TicketForm() + form.company.choices = [(company.id, company.name) for company in Company.query.all()] + if form.validate_on_submit(): + new_ticket = Ticket( + company_id=form.company.data, + name=form.name.data, + subject=form.subject.data, + ticket_number=form.ticket_number.data, + status=form.status.data, + created_at=datetime.utcnow() + ) + db.session.add(new_ticket) + db.session.commit() + return redirect(url_for('index')) + return render_template('add_ticket.html', form=form) + + @app.route('/add_note/', methods=['GET', 'POST']) + def add_note(ticket_id): + form = NoteForm() + ticket = Ticket.query.get(ticket_id) + if form.validate_on_submit(): + new_note = Note(ticket_id=ticket_id, note=form.note.data, created_at=datetime.utcnow()) + db.session.add(new_note) + db.session.commit() + return redirect(url_for('index')) + return render_template('add_note.html', form=form, ticket_id=ticket_id, ticket=ticket) + + @app.route('/close_ticket/', methods=['GET']) + def close_ticket(ticket_id): + ticket = Ticket.query.get(ticket_id) + if ticket: + ticket.status = 'Closed' + ticket.closed_at = datetime.utcnow() + db.session.commit() + return redirect(url_for('index')) + + @app.route('/archive_ticket/', methods=['GET']) + def archive_ticket(ticket_id): + ticket = Ticket.query.get(ticket_id) + if ticket: + ticket.archived = True + db.session.commit() + return redirect(url_for('index')) + + @app.route('/archive_note/', methods=['GET']) + def archive_note(note_id): + note = Note.query.get(note_id) + if note: + note.archive = True + db.session.commit() + return redirect(url_for('index')) + + @app.route('/delete_ticket/', methods=['GET']) + def delete_ticket(ticket_id): + ticket = Ticket.query.get(ticket_id) + if ticket: + db.session.delete(ticket) + db.session.commit() + return redirect(url_for('all_tickets')) + + @app.route('/all_tickets') + def all_tickets(): + closed_tickets = Ticket.query.filter_by(status='Closed').all() + open_tickets = Ticket.query.filter_by(status='Open').all() + return render_template('all_tickets.html', closed_tickets=closed_tickets, open_tickets=open_tickets) + + @app.route('/companies', methods=['GET', 'POST']) + def companies(): + form = CompanyForm() + if form.validate_on_submit(): + new_company = Company(name=form.name.data) + db.session.add(new_company) + db.session.commit() + return redirect(url_for('companies')) + all_companies = Company.query.all() + return render_template('companies.html', form=form, companies=all_companies) + + @app.route('/add_onsite', methods=['GET', 'POST']) + def add_onsite(): + form = OnsiteForm() + if form.validate_on_submit(): + new_onsite = Onsite( + company_id=form.company.data, + day_of_week=form.day_of_week.data, + note=form.note.data + ) + db.session.add(new_onsite) + db.session.commit() + return redirect(url_for('index')) + return render_template('add_onsite.html', form=form) + + @app.route('/delete_onsite/', methods=['GET']) + def delete_onsite(onsite_id): + onsite = Onsite.query.get(onsite_id) + if onsite: + db.session.delete(onsite) + db.session.commit() + return redirect(url_for('index')) + + @app.route('/update_ticket/', methods=['POST']) + def update_ticket(ticket_id): + data = request.get_json() + ticket = Ticket.query.get(ticket_id) + if ticket: + if data['field'] == 'company_id': + company = Company.query.filter_by(name=data['value']).first() + if company: + ticket.company_id = company.id + elif data['field'] == 'name': + ticket.name = data['value'] + elif data['field'] == 'subject': + ticket.subject = data['value'] + elif data['field'] == 'ticket_number': + ticket.ticket_number = data['value'] + db.session.commit() + return jsonify(success=True) + return jsonify(success=False), 400 + + @app.route('/reopen_ticket/', methods=['GET']) + def reopen_ticket(ticket_id): + ticket = Ticket.query.get(ticket_id) + if ticket: + ticket.status = 'Open' + ticket.archived = False + db.session.commit() + return redirect(url_for('index')) diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..8e59343 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,30 @@ +body { + font-family: Arial, sans-serif; +} + +header, nav, main { + padding: 1em; +} + +header { + background-color: #f8f8f8; + border-bottom: 1px solid #ddd; +} + +nav ul { + list-style: none; + padding: 0; +} + +nav ul li { + display: inline; + margin-right: 10px; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 1.5em; +} diff --git a/templates/add_note.html b/templates/add_note.html new file mode 100644 index 0000000..54a2ac7 --- /dev/null +++ b/templates/add_note.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %}Add Note{% endblock %} + +{% block content %} +

Add Note to {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }}

+
+ {{ form.hidden_tag() }} + {{ form.note.label }} {{ form.note() }}
+ {{ form.submit() }} +
+{% endblock %} diff --git a/templates/add_onsite.html b/templates/add_onsite.html new file mode 100644 index 0000000..c3aabc8 --- /dev/null +++ b/templates/add_onsite.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block title %}Add Onsite{% endblock %} + +{% block content %} +

Add Onsite

+
+ {{ form.hidden_tag() }} +

+ {{ form.company.label }}
+ {{ form.company }} +

+

+ {{ form.day_of_week.label }}
+ {{ form.day_of_week }} +

+

+ {{ form.note.label }}
+ {{ form.note }} +

+

{{ form.submit }}

+
+{% endblock %} diff --git a/templates/add_ticket.html b/templates/add_ticket.html new file mode 100644 index 0000000..7630061 --- /dev/null +++ b/templates/add_ticket.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} + +{% block title %}Add Ticket{% endblock %} + +{% block content %} +

Add Ticket

+
+ {{ form.hidden_tag() }} +

+ {{ form.company.label }}
+ {{ form.company }} +

+

+ {{ form.name.label }}
+ {{ form.name }} +

+

+ {{ form.subject.label }}
+ {{ form.subject }} +

+

+ {{ form.ticket_number.label }}
+ {{ form.ticket_number }} +

+

+ {{ form.status.label }}
+ {{ form.status }} +

+

{{ form.submit }}

+
+{% endblock %} diff --git a/templates/all_tickets.html b/templates/all_tickets.html new file mode 100644 index 0000000..f0a1309 --- /dev/null +++ b/templates/all_tickets.html @@ -0,0 +1,85 @@ +{% extends "base.html" %} + +{% block title %}All Tickets{% endblock %} + +{% block content %} +

Closed Tickets

+
    + {% for ticket in closed_tickets %} +
  • + + {{ ticket.company.name }} + - + + {{ ticket.name }} + - + + {{ ticket.subject }} + - + + {{ ticket.ticket_number }} + + (Delete) + (Reopen) +
      + {% for note in ticket.notes %} + +
    • + {{ note.note }} - {{ note.created_at.strftime('%Y-%m-%d %H:%M:%S') }} +
    • + + {% endfor %} +
    +
  • + {% endfor %} +
+ +

Open Tickets

+
    + {% for ticket in open_tickets %} +
  • + + {{ ticket.company.name }} + - + + {{ ticket.name }} + - + + {{ ticket.subject }} + - + + {{ ticket.ticket_number }} + + (Delete) + (Close) +
      + {% for note in ticket.notes %} + +
    • + {{ note.note }} - {{ note.created_at.strftime('%Y-%m-%d %H:%M:%S') }} +
    • + + {% endfor %} +
    +
  • + {% endfor %} +
+ + +{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..c7bf49d --- /dev/null +++ b/templates/base.html @@ -0,0 +1,21 @@ + + + + + {% block title %}Daily Overview{% endblock %} + + +
+

Daily Overview

+ +
+
+ {% block content %}{% endblock %} +
+ + diff --git a/templates/companies.html b/templates/companies.html new file mode 100644 index 0000000..e235505 --- /dev/null +++ b/templates/companies.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block title %}Companies{% endblock %} + +{% block content %} +

Companies

+
    + {% for company in companies %} +
  • {{ company.name }}
  • + {% endfor %} +
+

Add a New Company

+
+ {{ form.hidden_tag() }} + {{ form.name.label }} {{ form.name() }}
+ {{ form.submit() }} +
+{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..87b4443 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,68 @@ +{% extends "base.html" %} + +{% block title %}Daily Overview - Home{% endblock %} + +{% block content %} + +

Open Tickets

+
    + {% for ticket in open_tickets %} +
  • + {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }} + (Close) +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • + X + {{ note.note }} +
    • + {% endif %} + {% endfor %} +
    + Add Note +

  • + {% endfor %} +
+

Closed Tickets

+
    + {% for ticket in closed_tickets %} +
  • + {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }} + (Archive) + (Reopen) +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • + {{ note.note }} +
    • + {% endif %} + {% endfor %} +
    + Add Note +

  • + {% endfor %} +
+

Onsite Schedule

+

+ Add Onsite +

+ {% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %} + {% if onsites[day]|length > 0 %} +

{{ day }}

+
    + {% for onsite in onsites[day] %} +
  • + X + {{ onsite.company.name }} - {{ onsite.note }} +
  • + {% endfor %} +
+ {% endif %} + {% endfor %} + +

+ Printable Overview +

+{% endblock %} diff --git a/templates/overview.html b/templates/overview.html new file mode 100644 index 0000000..e2d22b6 --- /dev/null +++ b/templates/overview.html @@ -0,0 +1,55 @@ +{% extends "base.html" %} + +{% block title %}Printable Overview{% endblock %} + +{% block content %} +

Open Tickets

+
    + {% for ticket in open_tickets %} +
  • + {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }} +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • + {{ note.note }} +
    • + {% endif %} + {% endfor %} +
    +
  • + {% endfor %} +
+ +

Closed Tickets

+
    + {% for ticket in closed_tickets %} +
  • + {{ ticket.company.name }} - {{ ticket.name }} - {{ ticket.subject }} - {{ ticket.ticket_number }} +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • + {{ note.note }} +
    • + {% endif %} + {% endfor %} +
    +
  • + {% endfor %} +
+ +

Onsite Schedule

+ {% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %} + {% if onsites[day]|length > 0 %} +

{{ day }}

+
    + {% for onsite in onsites[day] %} +
  • + {{ onsite.company.name }} - {{ onsite.note }} +
  • + {% endfor %} +
+ {% endif %} + {% endfor %} +{% endblock %} diff --git a/templates/v2_index.html b/templates/v2_index.html new file mode 100644 index 0000000..5d81358 --- /dev/null +++ b/templates/v2_index.html @@ -0,0 +1,85 @@ +{% extends "base.html" %} + +{% block title %}Daily Overview - Home{% endblock %} + +{% block content %} +
+

Open Tickets

+
    + {% for ticket in open_tickets %} +
  • +
    +
    + {{ ticket.company.name }} - + {{ ticket.name }} - + {{ ticket.subject }} - + {{ ticket.ticket_number }} +
    + Close +
    +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • + X + {{ note.note }} +
    • + {% endif %} + {% endfor %} +
    + Add Note +
  • + {% endfor %} +
+ +

Closed Tickets

+
    + {% for ticket in closed_tickets %} +
  • +
    +
    + {{ ticket.company.name }} - + {{ ticket.name }} - + {{ ticket.subject }} - + {{ ticket.ticket_number }} +
    +
    + Archive + Reopen +
    +
    +
      + {% for note in ticket.notes %} + {% if not note.archive %} +
    • {{ note.note }}
    • + {% endif %} + {% endfor %} +
    + Add Note +
  • + {% endfor %} +
+ +

Onsite Schedule

+

+ Add Onsite +

+ {% for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] %} + {% if onsites[day]|length > 0 %} +

{{ day }}

+
    + {% for onsite in onsites[day] %} +
  • + {{ onsite.company.name }} - {{ onsite.note }} + X +
  • + {% endfor %} +
+ {% endif %} + {% endfor %} + +

+ Printable Overview +

+
+{% endblock %}