brewman/brewman/brewman/routers/fingerprint.py

190 lines
5.8 KiB
Python
Raw Normal View History

import csv
import uuid
2020-10-07 15:18:43 +00:00
from datetime import date, datetime, time, timedelta
from io import StringIO
2020-10-07 15:18:43 +00:00
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from sqlalchemy import and_, bindparam, exists, select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import Employee
from ..models.voucher import Fingerprint
from ..routers import get_lock_info
2020-10-07 15:18:43 +00:00
from ..schemas.auth import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("")
def upload_prints(
2020-10-07 16:59:24 +00:00
db: Session = Depends(get_db),
fingerprints: UploadFile = File(None),
user: UserToken = Depends(get_user),
):
try:
start, finish = get_lock_info(db)
employees = {}
for id_, code in db.query(Employee.id, Employee.code).all():
employees[code] = id_
file_data = read_file(fingerprints)
prints = [d for d in fp(file_data, employees) if start <= d["date"] <= finish]
2020-10-07 15:18:43 +00:00
paged_data = [prints[i : i + 100] for i in range(0, len(prints), 100)]
for i, page in enumerate(paged_data):
print(f"Processing page {i} of {len(paged_data)}")
db.execute(get_query(9.4), page)
db.commit()
return {}
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
2020-10-07 15:18:43 +00:00
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
except Exception:
db.rollback()
raise
def get_query(version):
if version == 9.5:
return (
pg_insert(Fingerprint)
.values(
2020-10-07 15:18:43 +00:00
{
"FingerprintID": bindparam("id"),
"EmployeeID": bindparam("employee_id"),
"Date": bindparam("date"),
}
)
.on_conflict_do_nothing()
)
else:
2020-10-07 16:59:24 +00:00
sel = select(
[bindparam("id"), bindparam("employee_id"), bindparam("date")]
).where(
~exists([Fingerprint.id]).where(
2020-10-07 15:18:43 +00:00
and_(
Fingerprint.employee_id == bindparam("employee_id"),
Fingerprint.date == bindparam("date"),
)
)
)
return Fingerprint.__table__.insert().from_select(
[Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel
)
def read_file(input_file: UploadFile):
input_file.seek(0)
output = bytearray()
while 1:
data = input_file.read(2 << 16)
if not data:
break
output.extend(data)
if output[0:3] == b"\xef\xbb\xbf":
encoding = "utf-8"
elif output[0:2] == b"\xfe\xff" or output[0:2] == b"\xff\xfe":
encoding = "utf-16"
else:
encoding = "ascii"
# raise ValidationError("The encoding of the attendance file is not correct")
return StringIO(output.decode(encoding))
def fp(file_data, employees):
fingerprints = []
reader = csv.reader(file_data, delimiter="\t")
header = next(reader, None)
employee_column = 2
date_column = len(header) - 1
date_format = "%Y/%m/%d %H:%M:%S" if date_column == 6 else "%Y-%m-%d %H:%M:%S"
for row in reader:
try:
employee_code = int(row[employee_column]) # EnNo
date = datetime.datetime.strptime(row[date_column], date_format)
if employee_code in employees.keys():
fingerprints.append(
2020-10-07 15:18:43 +00:00
{
"id": uuid.uuid4(),
"employee_id": employees[employee_code],
"date": date,
}
)
except ValueError:
continue
return fingerprints
# # Upsert using subquery
# sel = select([literal(uuid.uuid4()), literal(employee_id), literal(date)]).where(
# ~exists([Fingerprint.id]).where(
# and_(
# Fingerprint.employee_id == employee_id, Fingerprint.date == date
# )
# )
# )
# return Fingerprint.__table__.insert().from_select([Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel)
def get_prints(employee_id: uuid.UUID, date_: date, db: Session):
prints = (
db.query(Fingerprint)
.filter(Fingerprint.employee_id == employee_id)
.filter(Fingerprint.date >= datetime.combine(date_, time(hour=7)))
2020-10-07 16:59:24 +00:00
.filter(
Fingerprint.date < datetime.combine(date_ + timedelta(days=1), time(hour=7))
)
.order_by(Fingerprint.date)
.all()
)
last = None
for i in range(len(prints), 0, -1):
item = prints[i - 1].date
if last is not None and last - item < timedelta(minutes=10):
prints.remove(prints[i - 1])
else:
last = item
if len(prints) == 0:
hours_worked, full_day = "", None
elif len(prints) == 2:
time_worked = prints[1].date - prints[0].date
hours_worked, full_day = working_hours(time_worked)
elif len(prints) == 4:
2020-10-07 16:59:24 +00:00
time_worked = (prints[1].date - prints[0].date) + (
prints[3].date - prints[2].date
)
hours_worked, full_day = working_hours(time_worked)
else:
hours_worked, full_day = "Error", False
return (
", ".join([x.date.strftime("%H:%M") for x in prints]) + " ",
hours_worked,
full_day,
)
def working_hours(delta: timedelta):
minutes = (delta.seconds // 60) % 60
minutes = int(5 * round(float(minutes) / 5))
hours = delta.seconds // 3600
hours_worked = str(hours).zfill(2) + ":" + str(minutes).zfill(2)
return hours_worked, delta.seconds >= 60 * 60 * 9 # 9hrs