diff --git a/.gitignore b/.gitignore index 54ba69a..1a0aa2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /__pycache__ +**/__pycache__ /errorlog.txt /instance /logs.log diff --git a/app/__init__.py b/app/__init__.py index 07fb623..7d5261f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,10 +32,15 @@ def create_app(): app.wsgi_app, x_for=1, x_proto=1 ) - from app.users.models import User + import app.users.models as user_models + import app.campaigns.models as campaign_models + import app.main.models as main_models + + + @login_manager.user_loader def load_user(user_id): - user = User.query.get(user_id) + user = user_models.User.query.get(user_id) if user: return user else: @@ -43,17 +48,22 @@ def create_app(): login_manager.login_view = 'users.login' from app.users import users + from app.users.users_api import users_api from app.main import main from app.campaigns import campaigns + from app.campaigns.campaign_api import campaign_api from app.admin import admin - # from app.agent_reports import agent_reports - # from app.status_reports import status_reports + from app.arba_yesodot import arba_yesodot + + campaigns.register_blueprint(campaign_api) + users.register_blueprint(users_api) + app.register_blueprint(users) app.register_blueprint(main) app.register_blueprint(campaigns) app.register_blueprint(admin) - # app.register_blueprint(agent_reports) - # app.register_blueprint(status_reports) + app.register_blueprint(arba_yesodot) + @app.route('/') def reroute_base_url(): diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 3bab473..0000000 Binary files a/app/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index ad90979..0000000 Binary files a/app/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/app/__pycache__/config.cpython-310.pyc b/app/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 0ea1947..0000000 Binary files a/app/__pycache__/config.cpython-310.pyc and /dev/null differ diff --git a/app/__pycache__/config.cpython-311.pyc b/app/__pycache__/config.cpython-311.pyc deleted file mode 100644 index 311e52c..0000000 Binary files a/app/__pycache__/config.cpython-311.pyc and /dev/null differ diff --git a/app/__pycache__/models.cpython-311.pyc b/app/__pycache__/models.cpython-311.pyc deleted file mode 100644 index 6f92904..0000000 Binary files a/app/__pycache__/models.cpython-311.pyc and /dev/null differ diff --git a/app/admin/__pycache__/__init__.cpython-310.pyc b/app/admin/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 9eadb63..0000000 Binary files a/app/admin/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/app/admin/__pycache__/routes.cpython-310.pyc b/app/admin/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index 4287806..0000000 Binary files a/app/admin/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/app/admin/routes.py b/app/admin/routes.py index d3b6308..85785d7 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -1,6 +1,8 @@ from app import db from app.admin import admin from app.users.models import User +from app.campaigns.models import Campaign +from app.main.models import Donation from app.users.forms import LoginForm, RegisterUserForm#, RequestResetForm, ResetPasswordForm, EditUserForm, AddUserForm from flask import render_template, redirect, url_for, flash, request from flask_login import login_required, login_user, current_user, logout_user @@ -10,4 +12,14 @@ import os @admin.route('administration') def administration(): - return render_template('administration.html') + users = User.query.all() + campaigns = Campaign.query.filter_by(archived=False).all() + return render_template('administration.html', + users=users, + campaigns=campaigns) + +@admin.route('archive') +def archive(): + archived_campaigns = Campaign.query.filter_by(archived=True).all() + return render_template('archive.html', + archived_campaigns=archived_campaigns) \ No newline at end of file diff --git a/app/admin/static/admin.js b/app/admin/static/admin.js new file mode 100644 index 0000000..086db81 --- /dev/null +++ b/app/admin/static/admin.js @@ -0,0 +1,98 @@ +const archiveButtons = document.getElementsByClassName('archive-button'); +const activityButtons = document.getElementsByClassName('change-activity-btn'); +const adminButtons = document.getElementsByClassName('change-admin-btn'); + + +function deactivateAdminButtons(){ + for (let i = 0; i < adminButtons.length; i++){ + adminButtons[i].disabled=true; + } +} +function activateAdminButtons(){ + for (let i = 0; i < adminButtons.length; i++){ + adminButtons[i].disabled=false; + } +} +function deactivateArchiveButtons(){ + for (let i = 0; i < archiveButtons.length; i++){ + archiveButtons[i].disabled=true; + } +} +function activateArchiveButtons(){ + for (let i = 0; i < archiveButtons.length; i++){ + archiveButtons[i].disabled=false; + } +} +function deactivateActiveStatusCheckboxes(){ + for (let i = 0; i < activityButtons.length; i++){ + activityButtons[i].disabled=true; + } +} +function activateActiveStatusCheckboxes(){ + for (let i = 0; i < activityButtons.length; i++){ + activityButtons[i].disabled=false; + } +} + +function activateAllButtons(){ + activateAdminButtons() + activateArchiveButtons() + activateActiveStatusCheckboxes() +} +function deactivateAllButtons(){ + deactivateAdminButtons() + deactivateArchiveButtons() + deactivateActiveStatusCheckboxes() +} + +async function updateActiveStatus(id, status) { + deactivateAllButtons() + var result = await fetch(`/campaigns/campaign_api/update_active_status/${id}/${status}`, {method:'PUT'}); + var data = await result.json(); + if (status === true){ + status = 'True' + } else { + status = 'False' + } + document.getElementById(`${id}-active-status`).innerText = status; + activateAllButtons() +} + +async function archiveCampaign(id){ + deactivateAllButtons() + var result = await fetch(`/campaigns/campaign_api/archive_campaign/${id}`, {method:'PUT'}); + var data = await result.json(); + document.getElementById(`${id}-row`).remove(); + activateAllButtons() +} + +async function updateAdminStatus(id, status) { + deactivateAllButtons() + var result = await fetch(`/users/users_api/update_admin_status/${id}/${status}`, {method:'PUT'}); + var data = await result.json(); + if (status === true){ + status = 'True' + } else { + status = 'False' + } + document.getElementById(`${id}-admin-status`).innerText = status; + activateAllButtons() +} + +document.addEventListener("DOMContentLoaded", (event) => { + for(let i = 0; i < activityButtons.length; i++){ + activityButtons[i].addEventListener('change', e => { + updateActiveStatus(parseInt(e.target.value), e.target.checked) + }) + } + for(let i = 0; i < archiveButtons.length; i++){ + archiveButtons[i].addEventListener('click', e => { + archiveCampaign(parseInt(e.target.value)) + }) + } + for(let i = 0; i < adminButtons.length; i++){ + adminButtons[i].addEventListener('change', e => { + updateAdminStatus(parseInt(e.target.value), e.target.checked) + }) + } + }); diff --git a/app/admin/static/administration.css b/app/admin/static/administration.css new file mode 100644 index 0000000..4cb7b81 --- /dev/null +++ b/app/admin/static/administration.css @@ -0,0 +1,56 @@ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} +.switch input { + opacity: 0; + width: 0; + height: 0; +} +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + + /* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} \ No newline at end of file diff --git a/app/admin/static/archive.js b/app/admin/static/archive.js new file mode 100644 index 0000000..2db5a59 --- /dev/null +++ b/app/admin/static/archive.js @@ -0,0 +1,31 @@ +const unArchiveButtons = document.getElementsByClassName('unarchive-button'); + + +function deactivateUnArchiveButtons(){ + for (let i = 0; i < unArchiveButtons.length; i++){ + unArchiveButtons[i].disabled=true; + } +} +function activateUnArchiveButtons(){ + for (let i = 0; i < unArchiveButtons.length; i++){ + unArchiveButtons[i].disabled=false; + } +} + + +async function unArchiveCampaign(id){ + deactivateUnArchiveButtons() + var result = await fetch(`/campaigns/campaign_api/un_archive_campaign/${id}`, {method:'PUT'}); + var data = await result.json(); + document.getElementById(`${id}-row`).remove(); + activateUnArchiveButtons() +} + + +document.addEventListener("DOMContentLoaded", (event) => { + for(let i = 0; i < unArchiveButtons.length; i++){ + unArchiveButtons[i].addEventListener('click', e => { + unArchiveCampaign(parseInt(e.target.value)) + }) + } + }); \ No newline at end of file diff --git a/app/admin/templates/administration.html b/app/admin/templates/administration.html index 6f73217..b37eb93 100644 --- a/app/admin/templates/administration.html +++ b/app/admin/templates/administration.html @@ -1,2 +1,90 @@ {% extends 'base.html' %} -{% block title %}Administration{% endblock title %} \ No newline at end of file +{% block title %}Administration{% endblock title %} +{% block stylesheet %} + +{% endblock stylesheet %} +{% block content %} +
+
+

Users

+ + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + {% endfor %} + +
#First NameLast NameEmailIs Admin?Change Admin Status
{{user.id}}{{user.first_name}}{{user.last_name}}{{user.email}}{% if user.user_type == 'Admin' %}True{%else%}False{%endif%} + +
+
+ +
+
+

Campaigns

+ New Campaign +
+ + + + + + + + + + + + {% for campaign in campaigns %} + + + + + + + + {% endfor %} + +
#TitleActiveChange Active StatusArchive?
{{campaign.id}}{{campaign.title}}{{campaign.active}} + +
+
+
+ +{% endblock content %} diff --git a/app/admin/templates/archive.html b/app/admin/templates/archive.html new file mode 100644 index 0000000..0395be5 --- /dev/null +++ b/app/admin/templates/archive.html @@ -0,0 +1,38 @@ +{% extends 'base.html' %} +{% block title %}Archive{% endblock title %} +{% block stylesheet %}{% endblock stylesheet %} +{% block content %} +
+
+

Archived Campaigns

+ + + + + + + + + + + + + {% for campaign in archived_campaigns %} + + + + + + + + {% endfor %} + +
TitleGoalRaisedCampaign LinkUnarchive?
{{campaign.title}}{{campaign.goal}}{{campaign.get_amount_raised()}}See campaign details +
+
+
+ +{% endblock content %} \ No newline at end of file diff --git a/app/arba_yesodot/__init__.py b/app/arba_yesodot/__init__.py new file mode 100644 index 0000000..5a2f80f --- /dev/null +++ b/app/arba_yesodot/__init__.py @@ -0,0 +1,9 @@ +from flask import Blueprint + +arba_yesodot = Blueprint('arba_yesodot', + __name__, + template_folder='templates', + static_folder='static', + url_prefix='/arba_yesodot') + +from app.arba_yesodot import routes \ No newline at end of file diff --git a/app/arba_yesodot/routes.py b/app/arba_yesodot/routes.py new file mode 100644 index 0000000..b677a47 --- /dev/null +++ b/app/arba_yesodot/routes.py @@ -0,0 +1,15 @@ +from app import db +from app.arba_yesodot import arba_yesodot +from app.users.models import User +from app.campaigns.models import Campaign +from app.campaigns.forms import CreateCampaignForm +from flask import render_template, redirect, url_for, flash, request +from flask_login import login_required, login_user, current_user, logout_user +from werkzeug.security import check_password_hash, generate_password_hash +from datetime import datetime +import os +from time import sleep + +@arba_yesodot.route('arba_yesodot_homepage') +def arba_yesodot_homepage(): + return render_template('arba_yesodot_homepage.html') \ No newline at end of file diff --git a/app/arba_yesodot/templates/arba_yesodot_homepage.html b/app/arba_yesodot/templates/arba_yesodot_homepage.html new file mode 100644 index 0000000..1ceb498 --- /dev/null +++ b/app/arba_yesodot/templates/arba_yesodot_homepage.html @@ -0,0 +1,6 @@ +{% extends 'base.html' %} +{% block title %}Arba Yesodot{% endblock title %} +{% block stylesheet %}{% endblock stylesheet %} +{% block content %} + +{% endblock content %} diff --git a/app/campaigns/__pycache__/__init__.cpython-310.pyc b/app/campaigns/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 6919d93..0000000 Binary files a/app/campaigns/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/app/campaigns/__pycache__/routes.cpython-310.pyc b/app/campaigns/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index 4293027..0000000 Binary files a/app/campaigns/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/app/campaigns/campaign_api/__init__.py b/app/campaigns/campaign_api/__init__.py new file mode 100644 index 0000000..2753bee --- /dev/null +++ b/app/campaigns/campaign_api/__init__.py @@ -0,0 +1,9 @@ +from flask import Blueprint + +campaign_api = Blueprint('campaign_api', + __name__, + template_folder='templates', + static_folder='static', + url_prefix='/campaign_api') + +from app.campaigns.campaign_api import routes \ No newline at end of file diff --git a/app/campaigns/campaign_api/routes.py b/app/campaigns/campaign_api/routes.py new file mode 100644 index 0000000..20f4746 --- /dev/null +++ b/app/campaigns/campaign_api/routes.py @@ -0,0 +1,30 @@ +from app import db +from flask import render_template +from app.campaigns.campaign_api import campaign_api +from app.campaigns.models import Campaign +from time import sleep + + + +@campaign_api.route('update_active_status//', methods=['PUT']) +def update_active_status(id, status): + if status == 'true': + status = True + else: + status = False + Campaign.query.filter_by(id=id).update({'active':status}) + db.session.commit() + sleep(1) + return {'status':'success'} + +@campaign_api.route('archive_campaign/', methods=['PUT']) +def archive_campaign(id): + Campaign.query.filter_by(id=id).update({'archived': True, 'active':False}) + db.session.commit() + return {'status':'success'} + +@campaign_api.route('un_archive_campaign/', methods=['PUT']) +def un_archive_campaign(id): + Campaign.query.filter_by(id=id).update({'archived': False, 'active':False}) + db.session.commit() + return {'status':'success'} \ No newline at end of file diff --git a/app/campaigns/forms.py b/app/campaigns/forms.py index e69de29..52b1712 100644 --- a/app/campaigns/forms.py +++ b/app/campaigns/forms.py @@ -0,0 +1,20 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, \ + EmailField, \ + PasswordField, \ + SubmitField, \ + SelectField, \ + BooleanField,\ + DateField, IntegerField +from flask_wtf.file import FileField, FileRequired, FileAllowed +from wtforms.validators import DataRequired, ValidationError, NumberRange, EqualTo, Email +from app.campaigns.models import Campaign +from flask_login import current_user +# import logging +# logging.basicConfig(filename='logs.log', encoding='utf-8', level=logging.DEBUG) + +class CreateCampaignForm(FlaskForm): + title = StringField('Title', validators=[DataRequired()]) + active = BooleanField('Initialize as active?') + goal = IntegerField('Goal') + submit = SubmitField('Add Campaign') \ No newline at end of file diff --git a/app/campaigns/models.py b/app/campaigns/models.py index 228bd1f..35c4c08 100644 --- a/app/campaigns/models.py +++ b/app/campaigns/models.py @@ -6,9 +6,37 @@ import jwt from datetime import datetime, timezone, timedelta +class AmbassadorMap(db.Model): + __tablename__ = 'ambassador_map' + id = Column('id', INTEGER(), primary_key=True) + campaign_id = Column('campaign_id', INTEGER(), nullable=False) + user_id = Column('user_id', INTEGER(), nullable=False) + goal = Column('ambassador_goal', INTEGER()) + + class Campaign(db.Model): __tablename__ = 'campaign' - id = Column('id', INTEGER(), primary_key=True) - title = Column('title', TEXT(), nullable=False) - #ambassadors \ No newline at end of file + id = Column('id', INTEGER(), primary_key=True) + title = Column('title', TEXT(), nullable=False) + active = Column('active', Boolean(), nullable=False, default=True) + goal = Column('goal', INTEGER(), default=0) + archived = Column('archived', Boolean(), default=False) + + def get_donations(self): + from app.main.models import Donation + return Donation.query.filter_by(campaign_id=self.id).all() + + def get_amount_raised(self): + donations = self.get_donations() + total = 0 + for donation in donations: + total += donation.amount + return total + + def get_ambassadors(self): + return AmbassadorMap.query.filter_by(campaign_id=self.id).all() + + + def __repr__(self) -> str: + return f"{self.id} - {self.title}" \ No newline at end of file diff --git a/app/campaigns/routes.py b/app/campaigns/routes.py index fa4faa0..0487579 100644 --- a/app/campaigns/routes.py +++ b/app/campaigns/routes.py @@ -1,13 +1,36 @@ from app import db from app.campaigns import campaigns from app.users.models import User -# from forms import LoginForm, RequestResetForm, ResetPasswordForm, EditUserForm, AddUserForm +from app.campaigns.models import Campaign +from app.campaigns.forms import CreateCampaignForm from flask import render_template, redirect, url_for, flash, request from flask_login import login_required, login_user, current_user, logout_user from werkzeug.security import check_password_hash, generate_password_hash from datetime import datetime import os +from time import sleep -@campaigns.route('add_campaign') +@campaigns.route('add_campaign', methods=['GET', 'POST']) def add_campaign(): - return render_template('add_campaign.html') \ No newline at end of file + form = CreateCampaignForm() + if form.validate_on_submit(): + campaign = Campaign( + title=form.title.data, + active=form.active.data, + goal=form.goal.data + ) + db.session.add(campaign) + db.session.commit() + return redirect(url_for('admin.administration')) + return render_template('add_campaign.html', + form=form) + +@campaigns.route('campaign_page/') +def campaign_page(campaign_id): + campaign = Campaign.query.filter_by(id=campaign_id).first() + return render_template('campaign_page.html', + campaign=campaign) + +@campaigns.route('add_ambassador') +def add_ambassador(): + return render_template('add_ambassador.html') \ No newline at end of file diff --git a/app/campaigns/templates/add_ambassador.html b/app/campaigns/templates/add_ambassador.html new file mode 100644 index 0000000..e69de29 diff --git a/app/campaigns/templates/add_campaign.html b/app/campaigns/templates/add_campaign.html index d100737..4394370 100644 --- a/app/campaigns/templates/add_campaign.html +++ b/app/campaigns/templates/add_campaign.html @@ -1,2 +1,56 @@ {% extends 'base.html' %} -{% block title %}Add campaign{% endblock title %} \ No newline at end of file +{% block title %}Add campaign{% endblock title %} +{% block content %} +
+
+
+
+
+

Register

+
+ {{ form.hidden_tag() }} + +
+ {{ form.title.label(class="form-label") }} + {{ form.title(class="form-control") }} + {% if form.title.errors %} +
    + {% for error in form.title.errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endif %} +
+ +
+ {{ form.active.label(class="form-label") }} + {{ form.active() }} + {% if form.active.errors %} +
    + {% for error in form.active.errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endif %} +
+ +
+ {{ form.goal.label(class="form-label") }} + {{ form.goal(class="form-control") }} + {% if form.goal.errors %} +
    + {% for error in form.goal.errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endif %} +
+ + {{ form.submit(class='btn btn-primary') }} +
+
+
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/app/campaigns/templates/campaign_page.html b/app/campaigns/templates/campaign_page.html new file mode 100644 index 0000000..830e065 --- /dev/null +++ b/app/campaigns/templates/campaign_page.html @@ -0,0 +1,62 @@ +{% extends 'base.html' %} +{% block title %}{{campaign.title}}{% endblock title %} +{% block content %} +
+
+
+
+
+
+

{{campaign.title}} Ambassadors

+ Become an ambassador! +
+ + + + + + + + + + {% for campaign_ambassador in campaign.get_ambassadors() %} + + + + + + {% endfor %} + +
NameGoalAmbassador Page Link
{{campaign_ambassador.user_id}}{{campaign_ambassador.goal}}
+ +
+
+
+
+

{{campaign.title}}

+ + + + + + + + + + + {% for campaign_donation in campaign.get_donations() %} + + + + + + {% endfor %} + +
#AmountUser
{{campaign_donation.id}}{{campaign_donation.amount}}{{campaign_donation.get_user()}}
+ +
+
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/app/main/__pycache__/__init__.cpython-310.pyc b/app/main/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index dc72af7..0000000 Binary files a/app/main/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/app/main/__pycache__/__init__.cpython-311.pyc b/app/main/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 4112e73..0000000 Binary files a/app/main/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/app/main/__pycache__/routes.cpython-310.pyc b/app/main/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index be8b20e..0000000 Binary files a/app/main/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/app/main/__pycache__/routes.cpython-311.pyc b/app/main/__pycache__/routes.cpython-311.pyc deleted file mode 100644 index 167bfcf..0000000 Binary files a/app/main/__pycache__/routes.cpython-311.pyc and /dev/null differ diff --git a/app/main/models.py b/app/main/models.py new file mode 100644 index 0000000..4829f2b --- /dev/null +++ b/app/main/models.py @@ -0,0 +1,23 @@ +from app import db +from flask import current_app +from flask_login import UserMixin, current_user +from sqlalchemy import TEXT, Column, Boolean, ForeignKey, TEXT, INTEGER, VARCHAR +import jwt +from datetime import datetime, timezone, timedelta + + +class Donation(db.Model): + __tablename__ = 'donation' + id = Column('id', INTEGER(), primary_key=True, autoincrement=True) + currency_type = Column('currency_type', TEXT(), nullable=False) + amount = Column('amount', INTEGER(), nullable=False) + user_id = Column('user_id', INTEGER(), nullable=False) + campaign_id = Column('campaign_id', INTEGER(), nullable=False) + anonymous = Column('anonymous', Boolean(), default=False) + + def get_user(self): + from app.users.models import User + return User.query.filter_by(id=self.user_id).first() + + def __repr__(self) -> str: + return f"{self.id} - {self.currency_type} - {self.amount}" \ No newline at end of file diff --git a/app/main/routes.py b/app/main/routes.py index 01b8a13..a253fd8 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,6 +1,7 @@ from app import db from app.main import main from app.users.models import User +from app.campaigns.models import Campaign # from forms import LoginForm, RequestResetForm, ResetPasswordForm, EditUserForm, AddUserForm from flask import render_template, redirect, url_for, flash, request from flask_login import login_required, login_user, current_user, logout_user @@ -12,6 +13,7 @@ import os @main.route('/homepage') -def homepage(): - - return render_template('homepage.html') \ No newline at end of file +def homepage(): + active_campaigns = Campaign.query.filter_by(active=True).all() + return render_template('homepage.html', + active_campaigns=active_campaigns) \ No newline at end of file diff --git a/app/main/static/main.css b/app/main/static/main.css new file mode 100644 index 0000000..e69de29 diff --git a/app/main/templates/base.html b/app/main/templates/base.html index b10b24b..7014347 100644 --- a/app/main/templates/base.html +++ b/app/main/templates/base.html @@ -14,18 +14,19 @@ {% with messages = get_flashed_messages(with_categories=true) %} diff --git a/app/main/templates/homepage.html b/app/main/templates/homepage.html index 8aad2a0..3a77cb9 100644 --- a/app/main/templates/homepage.html +++ b/app/main/templates/homepage.html @@ -1,2 +1,44 @@ {% extends 'base.html' %} -{% block title %}Customer Search{% endblock title %} \ No newline at end of file +{% block title %}Homepage{% endblock title %} + +{% block stylesheet %} + +{% endblock stylesheet %} +{% block content %} +
+
+ +

Visit Campaigns

+ + + + + + + + + + + + {% for campaign in active_campaigns %} + + + + + + + {% endfor %} + +
TitleGoalRaisedCampaign Link
{{campaign.title}}{{campaign.goal}}{{campaign.get_amount_raised()}}See campaign details
+
+ +
+{% endblock content %} \ No newline at end of file diff --git a/app/users/__pycache__/__init__.cpython-310.pyc b/app/users/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 9125a02..0000000 Binary files a/app/users/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/app/users/__pycache__/__init__.cpython-311.pyc b/app/users/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 829a5b5..0000000 Binary files a/app/users/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/app/users/__pycache__/forms.cpython-310.pyc b/app/users/__pycache__/forms.cpython-310.pyc deleted file mode 100644 index f320745..0000000 Binary files a/app/users/__pycache__/forms.cpython-310.pyc and /dev/null differ diff --git a/app/users/__pycache__/models.cpython-310.pyc b/app/users/__pycache__/models.cpython-310.pyc deleted file mode 100644 index 8568868..0000000 Binary files a/app/users/__pycache__/models.cpython-310.pyc and /dev/null differ diff --git a/app/users/__pycache__/models.cpython-311.pyc b/app/users/__pycache__/models.cpython-311.pyc deleted file mode 100644 index 2185249..0000000 Binary files a/app/users/__pycache__/models.cpython-311.pyc and /dev/null differ diff --git a/app/users/__pycache__/routes.cpython-310.pyc b/app/users/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index 7e89603..0000000 Binary files a/app/users/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/app/users/__pycache__/routes.cpython-311.pyc b/app/users/__pycache__/routes.cpython-311.pyc deleted file mode 100644 index 945ef39..0000000 Binary files a/app/users/__pycache__/routes.cpython-311.pyc and /dev/null differ diff --git a/app/users/models.py b/app/users/models.py index d412b08..94d44fa 100644 --- a/app/users/models.py +++ b/app/users/models.py @@ -1,7 +1,7 @@ from app import db from flask import current_app from flask_login import UserMixin, current_user -from sqlalchemy import TEXT, Column, Boolean, ForeignKey, TEXT, INTEGER, VARCHAR +from sqlalchemy import TEXT, Column, Boolean, ForeignKey, TEXT, INTEGER, VARCHAR, select import jwt from datetime import datetime, timezone, timedelta @@ -14,6 +14,14 @@ class User(db.Model, UserMixin): email = Column('email', TEXT(), nullable=False, unique=True) password = Column('password', TEXT(), nullable=False) user_type = Column('user_type', TEXT(), nullable=False) + + def get_donations(self): + from app.main.models import Donation + return db.session.execute(select(Donation.currency_type,Donation.amount).where(Donation.user_id == self.id)).all() + + def __repr__(self) -> str: + return f"{self.first_name} {self.last_name}" + # donation_id = Column(INTEGER, ForeignKey('donation.id')) def get_reset_token(self, expiration=600): reset_token = jwt.encode( diff --git a/app/users/routes.py b/app/users/routes.py index 0afaa25..4f85501 100644 --- a/app/users/routes.py +++ b/app/users/routes.py @@ -1,6 +1,8 @@ from app import db from app.users import users from app.users.models import User +from app.campaigns.models import Campaign +from app.main.models import Donation from app.users.forms import LoginForm, RegisterUserForm#, RequestResetForm, ResetPasswordForm, EditUserForm, AddUserForm from flask import render_template, redirect, url_for, flash, request from flask_login import login_required, login_user, current_user, logout_user @@ -15,17 +17,19 @@ import os @login_required def user_page(user_id): if not int(current_user.id) == int(user_id): - return redirect(url_for('main.dashboard')) + return redirect(url_for('main.homepage')) user = User.query.filter_by(id=user_id).first() + donations = user.get_donations() return render_template('user_page.html', - user=user) + user=user, + donations=donations) @users.route('/') @users.route('/login', methods=('GET', 'POST')) def login(): if current_user.is_authenticated: - return redirect(url_for('main.dashboard')) + return redirect(url_for('main.homepage')) form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() @@ -66,4 +70,4 @@ def register_user(): login_user(user) flash('Succesfully Registered!') return redirect(url_for('main.homepage')) - return render_template('register_user.html', form=form) \ No newline at end of file + return render_template('register_user.html', form=form) diff --git a/app/users/templates/user_page.html b/app/users/templates/user_page.html index 384348b..74637f1 100644 --- a/app/users/templates/user_page.html +++ b/app/users/templates/user_page.html @@ -1,38 +1,40 @@ {% extends 'base.html' %} {% block title %}User Page{% endblock title %} {% block content %} -
+

Welcome, {{ user.first_name }} {{ user.last_name }}


+
+
+
+
+
+

Donations

+
+ + + + + + + + + {% for donation in donations %} + + + + + {% endfor %} + +
CurrencyAmount
{{donation[0]}}{{donation[1]}}
-
-
- {% if user_reports %} -

Downloadable Files:

- - - - - - - - - - - - - - -
Report TypeNumber of RowsTime CreatedRange StartRange EndDownloadDelete
- {% else %} -

No reports available.

- {% endif %} +
+
- {% endblock %} \ No newline at end of file diff --git a/app/users/users_api/__init__.py b/app/users/users_api/__init__.py new file mode 100644 index 0000000..220211f --- /dev/null +++ b/app/users/users_api/__init__.py @@ -0,0 +1,9 @@ +from flask import Blueprint + +users_api = Blueprint('users_api', + __name__, + template_folder='templates', + static_folder='static', + url_prefix='/users_api') + +from app.users.users_api import routes \ No newline at end of file diff --git a/app/users/users_api/routes.py b/app/users/users_api/routes.py new file mode 100644 index 0000000..3f4d1f4 --- /dev/null +++ b/app/users/users_api/routes.py @@ -0,0 +1,18 @@ +from app import db +from flask import render_template +from app.users.users_api import users_api +from app.users.models import User +from time import sleep + + + +@users_api.route('update_admin_status//', methods=['PUT']) +def update_admin_status(id, status): + if status == 'true': + status = 'Admin' + else: + status = 'User' + User.query.filter_by(id=id).update({'user_type':status}) + db.session.commit() + sleep(1) + return {'status':'success'} diff --git a/python_files/db_population.py b/python_files/db_population.py new file mode 100644 index 0000000..11cd533 --- /dev/null +++ b/python_files/db_population.py @@ -0,0 +1,151 @@ +from sqlalchemy import create_engine, MetaData, Table, select, insert, func, update, bindparam, delete +import os +import csv +from datetime import datetime, timedelta +from dateutil.parser import parse +import time +import json +from werkzeug.security import generate_password_hash +import random + +def engineer(): + path1 = 'C:/Users/Lenovo/Desktop/Pilzno/instance/site.db' + path2 = '/home/yisroel2/Desktop/Pilzno/instance/site.db' + path3 = '/home/ubuntu/PilznoProject/PilznoProduction/instance/site.db' + + for p in [path1, path2, path3]: + if os.path.exists(p): + path = p + break + engine = create_engine(f'sqlite:///{path}') + metadata_obj = MetaData() + metadata_obj.reflect(bind=engine) + return engine, metadata_obj + +def check_db(): + engine, metadata_obj = engineer() + user_table = Table("user", metadata_obj, autoload_with=engine) + donation_table = Table("donation", metadata_obj, autoload_with=engine) + campaign_table = Table("campaign", metadata_obj, autoload_with=engine) + with engine.connect() as conn: + print('USERS') + users = conn.execute(select(user_table)).all() + if users: + for user in users: + print(user.first_name) + else: + print('no users') + print("DONATIONS") + donations = conn.execute(select(donation_table)).all() + if donations: + for donation in donations: + print(donation.id) + else: + print('no donations') + campaigns = conn.execute(select(campaign_table)).all() + print('CAMPAIGNS') + if campaigns: + for campaign in campaigns: + print(campaign.title) + else: + print('no campaigns') + +def insert_users(): + users = [ + ['Yisroel', 'Baum', 'yisroel.d.baum@gmail.com', generate_password_hash('12'), 'Admin'], + ['Yoni', 'Gerzi', 'yoni@gerzi.com', generate_password_hash('12'), 'User'], + ['Shmuli', 'Modes', 'shmuli@modes.com', generate_password_hash('12'), 'User'], + ['Emma', 'Baum', 'emma@baum.com', generate_password_hash('12'), 'User'], + ['Yisroel', 'Factor', 'yisroel@factor.com', generate_password_hash('12'), 'User'], + ['Yaakov', 'Frager', 'yaakov@frager.com', generate_password_hash('12'), 'User'], + ['Michael', 'Oshman', 'michael@oshman.com', generate_password_hash('12'), 'User'], + ['Shalom', 'Goldberg', 'shalom@goldberg.com', generate_password_hash('12'), 'User'], + ['Daniel', 'Caller', 'daniel@caller.com', generate_password_hash('12'), 'User'], + + ] + engine, metadata_obj = engineer() + user_table = Table("user", metadata_obj, autoload_with=engine) + + with engine.connect() as conn: + for user in users: + conn.execute(user_table.insert().values( + first_name = user[0], + last_name = user[1], + email = user[2], + password = user[3], + user_type = user[4] + )) + conn.commit() + +def insert_donations(): + engine, metadata_obj = engineer() + donation_table = Table("donation", metadata_obj, autoload_with=engine) + + currency_types = ['shekel', 'dollar'] + boolean_choice = [True, False] + + with engine.connect() as conn: + for _ in range(100): + conn.execute(donation_table.insert().values( + currency_type = currency_types[random.randint(0,1)], + amount = random.randint(1,200), + anonymous = boolean_choice[random.randint(0,1)], + campaign_id=random.randint(1,4), + user_id=random.randint(1,9) + )) + conn.commit() + +def insert_campaigns(): + engine, metadata_obj = engineer() + campaign_table = Table("campaign", metadata_obj, autoload_with=engine) + + campaign_titles = ['general campaign', 'yomim noraim 2024', 'pesach kibbudim 2024', 'RH kibbudim 2024'] + is_active = [True, False] + with engine.connect() as conn: + for title in campaign_titles: + conn.execute(campaign_table.insert().values( + title=title, + active=is_active[random.randint(0,1)], + goal=5000, + archived=False + )) + conn.commit() + +def insert_ambassador_relationships(): + engine, metadata_obj = engineer() + user_table = Table("user", metadata_obj, autoload_with=engine) + donation_table = Table("donation", metadata_obj, autoload_with=engine) + campaign_table = Table("campaign", metadata_obj, autoload_with=engine) + + with engine.connect() as conn: + campaign_ids = conn.execute(select(campaign_table.c.id)).all() + user_ids = conn.execute(select(user_table.c.id)).all() + + +def test_selections(): + engine, metadata_obj = engineer() + user_table = Table("user", metadata_obj, autoload_with=engine) + donation_table = Table("donation", metadata_obj, autoload_with=engine) + campaign_table = Table("campaign", metadata_obj, autoload_with=engine) + + +def delete_all(): + engine, metadata_obj = engineer() + user_table = Table("user", metadata_obj, autoload_with=engine) + donation_table = Table("donation", metadata_obj, autoload_with=engine) + campaign_table = Table("campaign", metadata_obj, autoload_with=engine) + + with engine.connect() as conn: + conn.execute(delete(user_table)) + conn.execute(delete(campaign_table)) + conn.execute(delete(donation_table)) + conn.commit() + +def insert_all(): + insert_users() + insert_campaigns() + insert_donations() + +if __name__ == '__main__': + delete_all() + insert_all() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8fe6503 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,39 @@ +alembic==1.13.2 +amqp==5.2.0 +billiard==4.2.0 +blinker==1.8.2 +celery==5.4.0 +cffi==1.17.0 +click==8.1.7 +click-didyoumean==0.3.1 +click-plugins==1.1.1 +click-repl==0.3.0 +colorama==0.4.6 +cryptography==43.0.1 +dnspython==2.6.1 +email_validator==2.2.0 +Flask==3.0.3 +Flask-Login==0.6.3 +Flask-Mail==0.10.0 +Flask-Migrate==4.0.7 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.1 +greenlet==3.0.3 +idna==3.8 +itsdangerous==2.2.0 +Jinja2==3.1.4 +jwt==1.3.1 +kombu==5.4.0 +Mako==1.3.5 +MarkupSafe==2.1.5 +prompt_toolkit==3.0.47 +pycparser==2.22 +python-dateutil==2.9.0.post0 +six==1.16.0 +SQLAlchemy==2.0.33 +typing_extensions==4.12.2 +tzdata==2024.1 +vine==5.1.0 +wcwidth==0.2.13 +Werkzeug==3.0.4 +WTForms==3.1.2