Move all to subDir sustApp
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
@@ -0,0 +1,4 @@
|
||||
# from .models import Base
|
||||
|
||||
__version__ = "0.0.1"
|
||||
__author__ = "zisco"
|
||||
@@ -0,0 +1,106 @@
|
||||
# 3. assign manually
|
||||
|
||||
from database import Base, engine, SessionLocal
|
||||
from time import time
|
||||
from models import Payments_CSV, Payments, Members
|
||||
import logging, sys
|
||||
import datetime
|
||||
|
||||
def assign_by_reference(payments: Payments_CSV):
|
||||
for payment in payments:
|
||||
logging.debug(f'payment.pc_reference {payment.pc_reference}')
|
||||
reference = payment.pc_reference.split('-')
|
||||
if len(reference) == 1:
|
||||
reference = payment.pc_reference.split(' ')
|
||||
|
||||
if len(reference) == 4:
|
||||
addPayment(payment, reference[2], reference[0], payment.pc_amount)
|
||||
else:
|
||||
# more than one reference (e.g. 2024-4732-096-2,-097-1,-099-1)
|
||||
referenceList = payment.pc_reference.split(',')
|
||||
number_of_references = completeReference(referenceList=referenceList)
|
||||
amount = payment.pc_amount # assume that the payment is for all references
|
||||
|
||||
for reference in referenceList:
|
||||
referenceItem = reference.split('-')
|
||||
addPayment(payment, referenceItem[2], year=referenceItem[0], amount=amount/number_of_references*int(referenceItem[3]))
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
db.rollback()
|
||||
|
||||
def assign_by_name(payments: Payments_CSV):
|
||||
year = 2025 # pc_reference is empty
|
||||
removables = ('Dipl.', 'Ing.', 'Mag.')
|
||||
for payment in payments:
|
||||
logging.debug(f'payment.pc_intended {payment.pc_intended}')
|
||||
name = payment.pc_client.split(' ')
|
||||
for removeable in removables:
|
||||
while removeable in name: name.remove(removeable)
|
||||
|
||||
# last part is usually surname
|
||||
member = db.query(Members).filter(Members.member_surname.like(name[len(name)-1]), Members.member_firstname.like(name[0])).all()
|
||||
if len(member) > 1:
|
||||
logging.debug(f'More than one member with same name {payment.pc_client}')
|
||||
elif len(member) == 0:
|
||||
logging.debug(f'No member found with name {payment.pc_client}')
|
||||
else:
|
||||
# update Payments_CSV
|
||||
payment.pc_reference = f'{year}-4732-{member[0].id}-{member[0].member_count}'
|
||||
|
||||
#addPayment(payment, referenceItem[2], referenceItem[0], amount/number_of_references*int(referenceItem[3]))
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
db.rollback()
|
||||
|
||||
|
||||
def addPayment(payment, member_id, year, amount):
|
||||
newPayment = Payments(payment_date = payment.pc_valuta_date,
|
||||
member_id = member_id,
|
||||
payment_year = datetime.date(int(year),1,1),
|
||||
payment_amount = amount)
|
||||
|
||||
db.add(newPayment)
|
||||
payment.pc_processed = True
|
||||
|
||||
def completeReference(referenceList: list) -> int:
|
||||
number_of_references = 0
|
||||
i=0
|
||||
for reference in referenceList:
|
||||
referenceItem = reference.split('-')
|
||||
if referenceItem[0] == '':
|
||||
referenceItem.insert(1, referenceList[0].split('-')[0])
|
||||
referenceItem.insert(2, referenceList[0].split('-')[1])
|
||||
referenceItem.pop(0)
|
||||
|
||||
reference = '-'.join(referenceItem)
|
||||
referenceList[i]=reference
|
||||
i+=1
|
||||
|
||||
number_of_references += int(referenceItem[3])
|
||||
|
||||
return number_of_references
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
t = time()
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
|
||||
|
||||
db = SessionLocal()
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
# 1. create missing reference by name
|
||||
payments = db.query(Payments_CSV).filter(Payments_CSV.pc_intended!='', Payments_CSV.pc_reference=='', Payments_CSV.pc_processed!=True).all()
|
||||
assign_by_name(payments)
|
||||
|
||||
# 2. assign payment by reference
|
||||
payments = db.query(Payments_CSV).filter(Payments_CSV.pc_reference!='', Payments_CSV.pc_processed!=True).all()
|
||||
assign_by_reference(payments)
|
||||
|
||||
logging.debug(f"Time elapsed: " + str(time() - t) + " s.") #0.091s
|
||||
@@ -0,0 +1,73 @@
|
||||
# Importing libraries
|
||||
from numpy import genfromtxt, savetxt
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
import models
|
||||
import os, re
|
||||
from database import engine, SessionLocal
|
||||
from pytz import timezone
|
||||
|
||||
def Load_Data(file_name):
|
||||
#data = genfromtxt(file_name, delimiter=';', skip_header=1, converters={0: lambda s: str(s)})
|
||||
#str2date = lambda x: datetime.strptime(x, '%d-%m-%y')
|
||||
data = genfromtxt(file_name, delimiter=';', dtype=None, skip_header=0, encoding='utf-8-sig')
|
||||
|
||||
return data.tolist()
|
||||
|
||||
def extract_values(text):
|
||||
# Regular expressions to match the required fields
|
||||
regex_patterns = {
|
||||
'Auftraggeber': r'Auftraggeber: (.*?) (Verwendungszweck|Zahlungsreferenz|IBAN|BIC)',
|
||||
'Verwendungszweck': r'Verwendungszweck: (.*?) (Zahlungsreferenz|IBAN|BIC)',
|
||||
'Zahlungsreferenz': r'Zahlungsreferenz: (.*?) (Verwendungszweck|IBAN|BIC)',
|
||||
'IBAN': r'IBAN Auftraggeber: (.*?) ',
|
||||
'BIC': r'BIC Auftraggeber: (.*?)$'
|
||||
}
|
||||
|
||||
# Initialize an empty list to store extracted information
|
||||
extracted_info = []
|
||||
|
||||
# Initialize an empty dictionary to store information from this row
|
||||
row_info = {}
|
||||
|
||||
# Extract information using regular expressions
|
||||
for key, pattern in regex_patterns.items():
|
||||
match = re.search(pattern, text)
|
||||
if match:
|
||||
row_info[key] = match.group(1)
|
||||
else:
|
||||
row_info[key] = None
|
||||
|
||||
# Append the extracted information from this row to the list
|
||||
extracted_info.append(row_info)
|
||||
|
||||
return extracted_info
|
||||
|
||||
if __name__ == "__main__":
|
||||
t = time()
|
||||
|
||||
db = SessionLocal()
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
try:
|
||||
home_directory = os.path.expanduser('~')
|
||||
source_csv = os.path.join(home_directory, "OneDrive", "Dokumente", "SUST", "UmsatzListe_AT493443700000113407_20241212_095142_mod.csv")
|
||||
target_csv = source_csv.replace(".csv", "_out.csv")
|
||||
data = Load_Data(source_csv)
|
||||
|
||||
tz = timezone('UTC') # Replace with your desired timezone
|
||||
outData = []
|
||||
for i in data:
|
||||
if ('Auftraggeber' in i[1]):
|
||||
# extract key-value pairs Auftraggeber, Verwendungszweck, Zahlungsreferenz, IBAN
|
||||
new_list = extract_values(i[1])
|
||||
|
||||
if len(new_list) > 0:
|
||||
outData.append([i[0], new_list[0].get('Auftraggeber'), new_list[0].get('Zahlungsreferenz'), new_list[0].get('Verwendungszweck'), new_list[0].get('IBAN'), i[2], i[3].replace(',','.'), i[4], i[5]])
|
||||
|
||||
savetxt(target_csv, outData, delimiter=";", encoding='utf-8', header='valuta_date;client;reference;intended;IBAN;entry_date;amount;currency;timestamp', fmt='%s')
|
||||
|
||||
except Exception as e:
|
||||
print (e)
|
||||
finally:
|
||||
print("Time elapsed: " + str(time() - t) + " s.") #0.091s
|
||||
@@ -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
|
||||
@@ -0,0 +1,103 @@
|
||||
import models
|
||||
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from segno import helpers
|
||||
|
||||
import datetime
|
||||
import locale
|
||||
|
||||
import os
|
||||
|
||||
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 ""
|
||||
fullfilepath = os.path.join(os.getenv('APPDATA'), "sust", "payment_slips",subfolder,f"{member.member_zip}_payment_slip_member{member.id}.pdf")
|
||||
c = canvas.Canvas(fullfilepath, pagesize=A4)
|
||||
|
||||
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()
|
||||
@@ -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()
|
||||
@@ -0,0 +1,85 @@
|
||||
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')
|
||||
|
||||
insert into deliveries (delivery_date, member_id, delivered_by_id, delivery_year)
|
||||
values (now(), 8, 186, '2024-02-21')
|
||||
|
||||
select count(*) from deliveries d
|
||||
|
||||
update deliveries set delivery_year = '2024-01-01'
|
||||
|
||||
create table sust.payments_csv
|
||||
(id serial,
|
||||
pc_valuta_date date,
|
||||
pc_client varchar(50),
|
||||
pc_reference varchar(50),
|
||||
pc_intended varchar(50),
|
||||
pc_iban varchar(50),
|
||||
pc_entry_date date,
|
||||
pc_amount money,
|
||||
pc_currency varchar(3),
|
||||
pc_timestamp timestamp)
|
||||
|
||||
COPY payments_csv(pc_valuta_date, pc_client, pc_reference, pc_intended, pc_iban, pc_entry_date, pc_amount, pc_currency, pc_timestamp)
|
||||
FROM 'C:\Users\zisco\OneDrive\Dokumente\SUST\UmsatzListe_AT493443700000113407_v1.csv'
|
||||
DELIMITER ';'
|
||||
CSV HEADER;
|
||||
|
||||
CREATE UNIQUE INDEX payments_ukey_member_year ON sust.payments USING btree (member_id, payment_year)
|
||||
|
||||
select count(*) from payments_csv where pc_processed = true
|
||||
|
||||
|
||||
select * from payments_csv pc where PC.pc_client like '%Anna%';
|
||||
select * from members mem where mem.member_firstname like '%Anna%';
|
||||
select * from payments p where p.member_id = 58;
|
||||
|
||||
ALTER TABLE payments_csv ADD PRIMARY KEY (id);
|
||||
|
||||
select * from payments p where p.member_id in (205,206);
|
||||
select * from payments p where date_part('year', p.payment_year) = 2025;
|
||||
|
||||
select pc.pc_reference from payments_csv pc where pc_processed =false order by pc_reference asc
|
||||
|
||||
select m.member_surname, m.member_firstname, m.member_firstname_spouse from members m
|
||||
except
|
||||
select mp.member_surname, mp.member_firstname, mp.member_firstname_spouse from members_paid mp
|
||||
|
||||
select * from members_paid mp where id = 8
|
||||
|
||||
-- already paid? TODO: Member "active since?" attribute missing in "member"
|
||||
select m.* from members_active m
|
||||
join deliveries d on d.member_id = m.id
|
||||
where m.id in (select id from members_delivered md
|
||||
except
|
||||
select id from members_paid mp where mp.payment_year = '2024-01-01')
|
||||
order by delivery_date asc
|
||||
|
||||
insert into deliveries (delivery_date, member_id, delivery_year)
|
||||
select now(), ss.id, '2024-01-01' from
|
||||
(select id from members_active m
|
||||
except
|
||||
select member_id from deliveries d) ss
|
||||
|
||||
select count(*) from deliveries d
|
||||
@@ -0,0 +1,56 @@
|
||||
# Importing libraries
|
||||
from numpy import genfromtxt
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
import models
|
||||
import os
|
||||
from database import engine, SessionLocal
|
||||
from pytz import timezone
|
||||
|
||||
def Load_Data(file_name):
|
||||
#data = genfromtxt(file_name, delimiter=';', skip_header=1, converters={0: lambda s: str(s)})
|
||||
#str2date = lambda x: datetime.strptime(x, '%d-%m-%y')
|
||||
data = genfromtxt(file_name, delimiter=';', dtype=None, skip_header=1, encoding='utf-8') #
|
||||
return data.tolist()
|
||||
|
||||
if __name__ == "__main__":
|
||||
t = time()
|
||||
|
||||
db = SessionLocal()
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
try:
|
||||
home_directory = os.path.expanduser('~')
|
||||
source_csv = os.path.join(home_directory, "OneDrive", "Dokumente", "SUST", "UmsatzListe_AT493443700000113407_20241212_095142_mod_out.csv")
|
||||
data = Load_Data(source_csv)
|
||||
|
||||
tz = timezone('UTC') # Replace with your desired timezone
|
||||
for i in data:
|
||||
dt = datetime.strptime(i[8], '%d.%m.%Y %H:%M:%S:%f')
|
||||
ts_timestamp = tz.localize(dt)
|
||||
|
||||
record = models.Payments_CSV(**{
|
||||
'pc_valuta_date' : datetime.strptime(i[0], '%d.%m.%Y').date(),
|
||||
'pc_client' : i[1],
|
||||
'pc_reference' : i[2],
|
||||
'pc_intended' : i[3],
|
||||
'pc_iban' : i[4],
|
||||
'pc_entry_date' : datetime.strptime(i[5], '%d.%m.%Y').date(),
|
||||
'pc_amount' : float(i[6]),
|
||||
'pc_currency' : i[7],
|
||||
'pc_timestamp' : ts_timestamp
|
||||
})
|
||||
db.add(record) #Add all the records
|
||||
|
||||
try:
|
||||
db.commit() #Attempt to commit all the records
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
except Exception as e:
|
||||
print (e)
|
||||
db.rollback() #Rollback the changes on error
|
||||
finally:
|
||||
db.close() #Close the connection
|
||||
|
||||
print("Time elapsed: " + str(time() - t) + " s.") #0.091s
|
||||
@@ -0,0 +1 @@
|
||||
.\.venv\Scripts\Activate
|
||||
@@ -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)
|
||||
@@ -0,0 +1,80 @@
|
||||
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
|
||||
import os
|
||||
|
||||
|
||||
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'
|
||||
#TODO Secure
|
||||
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))
|
||||
|
||||
path_to_file = os.path.join(os.getenv('APPDATA'), "sust", "payment_slips","mail",f"{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_([8])).all()
|
||||
|
||||
for member in members:
|
||||
if member.member_email:
|
||||
send_mail(member=member)
|
||||
else:
|
||||
logging.debug(f'Member {member.member_surname} has no email associated!')
|
||||
+130
@@ -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()
|
||||
+7959
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
# 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 payment_made(member: models.Members, year: int):
|
||||
return db.query(models.Payments.payment_year)\
|
||||
.filter(models.Payments.member_id == member.id, extract('year', models.Payments.payment_year) == 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__)
|
||||
|
||||
#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)
|
||||
paid = payment_made(member, 2024)
|
||||
|
||||
iconColor = "blue"
|
||||
hasMail = checkEmail(member.member_email)
|
||||
if not paid:
|
||||
iconColor = "red"
|
||||
elif delivered:
|
||||
iconColor = "green"
|
||||
elif hasMail:
|
||||
iconColor = "orange"
|
||||
|
||||
# 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 & bezahlt' if (delivered and paid) else 'Nicht bezahlt' if not paid 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"))\
|
||||
@@ -0,0 +1,97 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime, Float, Numeric
|
||||
from sqlalchemy.types import TypeDecorator, VARCHAR, Integer
|
||||
from sqlalchemy_utils import EmailType
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import datetime
|
||||
|
||||
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_membership_start = 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(DateTime, index=True)
|
||||
member_id = Column(Integer, ForeignKey("members.id"))
|
||||
payment_amount = Column(Numeric)
|
||||
payment_year = Column(DateTime, index=True)
|
||||
|
||||
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]) """
|
||||
|
||||
class Payments_CSV(Base):
|
||||
__tablename__ = 'payments_csv'
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
pc_valuta_date = Column(DateTime)
|
||||
pc_client = Column(String)
|
||||
pc_reference = Column(String)
|
||||
pc_intended = Column(String)
|
||||
pc_iban = Column(String)
|
||||
pc_entry_date = Column(DateTime)
|
||||
pc_amount = Column(Numeric)
|
||||
pc_currency = Column(String)
|
||||
pc_timestamp = Column(DateTime)
|
||||
pc_processed = Column(Boolean, default=False)
|
||||
Generated
+102
@@ -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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2"
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
@@ -0,0 +1,273 @@
|
||||
table "deliveries" {
|
||||
schema = schema.sust
|
||||
column "id" {
|
||||
null = false
|
||||
type = serial
|
||||
}
|
||||
column "delivery_date" {
|
||||
null = true
|
||||
type = timestamp
|
||||
}
|
||||
column "member_id" {
|
||||
null = true
|
||||
type = integer
|
||||
}
|
||||
column "delivered_by_id" {
|
||||
null = true
|
||||
type = integer
|
||||
}
|
||||
column "delivery_year" {
|
||||
null = true
|
||||
type = date
|
||||
}
|
||||
primary_key {
|
||||
columns = [column.id]
|
||||
}
|
||||
foreign_key "deliveries_member_id_delivered_by_fkey" {
|
||||
columns = [column.delivered_by_id]
|
||||
ref_columns = [table.members.column.id]
|
||||
on_update = NO_ACTION
|
||||
on_delete = NO_ACTION
|
||||
}
|
||||
foreign_key "deliveries_member_id_fkey" {
|
||||
columns = [column.member_id]
|
||||
ref_columns = [table.members.column.id]
|
||||
on_update = NO_ACTION
|
||||
on_delete = NO_ACTION
|
||||
}
|
||||
index "ix_deliveries_delivery_date" {
|
||||
columns = [column.delivery_date]
|
||||
}
|
||||
index "ix_deliveries_id" {
|
||||
columns = [column.id]
|
||||
}
|
||||
unique "deliveries_ukey_member_year" {
|
||||
columns = [column.member_id, column.delivery_year]
|
||||
}
|
||||
}
|
||||
table "members" {
|
||||
schema = schema.sust
|
||||
column "id" {
|
||||
null = false
|
||||
type = serial
|
||||
}
|
||||
column "member_count" {
|
||||
null = true
|
||||
type = integer
|
||||
default = 1
|
||||
}
|
||||
column "member_firstname" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_firstname_spouse" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_surname" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_suffix" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_street" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_streetnumber" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_email" {
|
||||
null = true
|
||||
type = character_varying(255)
|
||||
}
|
||||
column "member_zip" {
|
||||
null = true
|
||||
type = integer
|
||||
}
|
||||
column "member_city" {
|
||||
null = true
|
||||
type = character_varying
|
||||
}
|
||||
column "member_active" {
|
||||
null = true
|
||||
type = boolean
|
||||
default = true
|
||||
}
|
||||
column "member_connected" {
|
||||
null = true
|
||||
type = integer
|
||||
}
|
||||
column "created_date" {
|
||||
null = true
|
||||
type = timestamp
|
||||
default = sql("CURRENT_TIMESTAMP")
|
||||
}
|
||||
column "updated_date" {
|
||||
null = true
|
||||
type = timestamp
|
||||
}
|
||||
column "member_gender" {
|
||||
null = true
|
||||
type = enum.gender
|
||||
}
|
||||
column "member_died" {
|
||||
null = true
|
||||
type = timestamp
|
||||
}
|
||||
column "member_membership_start" {
|
||||
null = true
|
||||
type = timestamp
|
||||
}
|
||||
column "member_printed" {
|
||||
null = true
|
||||
type = boolean
|
||||
}
|
||||
column "member_coordinates" {
|
||||
null = true
|
||||
type = point
|
||||
comment = "UNUSED"
|
||||
}
|
||||
column "member_geo_latitude" {
|
||||
null = true
|
||||
type = numeric
|
||||
}
|
||||
column "member_geo_longitude" {
|
||||
null = true
|
||||
type = numeric
|
||||
}
|
||||
primary_key {
|
||||
columns = [column.id]
|
||||
}
|
||||
foreign_key "members_member_connected_fkey" {
|
||||
columns = [column.member_connected]
|
||||
ref_columns = [table.members.column.id]
|
||||
on_update = NO_ACTION
|
||||
on_delete = NO_ACTION
|
||||
}
|
||||
index "ix_members_id" {
|
||||
columns = [column.id]
|
||||
}
|
||||
index "ix_members_member_city" {
|
||||
columns = [column.member_city]
|
||||
}
|
||||
index "ix_members_member_email" {
|
||||
columns = [column.member_email]
|
||||
}
|
||||
index "ix_members_member_firstname" {
|
||||
columns = [column.member_firstname]
|
||||
}
|
||||
index "ix_members_member_street" {
|
||||
columns = [column.member_street]
|
||||
}
|
||||
index "ix_members_member_surname" {
|
||||
columns = [column.member_surname]
|
||||
}
|
||||
index "ix_members_member_zip" {
|
||||
columns = [column.member_zip]
|
||||
}
|
||||
}
|
||||
table "payments" {
|
||||
schema = schema.sust
|
||||
column "id" {
|
||||
null = false
|
||||
type = serial
|
||||
}
|
||||
column "payment_date" {
|
||||
null = true
|
||||
type = date
|
||||
}
|
||||
column "member_id" {
|
||||
null = true
|
||||
type = integer
|
||||
}
|
||||
column "payment_amount" {
|
||||
null = true
|
||||
type = numeric
|
||||
}
|
||||
column "payment_year" {
|
||||
null = true
|
||||
type = date
|
||||
comment = "Year, for which the payment is intended for."
|
||||
}
|
||||
primary_key {
|
||||
columns = [column.id]
|
||||
}
|
||||
foreign_key "payments_member_id_fkey" {
|
||||
columns = [column.member_id]
|
||||
ref_columns = [table.members.column.id]
|
||||
on_update = NO_ACTION
|
||||
on_delete = NO_ACTION
|
||||
}
|
||||
index "ix_payments_id" {
|
||||
columns = [column.id]
|
||||
}
|
||||
index "ix_payments_payment_date" {
|
||||
columns = [column.payment_date]
|
||||
}
|
||||
index "payments_ukey_member_year" {
|
||||
unique = true
|
||||
columns = [column.member_id, column.payment_year]
|
||||
}
|
||||
}
|
||||
table "payments_csv" {
|
||||
schema = schema.sust
|
||||
column "id" {
|
||||
null = false
|
||||
type = serial
|
||||
}
|
||||
column "pc_valuta_date" {
|
||||
null = true
|
||||
type = date
|
||||
}
|
||||
column "pc_client" {
|
||||
null = true
|
||||
type = character_varying(50)
|
||||
}
|
||||
column "pc_reference" {
|
||||
null = true
|
||||
type = character_varying(50)
|
||||
}
|
||||
column "pc_intended" {
|
||||
null = true
|
||||
type = character_varying(100)
|
||||
}
|
||||
column "pc_iban" {
|
||||
null = true
|
||||
type = character_varying(50)
|
||||
}
|
||||
column "pc_entry_date" {
|
||||
null = true
|
||||
type = date
|
||||
}
|
||||
column "pc_amount" {
|
||||
null = true
|
||||
type = numeric
|
||||
}
|
||||
column "pc_currency" {
|
||||
null = true
|
||||
type = character_varying(3)
|
||||
}
|
||||
column "pc_timestamp" {
|
||||
null = true
|
||||
type = timestamp
|
||||
}
|
||||
column "pc_processed" {
|
||||
null = true
|
||||
type = boolean
|
||||
default = false
|
||||
}
|
||||
primary_key {
|
||||
columns = [column.id]
|
||||
}
|
||||
}
|
||||
enum "gender" {
|
||||
schema = schema.sust
|
||||
values = ["male", "female", "divers"]
|
||||
}
|
||||
schema "sust" {
|
||||
comment = "standard public schema"
|
||||
}
|
||||
@@ -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}')
|
||||
Reference in New Issue
Block a user