Initial commit

This commit is contained in:
Markus Zimmerberger
2024-02-13 15:50:44 +01:00
commit c66dccfaaa
20 changed files with 6707 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
node_modules
payment_slips
sust
env
.vscode
__pycache__
Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

+32
View File
@@ -0,0 +1,32 @@
from sqlalchemy import func
from sqlalchemy.types import UserDefinedType, Float
class EasyGeometry(UserDefinedType):
def get_col_spec(self):
return "GEOMETRY"
def bind_expression(self, bindvalue):
return func.ST_GeomFromText(bindvalue, type_=self)
def column_expression(self, col):
return func.ST_AsText(col, type_=self)
def bind_processor(self, dialect):
def process(value):
if value is None:
return None
assert isinstance(value, tuple)
lat, lng = value
return "POINT(%s %s)" % (lng, lat)
return process
def result_processor(self, dialect, coltype):
def process(value):
if value is None:
return None
#m = re.match(r'^POINT\((\S+) (\S+)\)$', value)
#lng, lat = m.groups()
lng, lat = value[6:-1].split() # 'POINT(135.00 35.00)' => ('135.00', '35.00')
return (float(lat), float(lng))
return process
+106
View File
@@ -0,0 +1,106 @@
import models
import locale
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from segno import helpers
import datetime
import locale
def print_multiple_lines(lines: int, text):
cnt = 1
while cnt < lines:
text.textLines("")
cnt += 1
def create_payment_slip(member: models.Members):
locale.setlocale(locale.LC_ALL, '')
w, h = A4
# create epc qr-code
qrcode = helpers.make_epc_qr(name='Sportunion St. Thomas',
iban='AT493443700000113407',
amount=15*member.member_count,
reference=f'2024-4732-{member.id:03}-{member.member_count}')
qrcode.save("epc_qr.png", scale=2)
subfolder = "mail/" if member.member_email else ""
c = canvas.Canvas(f"payment_slips/{subfolder}{member.member_zip}_payment_slip_member{member.id}.pdf", pagesize=A4)
#img = qrcode.to_pil(scale=4, dark='darkred', data_dark='darkorange',
# data_light='yellow')
#qrcodeba = bytearray(img)
c.drawImage(image="epc_qr.png", x=500, y=40) # epc qr
c.drawImage(image="Sportunion quer 4c.png", x=(w-180), y=(h-100), width=150, height=50, mask='auto')
c.setFont("Helvetica", 9)
text = c.beginText(60, h - 150)
if member.member_count > 1:
text.textLine(f"{member.member_firstname_spouse} u. {member.member_firstname} {member.member_surname}")
else:
text.textLine(f"{member.member_firstname} {member.member_surname} {member.member_suffix}")
text.textLine(f"{member.member_street} {member.member_streetnumber}")
text.textLine(f"{member.member_zip} {member.member_city}")
print_multiple_lines(5, text)
if member.member_count > 1:
salutation = "eure"
salutation2 = "euer"
salutation3 = "euch"
salutation4 = "Solltet ihr"
salutation5 = "sendet"
text.textLine(f"Liebe {member.member_firstname_spouse} und lieber {member.member_firstname}!")
else:
salutation = "Deine"
salutation2 = "Dein"
salutation3 = "Dich"
salutation4 = "Solltest Du"
salutation5 = "sende"
if member.member_gender == "female":
salutation_gender = "liebe"
else:
salutation_gender = "lieber"
text.textLine(f"{salutation_gender.capitalize()} {member.member_firstname}!")
c.drawString(w - 150, h - 225, f'St. Thomas, {datetime.date.today().strftime('%x')}')
print_multiple_lines(5, text)
text.textLines(f"Vielen Dank für {salutation} Treue!\n\n{salutation2.capitalize()} Beitrag ist gut angelegt, denn der Sportverein St. Thomas bietet für jede(n) etwas:")
print_multiple_lines(2, text)
text.textLines("* Skigymnastik\n* Radfahren\n* Stockschiessen\n* Pilates\n* Tagesskifahrt\n* uvam")
print_multiple_lines(5, text)
text.textLines(f"Wir möchten das Einheben des Mitgliedsbeitrags zuküntig einfacher machen, daher bitten wir {salutation3},\nuns eine Mail mit dem Betreff \"Zahlschein oder Abbucher\" an unsere Mail-Adresse sv.stthomas@gmx.at zu senden!")
print_multiple_lines(3, text)
text.textLines(f"{salutation2.capitalize()} Sportverein-Team!")
c.drawText(text)
text = c.beginText(60, h - 500)
c.setFont("Helvetica", 7)
text.textLines(f"Auf Grund der DSGVO müssen wir darauf hinweisen, dass wir außer Name, Adresse, Mail-Adresse\nzum Zwecke der Einebung des Mitgliedsbeitrags, keine persönlichen Daten unserer Mitglieder führen!\nDiese Daten werden von uns nicht an Dritte weitergegeben!\nWeiters informieren wir darüber, dass Fotos, die bei Veranstaltungen oder Aktivitäten entstehen,\nauf unserer Homepage oder in Zeitungen veröffentlicht werden können.\n{salutation4} gegen die Speicherung {salutation}r Daten oder die Veröffentlichung diverser Foto sein, {salutation5} uns bitte eine Mail.")
c.drawText(text)
# payment slip details
text = c.beginText(11, 75)
c.setFont("Helvetica", 8)
text.textLine(f"Beitrag gesamt: {locale.format_string('%.2f', 15 * member.member_count)}")
text.textLine(f"Mitgliedsnummer: 4732-{member.id:03}")
c.drawText(text)
text = c.beginText(188, 103)
c.setFont("Helvetica", 8)
text.textLine(f"Bei Online-Zahlung bitte die Zahlungsreferenz 2024-4732-{member.id:03}-{member.member_count} eintragen")
print_multiple_lines(1, text)
if member.member_count > 1:
text.textLine(f"Mitgliedsnummer: 4732-{member.id:03} {member.member_firstname_spouse} {member.member_suffix} {member.member_firstname} {member.member_surname}")
else:
text.textLine(f"Mitgliedsnummer: 4732-{member.id:03} {member.member_firstname} {member.member_surname}")
c.drawText(text)
c.setFont("Helvetica", 10)
c.drawString(188, h - 696, f'2024-4732-{member.id:03}-{member.member_count}') # Zahlungsreferenz Zahlungsanweisung
c.drawString(11, 120, f'2024-4732-{member.id:03}-{member.member_count}') # Zahlungsreferenz Auftragsbestätigung
c.drawString(130, h - 694, (locale.format_string('%.2f', 15 * member.member_count))) # Betrag Auftragsbestätigung
c.drawString(w - 30, 170, (locale.format_string('%.2f', 15 * member.member_count))) # Betrag Zahlungsanweisung
c.save()
+11
View File
@@ -0,0 +1,11 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
URL_DATABASE = 'postgresql://sust:sust@192.168.1.202:5432/sust'
engine = create_engine(URL_DATABASE)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

+25
View File
@@ -0,0 +1,25 @@
update members m set member_geo_latitude = null, member_geo_longitude = null
where id in (110,107,45,21,19,108,36,24,22,18,35,162,90,3,142,122,121,115,9,128,79,78,50,186,183,180,176,157,148,98);
select * from members m where m.member_geo_latitude is null;
select * from members m where id in (110,107,45,21,19,108,36,24,22,18,35,162,90,3,142,122,121,115,9,128,79,78,50,186,183,180,176,157,148,98);
update members m set member_geo_latitude = null, member_geo_longitude = null
ALTER TABLE sust.deliveries ADD CONSTRAINT deliveries_member_id_delivered_by_fkey FOREIGN KEY (delivered_by_id) REFERENCES members(id)
update deliveries set delivered_by_id = 186
update members set member_city = 'Sankt Thomas' where member_city = 'St. Thomas'
update members set member_city = trim(member_city)
insert into deliveries (delivery_date, member_id, delivered_by_id, delivery_year)
values (now(), 78, 186, '2024-01-01'),
(now(), 79, 186, '2024-01-01'),
(now(), 3, 186, '2024-01-01'),
(now(), 4, 186, '2024-01-01'),
(now(), 9, 186, '2024-01-01')
update deliveries set delivery_year = '2024-01-01'
+1
View File
@@ -0,0 +1 @@
.\.venv\Scripts\Activate
+24
View File
@@ -0,0 +1,24 @@
import logging.config
from pythonjsonlogger import jsonlogger
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"format": "%(asctime)s %(levelname)s %(message)s",
"class": "pythonjsonlogger.jsonlogger.JsonFormatter",
}
},
"handlers": {
"stdout": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"formatter": "json",
}
},
"loggers": {"": {"handlers": ["stdout"], "level": "DEBUG"}},
}
logging.config.dictConfig(LOGGING)
+76
View File
@@ -0,0 +1,76 @@
from sqlalchemy import extract
from database import engine, SessionLocal
import models
import logging, sys
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from os.path import exists
import datetime
from create_payment_slip import create_payment_slip
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
def payment_slip_delivered(member: models.Members, year: int):
return db.query(models.Deliveries.delivery_date)\
.filter(models.Deliveries.member_id == member.id, extract('year', models.Deliveries.delivery_date) == year)\
.count()
def send_mail(member: models.Members):
delivered = payment_slip_delivered(member, 2024)
if delivered:
logging.debug(f'Member: {member.member_surname} already mailed or delivered!')
return
smtp_server = 'mail.gmx.com'
smtp_port = 587
smtp_username = 'zisco@gmx.at'
smtp_password = '57spunk07'
from_email = 'sv.stthomas@gmx.at'
to_email = member.member_email
subject = 'Mitgliedsbeitrag 2024'
body = f'Liebes Mitglied,\n\nDein Zahlschein für 2024 liegt bei.\nDu kannst gerne mittels beiliegendem, aufgedrucktem QR-Code überweisen.\nDanke sagt Dein Team vom Sportverein!'
msg = MIMEMultipart()
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = subject
msg.attach(MIMEText(body))
# es kann sein, dass die Mail-Adresse erst später ins System kam
path_to_file = f'payment_slips\\mail\\{member.member_zip}_payment_slip_member{member.id}.pdf'
if not exists(path_to_file):
create_payment_slip(member=member)
with open(path_to_file, 'rb') as f:
attachment = MIMEApplication(f.read(), _subtype='pdf')
attachment.add_header('Content-Disposition', 'attachment', filename=f'{member.member_zip}_payment_slip_member{member.id}.pdf')
msg.attach(attachment)
with smtplib.SMTP(smtp_server, smtp_port) as smtp:
smtp.starttls()
smtp.login(smtp_username, smtp_password)
smtp.send_message(msg)
newDelivery = models.Deliveries(delivery_date = datetime.datetime.now(),
member_id = member.id,
delivered_by_id = 186,
delivery_year = datetime.date.today())
db.add(newDelivery)
db.commit()
logging.debug(f'Sent mail to {member.member_email}')
db = SessionLocal()
models.Base.metadata.create_all(bind=engine)
#members = db.query(models.Members).filter(models.Members.member_email != None, ~models.Members.id.in_([183,51,200,49])).all()
#members = db.query(models.Members).filter(models.Members.member_active == True).all()
#members = db.query(models.Members).filter(models.Members.id.in_([2,3,9,199]), models.Members.member_active == True, models.Members.member_printed == False).all()
members = db.query(models.Members).filter(models.Members.id.in_([89,149,132,102,106])).all()
for member in members:
send_mail(member=member)
+130
View File
@@ -0,0 +1,130 @@
from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import OAuth2PasswordBearer
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Annotated, Optional
import models
from database import engine, SessionLocal
from sqlalchemy.orm import Session
from datetime import datetime
#http://127.0.0.1:8000/docs -> swagger like ui
#ngrok http http://localhost:8000
app = FastAPI()
#oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
models.Base.metadata.create_all(bind=engine)
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
"http://localhost:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class DeliveryBase(BaseModel):
delivery_date: datetime
delivered_by: int
class PaymentBase(BaseModel):
payment_date: datetime
class MemberBase(BaseModel):
member_count: int
member_firstname: str
member_firstname_spouse: str | None = None
member_surname: str
member_suffix: str | None = None
member_street: str
member_streetnumber: str | None = None
member_email: str | None = None
member_zip: int
member_city: str
member_active: bool
member_gender: str | None = None
member_connected: int | None = None
member_printed: bool
#member_coordinates:
#payments: Optional[List[PaymentBase]]
member_deliveries: Optional[List[DeliveryBase]]
#deliveries: Mapped[List["DeliveryBase"]] = relationship()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
db_dependency = Annotated[Session, Depends(get_db)]
@app.get("/members/", response_model=List[MemberBase])
async def read_members(db: db_dependency, skip: int = 0, limit: int = 100 ):
members = db.query(models.Members).offset(skip).limit(limit).all()
if not members:
raise HTTPException(status_code=400, detail='No members')
return members
@app.get("/member/{member_id}")
async def read_member(member_id: int, db: db_dependency): #, token: Annotated[str, Depends(oauth2_scheme)]):
result = db.query(models.Members).filter(models.Members.id == member_id).first()
if not result:
raise HTTPException(status_code=400, detail='Member does not exist')
return result
@app.get("/payments/{member_id}")
async def read_payments(member_id: int, db: db_dependency):
result = db.query(models.Members).filter(models.Members.id == member_id).all()
if not result:
raise HTTPException(status_code=400, detail=f'No payments found for member {member_id}')
return result
@app.post("/members/")
async def create_member(member: MemberBase, db: db_dependency):
#db_member = models.Members(**member.model_dump())
db_member = models.Members(member_firstname=member.member_firstname,
member_surname=member.member_surname,
member_active=member.member_active)
db.add(db_member)
db.commit()
@app.post("/fullmembers/")
async def create_members(members: List[MemberBase], db: db_dependency):
for member in members:
db_member = models.Members(member_count=member.member_count,
member_firstname=member.member_firstname,
member_firstname_spouse=member.member_firstname_spouse,
member_surname=member.member_surname,
member_suffix=member.member_suffix,
member_street=member.member_street,
member_streetnumber=member.member_streetnumber,
member_email=member.member_email,
member_zip=member.member_zip,
member_city=member.member_city,
member_active=member.member_active,
member_gender=member.member_gender,
member_connected=member.member_connected)
db.add(db_member)
#db.commit()
#db.refresh(db_member)
if not member.payments is None:
for payment in member.payments:
db_payment = models.Payments(payment_date=payment.payment_date,
member_id=db_member.id)
db.add(db_payment)
db.commit()
+5902
View File
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
# import the library and its Marker clusterization service
import folium
from database import SessionLocal
from folium.plugins import MarkerCluster
import models
import logging
from geopy.geocoders import Bing
from sqlalchemy import *
import logging
import webbrowser
import re
def payment_slip_delivered(member: models.Members, year: int):
return db.query(models.Deliveries.delivery_date)\
.filter(models.Deliveries.member_id == member.id, extract('year', models.Deliveries.delivery_date) == year)\
.count()
def update_missing_locs():
members = db.query(models.Members).filter(models.Members.member_geo_latitude == None).all()
for member in members:
address = f'{member.member_street} {member.member_streetnumber}, {member.member_zip} {member.member_city}, Austria'
location = geolocator.geocode(address)
if location != None:
#member.member_coordinates = [location.latitude, location.longitude]
member.member_geo_latitude = location.latitude
member.member_geo_longitude = location.longitude
logger.info(f'Member: {member.id} updated with geo-coordinates')
else:
logger.info(f'Member: {member.id} address couldn\'t be found')
db.commit()
def checkEmail(email):
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
if email is None: return False
# pass the regular expression
# and the string into the fullmatch() method
if(re.fullmatch(regex, email)):
return True
else:
return False
logger = logging.getLogger(__name__)
logger.info("An info")
logger.warning("A warning")
#geolocator = Nominatim(user_agent="sust", timeout=10)
geolocator = Bing(api_key='ApbTfEJ024nQCu6DC7TPIlqAcRMfZT8lPjAyMnVRcmo9rqxde5x-IS4fW-nGu0FI')
db = SessionLocal()
#models.Base.metadata.create_all(bind=engine)
update_missing_locs()
#members = db.query(models.Members).filter(models.Members.id.in_([2,4,5,6])).all()
#TODO try
""" members = Members.query.all()
for member in members:
for delivery in members.deliveries:
print(f'{delivery.delivery_date}') """
members = db.query(models.Members.member_geo_latitude,
models.Members.member_geo_longitude,
models.Members.member_firstname,
models.Members.member_surname,
models.Members.id,
models.Members.member_email)\
.filter(models.Members.member_geo_latitude != None, models.Members.member_count != 0)\
.all()
# .join(models.Members.member_deliveries)\
# Create a map object and center it to the avarage coordinates to m
coords = [48.284793, 13.880550]
map = folium.Map(location=coords, zoom_start = 13)
# if the points are too close to each other, cluster them, create a cluster overlay with MarkerCluster, add to m
marker_cluster = MarkerCluster().add_to(map)
# draw the markers and assign popup and hover texts
# add the markers the the cluster layers so that they are automatically clustered
#folium.Marker([lat, lon], popup=str(name)+': '+color+'-'+str(clname), icon=folium.Icon(color='black',icon_color='#FFFF00')).add_to(feature_group)
for member in members:
if member.member_geo_latitude != None and member.member_geo_latitude != None:
delivered = payment_slip_delivered(member, 2024)
iconColor = "blue"
hasMail = checkEmail(member.member_email)
if hasMail:
iconColor = "orange"
elif delivered:
iconColor = "green"
if hasMail and delivered:
iconColor = "darkgreen"
#delivered = member.member_deliveries
location = (member.member_geo_latitude, member.member_geo_longitude)
folium.Marker(location=location,
popup=f'{member.member_firstname} {member.member_surname}',
tooltip=f'\nZugestellt' if delivered else 'Nicht zugestellt',
icon=folium.Icon(color=iconColor))\
.add_to(marker_cluster)
#Display the map
map.save("map.html")
webbrowser.open("map.html")
#func.count(models.Members.member_deliveries).label("count"))\
+80
View File
@@ -0,0 +1,80 @@
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime, Float
from sqlalchemy.types import TypeDecorator, VARCHAR, Integer
from sqlalchemy_utils import EmailType
from sqlalchemy.orm import relationship, Mapped
from geoalchemy2 import Geometry
from database import Base
import datetime
from coordinates_class import EasyGeometry
class NullCatchingString(TypeDecorator):
impl = VARCHAR
def process_result_value(self, value, dialect):
if value is None:
return ''
return value
class NullCatchingInteger(TypeDecorator):
impl = Integer
def process_result_value(self, value, dialect):
if value is None:
return ''
return value
class Members(Base):
__tablename__ = 'members'
id = Column(Integer, primary_key=True, index=True)
member_count = Column(Integer)
member_firstname = Column(String, index=True)
member_firstname_spouse = Column(NullCatchingString())
member_surname = Column(String, index=True)
member_suffix = Column(NullCatchingString(), nullable=True)
member_street = Column(NullCatchingString(), index=True)
member_streetnumber = Column(NullCatchingString(), nullable=True)
member_email = Column(EmailType, index=True)
member_zip = Column(NullCatchingInteger(), index=True)
member_city = Column(NullCatchingString(), index=True)
member_active = Column(Boolean, default=True)
member_gender = Column(String, nullable=True)
member_connected = Column(Integer, ForeignKey("members.id"), nullable=True)
created_date = Column(DateTime, default=datetime.datetime.utcnow)
updated_date = Column(DateTime, default=datetime.datetime.utcnow)
member_died = Column(DateTime)
member_printed = Column(Boolean, default=False)
#member_coordinates = Column(Geometry(geometry_type='POINT'))
member_geo_latitude = Column(Float, nullable=True)
member_geo_longitude = Column(Float, nullable=True)
member_deliveries = relationship('Deliveries', backref='members')
#member_deliveries: Mapped[List["Deliveries"]] = relationship()
def __repr__(self):
return f'<Comment "{self.content[:20]}...">'
class Payments(Base):
__tablename__ = 'payments'
id = Column(Integer, primary_key=True, index=True)
payment_date = Column(String, index=True)
member_id = Column(Integer, ForeignKey("members.id"))
def __repr__(self):
return f'<Comment "{self.content[:20]}...">'
class Deliveries(Base):
__tablename__ = 'deliveries'
id = Column(Integer, primary_key=True, index=True)
delivery_date = Column(DateTime, index=True)
member_id = Column(Integer, ForeignKey("members.id"))
delivered_by_id = Column(Integer)
delivery_year = Column(DateTime, index=True)
def __repr__(self):
return f'<Comment "{self.content[:20]}...">'
""" delivered_by_id = Column(Integer, ForeignKey("members.id" ))
member = relationship("Members", foreign_keys=[member_id])
delivering_member = relationship("Members", foreign_keys=[member_id]) """
+102
View File
@@ -0,0 +1,102 @@
{
"name": "fastAPI-PostgreSQL",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^1.6.2"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"dependencies": {
"axios": "^1.6.2"
}
}
+23
View File
@@ -0,0 +1,23 @@
from reportlab.lib.pagesizes import A4
from create_payment_slip import create_payment_slip
from database import engine, SessionLocal
from pydantic import BaseModel
from sqlalchemy.orm import Session
import models
from reportlab.lib.utils import ImageReader
import logging, sys
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
db = SessionLocal()
models.Base.metadata.create_all(bind=engine)
#members = db.query(models.Members).filter(models.Members.member_email == None).all()
#members = db.query(models.Members).filter(models.Members.member_active == True).all()
#members = db.query(models.Members).filter(models.Members.id.in_([2,3,9,199]), models.Members.member_active == True, models.Members.member_printed == False).all()
members = db.query(models.Members).filter(models.Members.id.in_([200])).all()
for member in members:
create_payment_slip(member=member)
logging.debug(f'Member: {member.member_surname}')
#logging.info('We processed %d records', len(processed_records))
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python
from heyoo import WhatsApp
#import pywhatkit
from database import engine, SessionLocal
import models
import logging, sys
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from os.path import exists
import datetime
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
def send_whatsapp(member: models.Members):
messenger = WhatsApp('EAAj1p1TLuUsBOy3849bBRZC8fEAZCc12440OW6L4BXDiiviWNWtAOLK3iyOKy3KnoRL3FOzCbiCoU0JKcoIAVzHLeBKUITaYWZBBl616wGyuqvIeQTyMFRuHiXM1MQ2ivnz74Bg6VD6GXMEpF0wzU68RdyyTwaQzSRmaXMW0R9IN0I9BUnSgFUJDqO1P66sngthaLuOxS3LGjvi',
phone_number_id='250992074758193')
#For sending a Text messages
messenger.send_message('Hello I am WhatsApp Cloud API (on behalf of your husband)', '4367761802110')
#For sending an Image
# messenger.send_image(
# image="https://i.imgur.com/YSJayCb.jpeg",
# recipient_id="91989155xxxx",
# )
# subject = 'Mitgliedsbeitrag 2024'
# body = f'Liebes Mitglied,\n\nDein Zahlschein für 2024 liegt bei.\nDu kannst gerne mittels beiliegendem, aufgedrucktem QR-Code überweisen.\nDanke sagt Dein Team vom Sportverein!'
# # es kann sein, dass die Mail-Adresse erst später ins System kam
# path_to_file = f'payment_slips\\mail\\{member.member_zip}_payment_slip_member{member.id}.pdf'
# if exists(path_to_file):
# with open(path_to_file, 'rb') as f:
# attachment = MIMEApplication(f.read(), _subtype='pdf')
# attachment.add_header('Content-Disposition', 'attachment', filename=f'{member.member_zip}_payment_slip_member{member.id}.pdf')
# msg.attach(attachment)
# with smtplib.SMTP(smtp_server, smtp_port) as smtp:
# smtp.starttls()
# smtp.login(smtp_username, smtp_password)
# smtp.send_message(msg)
# newDelivery = models.Deliveries(delivery_date = datetime.datetime.now(),
# member_id = member.id,
# delivered_by_id = 186,
# delivery_year = datetime.date.today())
# db.add(newDelivery)
# db.commit()
# logging.debug(f'Sent mail to {member.member_email}')
db = SessionLocal()
models.Base.metadata.create_all(bind=engine)
#members = db.query(models.Members).filter(models.Members.member_email != None, ~models.Members.id.in_([183,51,200,49])).all()
#members = db.query(models.Members).filter(models.Members.member_active == True).all()
#members = db.query(models.Members).filter(models.Members.id.in_([2,3,9,199]), models.Members.member_active == True, models.Members.member_printed == False).all()
members = db.query(models.Members).filter(models.Members.id.in_([186])).all()
for member in members:
send_whatsapp(member=member)
logging.debug(f'Member: {member.member_surname}')